@ 261102.1

150823.1 · 0c93eb3379241dc4775718a9d39f54a6c4de20d6

Move source files to src/

reading declarations

.gitignore

@@ -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:

Makefile.am

@@ -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

deleted file mode 100644@@ -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), "&&amp;\0<&lt;\0>&gt;\0'&apos;\0\"&quot;\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(&reg, (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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

configure.ac

@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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

deleted file mode 100644@@ -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;-}

src/builtin.c

new file mode 100644@@ -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), "&&amp;\0<&lt;\0>&gt;\0'&apos;\0\"&quot;\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(&reg, (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;+}

src/builtin.h

new file mode 100644@@ -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

src/bytecode.c

new file mode 100644@@ -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);+}

src/bytecode.h

new file mode 100644@@ -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

src/compile.c

new file mode 100644@@ -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);+  }+}

src/compile.h

new file mode 100644@@ -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

src/exec_stack.h

new file mode 100644@@ -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

src/execute.c

new file mode 100644@@ -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;+}

src/inject_errors.c

new file mode 100644@@ -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;+  }+}

src/jq.h

new file mode 100644@@ -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 */

src/jq_parser.h

new file mode 100644@@ -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

src/jq_test.c

new file mode 100644@@ -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);+  }+}

src/jv.c

new file mode 100644@@ -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;+}

src/jv.h

new file mode 100644@@ -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?+++ */

src/jv_alloc.c

new file mode 100644@@ -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

src/jv_alloc.h

new file mode 100644@@ -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

src/jv_aux.c

new file mode 100644@@ -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;+}

src/jv_dtoa.c

new file mode 100644@@ -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;+	}

src/jv_dtoa.h

new file mode 100644@@ -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

src/jv_file.c

new file mode 100644@@ -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;+}

src/jv_parse.c

new file mode 100644@@ -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));+}

src/jv_print.c

new file mode 100644@@ -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;+}

src/jv_unicode.c

new file mode 100644@@ -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;+}

src/jv_unicode.h

new file mode 100644@@ -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

src/jv_utf8_tables.h

new file mode 100644@@ -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};

src/lexer.c

new file mode 100644@@ -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.