jq-jq-1.6/0000700000175000017500000000000013366726451011713 5ustar czchenczchenjq-jq-1.6/.github/0000700000175000017500000000000013366726451013253 5ustar czchenczchenjq-jq-1.6/.github/ISSUE_TEMPLATE/0000700000175000017500000000000013366726451015436 5ustar czchenczchenjq-jq-1.6/.github/ISSUE_TEMPLATE/bug_report.md0000600000175000017500000000157413366726451020141 0ustar czchenczchen--- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Provide a minimal test case to reproduce the behavior. If the input is large, either attach it as a file, or [create a gist](https://gist.github.com) and link to it here. **Expected behavior** A clear and concise description of what you expected to happen. **Environment (please complete the following information):** - OS and Version: [e.g. macOS, Windows, Linux (please specify distro)] - jq version [e.g. 1.5] **Additional context** Add any other context about the problem here. jq-jq-1.6/KEYS0000600000175000017500000000064513366726451012420 0ustar czchenczchenpub 4096R/71523402 2015-10-11 Key fingerprint = 4FD7 01D6 FA9B 3D2D F5AC 935D AF19 040C 7152 3402 uid jq Release Signing Key pub 2048R/D15684DB 2015-10-12 [expires: 2017-10-11] Key fingerprint = 7F6C 7BD3 0412 AFD5 8C1A 5007 EB26 A4F8 D156 84DB uid Nicolas Williams sub 2048R/9C9CCD6A 2015-10-12 [expires: 2017-10-11] jq-jq-1.6/scripts/0000700000175000017500000000000013366726451013402 5ustar czchenczchenjq-jq-1.6/scripts/update-website0000700000175000017500000000076613366726451016263 0ustar czchenczchen#!/bin/sh # This script builds the website from the docs directory of # the current branch and copies it over to the gh-pages # branch. set -eu set -o xtrace # build website scriptdir=`dirname "$0"` cd "$scriptdir"/../docs rm -rf output rake build cd .. # copy to /tmp tmpdir=$(mktemp -d -t jq.website.XXXXXXXXXX) cp -r docs/output/* "$tmpdir" cp .gitignore "$tmpdir" # copy to gh-pages git checkout gh-pages cp -r "$tmpdir"/* . cp "$tmpdir"/.gitignore . # clean up rm -rf "$tmpdir" echo SUCCESS jq-jq-1.6/scripts/crosscompile0000700000175000017500000000152613366726451016036 0ustar czchenczchen#!/bin/sh # This script is used to cross-compile binaries for # platforms other than the current one # Usage: $0 # is arbitrary, it is the name # of the directory which will be created to contain # the output binaries. # e.g. $0 win32 --host=i686-w64-mingw32 set -e cd `dirname "$0"`/../build jobs=-j4 case "X$1" in X-j*) jobs=$1; shift;; esac plat="$1" [ -z "$plat" ] && exit 1 shift case "$plat" in */*) echo "platform name must not be a path"; exit 1;; *..*) echo "platform name must not be a path"; exit 1;; *) plat=$PWD/$plat;; esac [ -d "$plat" ] || mkdir "$plat" rm -rf "$plat/tmp" mkdir "$plat/tmp" cd "$plat/tmp" ../../../configure "$@" make "$jobs" DESTDIR=$plat install set -x for jq in `find . -type f \( -name jq -o -name jq.exe \) -print`; do cp "$jq" .. done cd .. rm -rf tmp jq-jq-1.6/scripts/version0000700000175000017500000000041113366726451015011 0ustar czchenczchen#!/bin/sh set -e cd `dirname "$0"` if git rev-parse --verify -q jq-1.0 > /dev/null 2>&1; then git describe --tags --match 'jq-*' --dirty | sed 's/^jq-//' else b=`git rev-parse --abbrev-ref HEAD` c=`git describe --always --dirty` echo "${b}-${c}" fi jq-jq-1.6/scripts/gen_utf8_tables.py0000600000175000017500000000175013366726451017032 0ustar czchenczchen#!/usr/bin/python # This program was used to generate jv_utf8_tables.gen.h mask = lambda n: (1 << n) - 1 def print_table(type, name, t): print("static const %s %s[] =" % (type, name)) for i in range(0,len(t),16): print ((" {" if i == 0 else " ") + ", ".join("0x%02x"%n for n in t[i:i+16]) + ("," if i + 16 < len(t) else "};")) def utf8info(c): if c < 0x80: return 1, mask(7) if 0x80 <= c <= 0xBF: return 255, mask(6) if 0xC0 <= c <= 0xC1: return 0, 0 if 0xC2 <= c <= 0xDF: return 2, mask(5) if 0xE0 <= c <= 0xEF: return 3, mask(4) if 0xF0 <= c <= 0xF4: return 4, mask(3) if 0xF4 <= c <= 0xFF: return 0, 0 table = lambda i: [utf8info(c)[i] for c in range(256)] print("#define UTF8_CONTINUATION_BYTE ((unsigned char)255)") print_table("unsigned char", "utf8_coding_length", table(0)) print_table("unsigned char", "utf8_coding_bits", table(1)) print_table("int", "utf8_first_codepoint", [0, 0x0, 0x80, 0x800, 0x10000]) jq-jq-1.6/ChangeLog0000600000175000017500000010100313366726451013462 0ustar czchenczchen2015-07-10 Nicolas Williams nico@cryptonector.com Use `include` for import into namespace Simplify import docs Fix typo in docs 2015-07-06 James Andariese james.andariese@locationlabs.com Dockerfile reorganized 2015-07-04 David Tolnay dtolnay@gmail.com Make jq.h usable from C++ 2015-07-03 Nicolas Williams nico@cryptonector.com Document math support 2015-06-30 David Tolnay dtolnay@gmail.com strftime wrong day-of-week (fix #838) 2015-06-28 Nicolas Williams nico@cryptonector.com Document --run-tests Make --run-tests' jv_test() quiet 2015-06-27 Nicolas Williams nico@cryptonector.com Make --run-tests less verbose by default Add more basic number tests Add `pow`, better libm detection (fix #443) 2015-06-27 David Tolnay dtolnay@gmail.com gcov exclusions flag to enable gcov and coveralls add configure option to run tests without valgrind 2015-06-20 David Tolnay dtolnay@gmail.com get Travis CI working 2015-06-26 Nicolas Williams nico@cryptonector.com Add `{$var}` `. as {$var}` syntax (fix #831) Add streaming utilities (fix #827) 2015-06-04 Santiago Lapresta santiago@typeform.com Add combinations/0 and combinations/1 2015-06-22 Nicolas Williams nico@cryptonector.com WriteFile() on WIN32 when stdout isatty (fix #824) 2015-06-19 David Tolnay dtolnay@gmail.com fix errors flagged by clang static analyzer 2015-06-19 Nicolas Williams nico@cryptonector.com Fix #811: use CommandLineToArgvW() and _wfopen() 2015-06-18 David Tolnay dtolnay@gmail.com fix use after free in f_strptime separate jq, oniguruma, sh, and man tests 2015-06-18 Nicolas Williams nico@cryptonector.com argv[] may not be UTF-8 (fix #811) 2015-06-18 Doug Luce doug@github.con.com Add alloca() discovery to configure.ac 2015-06-18 Nicolas Williams nico@cryptonector.com Fix `finites` 2015-06-17 David Tolnay dtolnay@gmail.com fix broken tests in manual.yml 2015-06-17 Nicolas Williams nico@cryptonector.com Add isnormal and related, rename *inf 2015-06-17 Nicolas Williams nico@cryptonector.com Fix #814: raise on div-0, add inf isinf nan isnan 2015-06-17 Nicolas Williams nico@cryptonector.com Sequence parser: wait for RS on startup (fix #687) 2015-06-07 David Tolnay dtolnay@gmail.com array and object destructuring (fix #533) 2015-06-03 Nicolas Williams nico@cryptonector.com Add --tab and -indent n options 2015-05-29 Nicolas Williams nico@cryptonector.com Fixup --slurpfile/argile docs Add --slurpfile Better handling of stdout errors 2015-05-25 Nicolas Williams nico@cryptonector.com Add ./configure --enable-all-static 2015-05-25 Nicolas Williams nico@cryptonector.com Keywords should be OK as object keys (fix #794) 2015-03-04 Travis Gockel travis@gockelhut.com Add wrapping and clamping to jv_array_slice 2015-04-17 Assaf Gordon assafgordon@gmail.com Print offending object in runtime error messages Add filename/line functions to jq (fix #753) 2015-04-17 Assaf Gordon assafgordon@gmail.com Report filename:line on runtime errors (fix #752) 2015-05-19 Nicolas Williams nico@cryptonector.com Document gsub/3 2015-05-03 Nicolas Williams nico@cryptonector.com Add error injection library 2015-04-28 Nicolas Williams nico@cryptonector.com Report read errors too (and fix #772) 2015-05-02 Nicolas Williams nico@cryptonector.com README: send questions to SO and Freenode 2015-04-28 Nicolas Williams nico@cryptonector.com usage() should check fprintf() result (fix #771) 2015-04-28 Nicolas Williams nico@cryptonector.com Fix header guards (fix #770) 2015-04-24 Nicolas Williams nico@cryptonector.com --raw-input wrongly adds NULs (fix #761) 2015-04-23 Nicolas Williams nico@cryptonector.com With `inputs` builtin, -n and -R can now coexist --raw-input ought to read NULs (partial fix #760) --slurp --raw-input is broken (fix #761) from_entries is broken (fix #767) 2015-04-22 Assaf Gordon assafgordon@gmail.com regex functions: report informative error if not available. 2015-04-21 Andrew O'Brien obrien.andrew@gmail.com Fixes manual generation with psych 2015-04-20 Assaf Gordon assafgordon@gmail.com Handle NUL in escaped-string output 2015-04-03 tal@whatexit.org tal@whatexit.org manual.yml: Clarify how to specify keys with ":" and special chars. 2015-04-15 Assaf Gordon assafgordon@gmail.com docs: expand @tsv section - add escape sequences. @tsv: escape \r, \n, \\ 2015-03-30 Nicolas Williams nico@cryptonector.com Add `$__loc__` (fix #740) 2015-03-29 Nicolas Williams nico@cryptonector.com Include filename and lineno in error messages 2015-03-06 Assaf Gordon assafgordon@gmail.com detect and report output writing errors 2015-03-18 Santiago Lapresta santiago.lapresta@gmail.com Adds Dockerfile 2015-03-10 Assaf Gordon assafgordon@gmail.com partial handling of input errors 2015-03-09 Assaf Gordon assafgordon@gmail.com always propagate input errors to exit code 2015-03-23 William Langford wlangfor@gmail.com Fix #735 (SIGFPE on modulo by 0) 2015-03-08 Nicolas Williams nico@cryptonector.com Add more date builtins Automake: jq depends on version.h (fix #721) 2015-03-06 Assaf Gordon assafgordon@gmail.com exit with non-zero code on runtime exceptions 2015-03-06 Nicolas Williams nico@cryptonector.com Add date builtins (fix #364) 2015-02-18 Stefan Seemayer stefan@seemayer.de Correct automake and autoconf version requirements 2015-02-17 Nicolas Williams nico@cryptonector.com Mention --disable-maintainer-mode in bison error 2015-02-16 Sebastian Freundt freundt@ga-group.nl Fix oniguruma detection logic 2015-02-15 Nicolas Williams nico@cryptonector.com Add --disable-maintainer-mode; make bison optional 2015-02-14 Nicolas Williams nico@cryptonector.com Make Oniguruma/regexp optional 2015-02-01 Nicolas Williams nico@cryptonector.com Refactor moar: move parts of main.c into libjq 2014-12-27 Nicolas Williams nico@cryptonector.com Refactor handling of inputs in main() (fix #667) 2015-02-10 Kim Toms kim.toms@bplglobal.net Enhance from_entries to better deal with Amazon AWS Tags 2015-01-26 Nicolas Williams nico@cryptonector.com Usage message for -h should go to stdout 2015-01-27 i isomorphisms@sdf.org readability 2015-01-14 Joel Purra code+github@joelpurra.com Empty arrays join/1 to an empty string, fixes #668 bug introduced by 9760245 2014-12-27 Nicolas Williams nico@cryptonector.com Add `debug` and `stderr` builtins 2015-01-13 Nicolas Williams nico@cryptonector.com join/1: respect empty strings (fix #668) 2015-01-13 Nicolas Williams nico@cryptonector.com Split on empty sep: fix #552 moar 2015-01-12 Nicolas Williams nico@cryptonector.com Fix docs for `split/0` 2015-01-12 Nicolas Williams nico@cryptonector.com Fix #552 2015-01-02 Nicolas Williams nico@cryptonector.com Look for jq/main.jq for imports 2015-01-01 Nicolas Williams nico@cryptonector.com Add static build instructions (fix #294) 2014-12-30 Nicolas Williams nico@cryptonector.com Further module system revamp (fix #659) 2014-12-28 Nicolas Williams nico@cryptonector.com Add `label $name | EXP`; fix `break` 2014-12-30 Nicolas Williams nico@cryptonector.com Remove string indexing by string (fix #454) 2014-12-30 Nicolas Williams nico@cryptonector.com Add support for testing erroneous programs 2014-12-30 Nicolas Williams nico@cryptonector.com Make --run-tests more informative 2014-10-06 pkoppstein pkoppstein@gmail.com transpose/0 for possibly jagged matrices 2014-10-07 pkoppstein pkoppstein@gmail.com bsearch(x) (binary search): builtin.c (tested), with documentation and test case. Always yields an integer (even if input is unsorted); returns (-1 - ix) if x is not in input array. 2014-10-06 pkoppstein pkoppstein@gmail.com ascii_upcase/0 and ascii_downcase/0 2014-12-27 Nicolas Williams nico@cryptonector.com Add `debug` builtin Don't force C API users to set input cb 2014-12-26 Nicolas Williams nico@cryptonector.com Make jq --run-tests show test line numbers Streaming parser torture tests Fuzz JSON parser 2014-12-22 Nicolas Williams nico@cryptonector.com Add Streaming parser (--stream) 2014-12-26 Nicolas Williams nico@cryptonector.com Allow C-coded functions to `empty` Add BLOCK_8() macro Fix `foreach` non-progation of errors Allow zero-length buffers in jv_parser_set_buf() 2014-12-24 Nicolas Williams nico@cryptonector.com Add @tsv; fix #645 Module search revamp for pkg managers Fix #348: reject unescaped control chars 2014-12-23 Nicolas Williams nico@cryptonector.com Use __attribute__ __printf__ with GCC Make `values` faster (fix #652) 2014-12-22 Marc Abramowitz marc@marc-abramowitz.com .travis.yml: Set sudo false; use containers 2014-12-22 Santiago Lapresta santiago.lapresta@gmail.com Define `map_values` 2014-05-21 Santiago Lapresta santiago.lapresta@gmail.com `in` is now `inside`, added `in` as inverse of `has` 2014-05-20 Santiago Lapresta santiago.lapresta@gmail.com Added `in` command 2014-12-21 Eiichi Sato sato.eiichi@gmail.com Fix examples in manual Fix indents in manual.yml HTML-escape jq programs in manual Fix examples in manual 2014-12-12 Nicolas Williams nico@cryptonector.com Add until(cond; next); fix #639 Add --argjson, fix #648 2014-11-29 Nicolas Williams nico@cryptonector.com Fix refcount leak, fix #618 2014-11-28 Nicolas Williams nico@cryptonector.com STOREV/LOADV* should also print refcnts Enable printing of stack val refcnts Print stack value refcounts when tracing (#636) 2014-11-23 Colin von Heuring colin@janrain.com Doc correction 2014-11-11 Ian Miell ian.miell@gmail.com Requirements made slightly more complete: cf https://github.com/ianmiell/shutit/blob/master/library/jq/jq.py 2014-11-05 Steven Maude StevenMaude@users.noreply.github.com Fix typos in tutorial 2014-10-21 Santiago Lapresta santiago.lapresta@gmail.com Define {any,all}/2 independently from {any,all}/0 2014-10-20 Santiago Lapresta santiago.lapresta@gmail.com Define {any,all}/{0,1} in terms of {any,all}/2 2014-10-10 Nicolas Williams nico@cryptonector.com Add support for JSON sequence MIME type 2014-10-06 William Langford wlangfor@gmail.com Properly call onig_error_code_to_str 2014-10-06 pkoppstein pkoppstein@gmail.com fix sub (#586); add gsub/3; add transpose/0. 2014-10-03 Nicolas Williams nico@cryptonector.com Update docs about sort/group/min/max/unique from-entries should work with EC2 (fix #592) Remove sort/1 and group/1 2014-09-30 Nicolas Williams nico@cryptonector.com to_entries should not sort keys (fix #561) 2014-09-22 William Langford wlangfor@gmail.com Properly handle when objects cannot be folded 2014-08-30 Nicolas Williams nico@cryptonector.com Drop the jq version directory from search path Never close stdin; allow multiple `-` arguments Handle invalid inputs in argument files (fix #562) 2014-08-28 William Langford wlangfor@gmail.com Properly handle incomplete json when input is file 2014-08-10 Nicolas Williams nico@cryptonector.com Add `module` directive, `modulemeta` builtin 2014-08-09 Nicolas Williams nico@cryptonector.com Constant fold objects Fold constant arrays More constant folding: null, true, and false `.foo[-1] = ...` trips assertion (fix #490) Allow any number of jq-coded function arguments 2014-08-08 Nicolas Williams nico@cryptonector.com Make regexp builtins and range/3 use #524 too Use `def f($a): ...;` syntax for builtins Add `def f($arg):` syntax (fix #524) 2014-07-31 pkoppstein pkoppstein@gmail.com regex filters (#432): scan, splits, split, sub, gsub 2014-08-06 Nicolas Williams nico@cryptonector.com Better error msg for bad shell quoting (fix #538) 2014-08-04 William Langford wlangfor@gmail.com Actually check version for bison. 2014-08-03 pkoppstein pkoppstein@gmail.com Apply TCO to recurse/1, add recurse/2; tweak docs 2014-08-01 Adam Lindberg hello@alind.io Add example of selecting object with keys 2014-07-19 pkoppstein pkoppstein@gmail.com Add capture; document regular expression filters 2014-07-28 Nicolas Williams nico@cryptonector.com Add `first`, `nth`, `last` (fix #510) 2014-07-27 Nicolas Williams nico@cryptonector.com Fold constants (fix #504) 2014-07-21 William Langford wlangfor@gmail.com Changing color codes to fix #495 2014-07-09 William Langford wlangfor@gmail.com Added library system with -l, -L, and JQ_LIBRARY_PATH 2014-07-14 Simon Elsbrock simon@iodev.org jq 1.4 is in Debian 2014-07-13 Marc Bruggmann marcbr@spotify.com Fix manual example for `endswith`. 2014-07-09 Hanfei Shen qqshfox@gmail.com Fix examples for `del` in manual 2014-07-08 Zhiming Wang zmwangx@gmail.com Fix invalid YAML in manual.yml Add tests/all.trs to .gitignore 2014-07-09 Nicolas Williams nico@cryptonector.com Better document `path()`'s power; also `|=` Add `foreach EXP as $var (INIT; UPDATE)` form Make `while()` handle `break` 2014-07-07 Nicolas Williams nico@cryptonector.com Make C-coded built-ins take `jq_state *` argument `error(x)` should not `tostring` its arg; fix #466 `limit` should use `break` Make `any/2` and `all/2` efficient using `foreach` 2013-12-24 Nicolas Williams nico@cryptonector.com jv_invalid() shouldn't allocate 2013-12-31 Nicolas Williams nico@cryptonector.com jv_show() should be able to display invalid values 2014-07-07 Nicolas Williams nico@cryptonector.com Add `break` builtin for `foreach` Explain `foreach`'s powers a bit more Document `path(path_expression)` builtin $var["foo"]=1 can't work as expected; doc fix #236 Better check for lib has only functions (fix #138) 2014-07-06 Nicolas Williams nico@cryptonector.com Add `any/N` and `all/N` x N in (1, 2) (fix #455) Add `foreach` and `limit` 2014-07-04 William Langford wlangfor@gmail.com Add support for negative indices for .[]; fix #462 2014-07-06 Nicolas Williams nico@cryptonector.com Add general `?` operator 2014-07-05 Nicolas Williams nico@cryptonector.com Add `try EXP catch EXP` 2014-07-06 Nicolas Williams nico@cryptonector.com Document `error/1` 2014-07-02 Nicolas Williams nico@cryptonector.com Add `while(cond; update)` (fix #314) Add `range(init;upto;by)` (fix #317) 2014-07-01 Nicolas Williams nico@cryptonector.com Describe generators, range() with by to manual 2014-07-01 William Langford wlangfor@gmail.com Fixed base64 issue with UTF-8 strings 2014-06-30 Nicolas Williams nico@cryptonector.com TCO to the max! 2014-06-25 William Langford wlangfor@gmail.com Added cross-compilation script to build libjq for iOS. 2014-06-29 Zhiming Wang zmwangx@gmail.com Let @uri produce uppercase hexadecimal digits... 2014-06-24 Nicolas Williams nico@cryptonector.com Get "Try Online" button working (fix #440) 2014-06-22 Nicolas Williams nico@cryptonector.com Tail call optimization (close #437) 2014-06-20 Nicolas Williams nico@cryptonector.com Allow stacking of short options (fix #346) 2014-06-18 William Langford wlangfor@gmail.com Added regex support as per issue #164. 2014-06-17 Nicolas Williams nico@cryptonector.com Add `-j` / `--join-output` option, similar to `-r` 2014-06-18 Santiago Lapresta santiago.lapresta@gmail.com Simplified standard library 2014-06-16 Nicolas Williams nico@cryptonector.com Fix #280: from_entries of [] is null, should be {} 2014-06-16 Nicolas Williams nico@cryptonector.com No args default w/ tty stdout, not tty stdin #220 2014-06-16 Santiago Lapresta santiago.lapresta@gmail.com Added `flatten` and `flatten(x)` functions 2014-06-16 Nicolas Williams nico@cryptonector.com Add ChangeLog and NEWS files 2014-06-14 Nicolas Williams nico@cryptonector.com Allow multiple functions with different arities 2014-06-13 Nicolas Williams nico@cryptonector.com Add `env` builtin 2014-06-13 Nicolas Williams nico@cryptonector.com Document the lambda nature of function args #391 2014-06-13 Nicolas Williams nico@cryptonector.com Add jqplay link to the site 2014-06-12 Jingwen Owen Ou jingweno@gmail.com jqplay has a domain now 2014-06-12 Nicolas Williams nico@cryptonector.com Make a better jq.1 when Ruby deps missing 2014-06-11 Kim De Mey kim.demey@gmail.com Detect endianness at configuration with Autoconf AC_C_BIGENDIAN feature 2014-06-09 Nicolas Williams Add libm.h to dist file list Add note about cmd.exe quoting Building docs fails on powerpc (#349) 2014-06-08 Nicolas Williams Update site news Also fix configure.ac to use git describe --tags Fix scripts/version: use git describe --tags ... After tagging as 1.4 scripts/version was still producing jq-1.3-.... Add `indices(s)`, improve `index(s)`, `rindex(s)` Now these deal with arrays as input and `s` being an array or a scalar. Improve `index` and `rindex` examples Remove reference to `getpath` from docs Document `index` and `rindex` (#389) 2014-06-07 Santiago Lapresta Added `join` function 2014-06-07 Nicolas Williams String * number should be commutative 2014-06-04 Nicolas Williams Add cross-compilation notes to README A detailed set of instruction as to how to setup a cross-compilation environment for OS X and Win32/64 would be nice. Add -j option to scripts/crosscompile Add flags argument to jv_parser_new() For extensibility. We might add streaming parser options, even binary JSON encoding options. 2014-06-02 Nicolas Williams Fix tests failures on Windows And Solaris 8 and 9 too, no doubt. The problem was that non-standard vsnprintf()s that return -1 when the buffer is too small were not properly supported. 2014-05-20 Santiago Lapresta Documented `del` command 2014-05-11 Santiago Lapresta Added texts/examples to unique_by function Added unique_by function 2014-04-17 Nicolas Williams Make pthread tls configurable for Mingw build For the Mingw build we don't want to pull in the pthread DLL just because we can autodetect pthread support. That would make the jq.exe binary not self-contained. 2014-04-16 Nicolas Williams Add autoconf checks for pthreads; fix #340 2014-03-20 Jingwen Owen Ou Add link to jqplay 2014-03-13 Nicolas Williams Fix for #303 in the sources 2014-03-13 Santiago Lapresta Added `arrays` and other filters Arrays, objects, numbers, strings, booleans, nulls, values (non-nulls) -- these builtins filter out those inputs that don't match the name of the builtin. This fixes #322 and #324. 2014-03-07 Filippo Valsorda Add a recursive object merge strategy and bind it to * This commit adds a jv_object_merge_recursive function, that performs recursive object merging, and binds it to multiply when applied to two objects. Closes #320 2014-03-06 Nicolas Williams Make libm tests more portable 2014-02-26 Andrew Rodland Repair jv_show 2014-02-26 Andrew Rodland Make jq --raw-output --unbuffered work --unbuffered was only affecting the normal output case, not the --raw-output case. Make the two of them play together. This also makes sure that the output is flushed *after* printing the newline, so a consumer doesn't lag a line behind. 2014-02-21 Nicolas Williams Add cbrt (cube root) Add missing trig functions and barebones test Remove non-standard exp10() 2014-02-21 Mike McCabe Initial add of math functions. 2014-02-20 Nicolas Williams Add `?`, `.[]?`, and `..` operators Make XPath-like `//a/b` recursive structure traversal easier in jq, which then becomes: ..|.a?.b? The `?` operator suppresses errors about . not being an array or object. The `..` operator is equivalent to calling the new `recurse_down` built-in, which in turn is equivalent to recurse(.[]?) Note that `..a` is not supported; neither is `...a`. That could be add added, but it doesn't seem worth the trouble of saving the need to type a '|'. 2014-02-16 Santiago Lapresta Added `all` and `any` builtins 2014-01-25 polyester work with newer versions of automake when using a newer automake, the autoreconf step fails with warnings: "linking libtool libraries using a non-POSIX archiver requires 'AM_PROG_AR' in 'configure.ac' " This happens for instance on ubuntu 13.10. Doing just that, adding 'AM_PROG_AR' to configure.ac fixes the problem. 2014-01-01 Nicolas Williams Fix #201; check that bison accepts --warnings 2013-12-27 Joe Littlejohn Fix rpm build (`make rpm`) * Re-add VERSION as it's required for `./setup superclean` and `make rpm`. * Add *.rpm to git ignore, we never want them under version control. 2013-12-27 Filippo Giunchedi include additional files in jq.spec this will probably need changing upon SONAME bump fix rpm Makefile target and prerequisites depend on dist and the specfile, plus use automake's variables 2013-12-26 Nicolas Williams Document --version 2013-12-26 Nicolas Williams Add jv_dumpf() and jv_show() jv_dumpf() takes a FILE *. jv_show() is intended for use in debuggers, so it dumps the jv to stderr and it does not jv_free() the jv, so it's safe to "call jv_show(some_jv, -1)" in a debugger. If flags == -1 then the jv will be shown pretty-printed and in color. 2013-12-26 Nicolas Williams Document .foo.bar in manual Document exit numbers Normalize errors for -e 2013-12-25 Nicolas Williams Fix doc typos (.[foo] wanted to be .["foo"]) Add note to jq.1 about shell quoting 2013-12-20 Philipp Hagemeister Ignore the config/test-driver file This file is automatically generated and does not need to be committed. Fix @uri example Previously, the @uri example didn't match the actual behavior of the current jq, as exclamation marks do not need to be encoded in URIs. Replace the example with an input that needs encoding, and is encoded by jq. 2013-12-17 Stephen Dolan Allow negated object values without parens. Fixes #247 2013-12-17 Nicolas Williams Fix memmem() error 2013-12-13 Rémy Léone Adding a .travis.yml file to use the travis-ci.org From wikipedia: Travis CI is a hosted, distributed continuous integration service used to build and test projects hosted at GitHub. Travis CI is configured by adding a file named .travis.yml, which is a YAML format text file, to the root directory of the GitHub repository. Travis CI automatically detects when a commit has been made and pushed to a GitHub repository that is using Travis CI, and each time this happens, it will try to build the project and run tests. This includes commits to all branches, not just to the master branch. When that process has completed, it will notify a developer in the way it has been configured to do so — for example, by sending an email containing the test results (showing success or failure), or by posting a message on an IRC channel. It can be configured to run the tests on a range of different machines, with different software installed (such as older versions of a programming language, to test for compatibility). 2013-12-13 Stephen Dolan Make the testsuite run on machines without valgrind Format more integers as integers, not scientific notation. jq is now willing to put up to 15 zeros after an integer before moving to scientific notation. 2013-12-11 Nicolas Williams Complete more-arity feature not complete And test 2013-12-10 David R. MacIver convert range bounds to integers in a way that avoids undefined behaviour add checking of numeric indices to an array to see if they can reasonably be considered integers. Avoid undefined behaviour if out of bounds 2013-12-09 David R. MacIver some functions were missing prototypes. Add them 2013-12-08 David R. MacIver These vfprintfs are being used as if they were printfs. Fix that consistent use of goto out in main 2013-12-08 Stephen Dolan Refactor jv structure. New structure layout is simpler and also faster. In particular, it's now small enough to be passed in registers on amd64. Make testsuite not leak when compiled with -DNDEBUG. 2013-12-08 David R. MacIver test for losing memory on compile errors args to jq_compile_args were not getting freed when there were errors in the compile 2013-12-06 Nicolas Williams Fix double-free typo in print_error() Fix manual.yml 2013-12-04 Nicolas Williams Conditionally #define _GNU_SOURCE in compile.c Add tests for string index by string and builtins Add index and rindex builtins Add index strings by string; return string indexes % jq '.[","]' "a,bc,def,ghij,klmno" [1,4,8,13] % Make length return abs value of numeric inputs Add callback interface for errors Printing to stderr is not the right answer for a library. Add jv_string_vfmt() Document ltrimstr and rtrimstr Test ltrimstr and rtrimstr functions Add ltrimstr and rtrimstr functions Document -e / --exit-status argument Add -e | --exit-status CLI option Document tojson and fromjson builtins Test tojson and fromjson Add tojson and fromjson builtins Document split function Document string multiplication and division Document string functions and slicing Test string slicing Add string slicing Add tests for string division/splitting Add string division by string (split on separator) Test starts/endswith and string multiplication Add string multiplication by number Add startswith/endswith Add explode/implode jq functions to match jv API Use uint32_t for codepoint in jv_string_append_codepoint() Add jv string utility functions jv_string_empty() -> return an empty string with given allocated length (for fast appends) jv_string_append_codepoint -> append a single codepoint (int) to the given string jv_string_explode -> return an array of codepoints making up a string jv_string_implode -> return the UTF-8 encoding of an array of codepoint numbers Support more arguments for defs 2013-12-04 Stephen Dolan Preserve insertion order in objects. Closes #169. 2013-11-30 Nicolas Pouillard Add a few more test cases (from the man page) 2013-11-08 Stephen Dolan Add a --unbuffered option. Closes #206 2013-11-07 Peter van Dijk count should be length Example refers to a count function, which does not exist. Replacing it with length works. 2013-11-07 Stephen Dolan Fix a crash on group_by of empty list. Fixes #208. 2013-10-16 Ryoichi KATO Docs: add description of --from-file option 2013-10-06 Juan Guerrero Fix typo on error message 2013-09-19 Kenny Shen Add missing -i flag in build instructions 2013-09-14 Michael Daines Add test showing calculation of standard deviation 2013-09-13 Mike Daines Fix typo 2013-09-11 Michael Daines Add sqrt operator 2013-09-04 Jack Pearkes docs: update the tutorial to use GitHub's API 2013-09-01 Ankur Call AM_INIT_AUTOMAKE once only Fixes build with automake-1.14 2013-08-19 Joe Littlejohn Fix Makefile after refactoring of stacks in 05d90517b02 2013-06-23 Stephen Dolan Remove #includes from jv.h Fix the jv_parser interface. Use libtool's built-in symbol exporting rather than a mapfile. Move gen_utf8_tables to scripts Move libtool m4 junk to config/ and delete some autogenerated files. Remove Autoconf-generated config.h. 2013-06-22 Stephen Dolan Build libjq only once, and link it statically to ./jq This means ./jq is a real binary rather than a libtool turd. Fix distcheck. Update list of files to be distributed. Utf8 fixes. Closes #161 Reject all overlong UTF8 sequences. Fix various UTF8 parsing bugs. In particular, parse bad UTF8 by replacing the broken bits with U+FFFD and resychronise correctly after broken sequences. Fix example in manual for `floor`. See #155. 2013-06-21 Nicolas Williams Document floor Add floor operator Document mod Add mod (and setmod) operators Update .gitignore Add libjq autoconf goo Quiet setup.sh re: tmp dir 2013-06-21 Stephen Dolan Move cfunction invocation code to the interpreter loop. 2013-06-18 Nicolas Williams Fix serious bug in handling of --argfile Fix leaks in jv_load_file() 2013-06-17 Stephen Dolan Fold opcode.{c,h} into bytecode.{c,h} Simplify block functions for variables Saner build instructions in README.md Closes #144 Remove some initialise-to-zero code. This lets valgrind find more bugs - if a field isn't given a well-defined value valgrind will now find it instead of seeing it set to zero with memset. 2013-06-17 Nicolas Williams Remove accidentally introduced use of fopen "e" 2013-06-16 Stephen Dolan Merge pull request #114 from nicowilliams/nomem_handler Add jv_nomem_handler() 2013-06-16 Nicolas Williams Remove last remnant of main.h 2013-06-15 Nicolas Williams Allow --run-tests to take a file argument Fixup API to get closer to a libjq 2013-06-15 Nicolas Williams Move slurp_file() into library as jv_load_file() Needed as part of creating a libjq. 2013-06-14 Stephen Dolan Clean up lots of stack and frame logic. Move frame defs to execute.c 2013-06-13 Stephen Dolan Simplify frame logic. Unify all stacks. Passes tests, but needs cleanup. 2013-06-11 Stephen Dolan Support ."foo" syntax for accessing fields. See #141. 2013-06-09 Stephen Dolan Unify frame and data stacks 2013-06-05 Stephen Dolan Speed up cached configure (./configure -C) Clean up flex lines in build Lex and parse .foo better. '.as' is now valid, '. foo' is now invalid. See #141. 2013-06-04 Markus Lanthaler Update README.md Update the link to the documentation. All GitHub pages are now using the github.io domain. 2013-06-03 Stephen Dolan Make jq --version print to stdout, not stderr Better error handling for .foo case in parser. See #141. Let the parser rather than the lexer handle invalid characters. Add command-line option to sort object keys. Closes #79. Clean up Makefile.am (distcheck, rebuild version.h less often) 2013-05-31 Brendan Macmillan Stop warning on fgets, simple version Stop warning on fgets, complex version 2013-05-31 Stephen Dolan Squash a warning on some GCC versions 2013-05-29 Stephen Dolan Support for printing object keys in sorted order. No command-line option to enable this yet. See #79. 2013-05-29 Brendan Macmillan Bugfix multiline off-by-one (locfile.c) locfile.h -> locfile.h + locfile.c clean up includes of a few files Hack bugfix for multiline off-by-one (locfile.c) Load library from ~/.jq 2013-05-24 Stephen Dolan Make jq --version report an actual git revision. Closes #129. 2013-05-23 Nicolas Williams Add --argfile variant of --arg (issue #117) This is useful when one has a database (in JSON form) to query using jq input data. % echo '{"a":1, "c":5}' > db.json % echo '"c"'|./jq --argfile f /tmp/a '$f[.]' 5 % echo '"a"'|./jq --argfile f /tmp/a '$f[.]' 1 % echo '"b"'|./jq --argfile f /tmp/a '$f[.]' null % 2013-05-23 Stephen Dolan 'make clean' won't delete jq.1 if it can't be rebuilt. See #131 jq-jq-1.6/appveyor.yml0000600000175000017500000000442313366726451014310 0ustar czchenczchenenvironment: matrix: - MSYSTEM: MINGW64 PATH: C:\msys64\usr\bin;C:\msys64\mingw64\bin;C:\Windows\System32;C:\Windows;%PATH% - MSYSTEM: MINGW32 PATH: C:\msys64\usr\bin;C:\msys64\mingw32\bin;C:\Windows\System32;C:\Windows;%PATH% clone_script: - bash -lc "git clone -q --branch=$APPVEYOR_REPO_BRANCH https://github.com/${APPVEYOR_REPO_NAME}.git $APPVEYOR_BUILD_FOLDER" - bash -lc "cd $APPVEYOR_BUILD_FOLDER && git checkout -qf $APPVEYOR_REPO_BRANCH" - bash -lc "cd $APPVEYOR_BUILD_FOLDER && git submodule update --init --recursive" install: # update mysy2 - C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors" - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sy" - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S autoconf automake bison flex" - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S git" build_script: - bash -lc "exec 0 jq.html" - 7z a jq-package.zip jq.1 jq.html jq.exe - bash -lc "exec 0 Maintained by: Nicolas Williams William Langford Contributions by: Aaron Peschel Adam Lindberg Alex Chamberlain Andrew O'Brien - docs build Andrew Rodland - bug fixes Ankur - bug fixes Anthony Shortland - rpmbuild target Assaf Gordon - error handling Brendan Macmillan - bug fixes, load library from ~/.jq cdnbacon Charles Merriam Colin von Heuring Damian Gryski David Fetter - added --rawfile David Haguenauer David R. MacIver - bug fixes David Tolnay - destructuring, build improvements Doug Luce - build Eiichi Sato Eric Bréchemier - bug fix Filippo Giunchedi - bug fixes Filippo Valsorda - recursive object merge (`*`) Hanfei Shen i Ian Miell Jack Pearkes - update tutorial James Andariese - Dockerfile Jingwen Owen Ou - jqplay.org and link to it jkleint Joe Littlejohn - bug fixes Joel Purra Juan Guerrero - bug fixes Kenny Shen - doc fixes Kim De Mey - build Kim Toms LCD 47 Lee Thompson - autoconf stuff, rpm Marc Abramowitz Marc Bruggmann Markus Lanthaler - doc fixes Maxime Biais - build Michael Daines - add sqrt; doc fixes Mike Fletcher Mike McCabe - math (libm) functions Nicolas Pouillard - add tests Nicolas Williams - library-fication, autoconf stuff, exception handling, various Peter van Dijk - doc fixes Philipp Hagemeister - doc fixes pkoppstein - various builtins, improvements polyester - automake version update Ryoichi KATO - doc fixes Rémy Léone - add .travis.yml Santiago Lapresta - join, arrays, all, any, other filters Sebastian Freundt - build Shaun Guth - base64d Shay Elkin Simon Elsbrock - Debian Stefan Seemayer Stephen Roantree Stephen Shaw Steven Maude Steven Penny - Windows bug fixes tal@whatexit.org Travis Gockel Zhiming Wang 13ren jq-jq-1.6/jq.spec0000600000175000017500000000265313366726451013211 0ustar czchenczchen# This is spec file maintained by developers of JQ, not by a OS distro. # Your OS of choice will likely ignore this RPM spec file. Summary: Command-line JSON processor Name: jq Version: %{myver} Release: %{myrel}%{?dist} Source0: jq-%{myver}.tar.gz URL: https://stedolan.github.io/jq License: BSD AutoReqProv: no #BuildPrereq: autoconf, libtool, automake, flex, bison, python Group: Applications/System # Requires: # Disables debug packages and stripping of binaries: %global _enable_debug_package 0 %global debug_package %{nil} %global __os_install_post %{nil} # Crank up the compression %define _binary_payload w7.lzdio %description jq is a command-line JSON processor %prep %setup %build echo "Building in: \"$(pwd)\"" %if "%{devbuild}" == "yes" ./configure --prefix=%{_prefix} --enable-devel %else ./configure --prefix=%{_prefix} %endif make %install echo "Installing to: \"%{buildroot}\"" make install DESTDIR=%{buildroot} %clean rm -rf %{buildroot} %files %defattr(-,root,root) %{_bindir}/jq %if "%{devbuild}" == "yes" %{_libexecdir}/%{name}/jq_test %{_libexecdir}/%{name}/testdata %endif %{_datadir}/doc/%{name}/AUTHORS %{_datadir}/doc/%{name}/COPYING %{_datadir}/doc/%{name}/README %{_datadir}/doc/%{name}/README.md %{_datadir}/man/man1/jq.1 %{_includedir}/jq.h %{_includedir}/jv.h %{_prefix}/lib/libjq.a %{_prefix}/lib/libjq.la %{_prefix}/lib/libjq.so %{_prefix}/lib/libjq.so.1 %{_prefix}/lib/libjq.so.1.0.4 %changelog %pre %post jq-jq-1.6/tests/0000700000175000017500000000000013366726451013055 5ustar czchenczchenjq-jq-1.6/tests/jq-f-test.sh0000700000175000017500000000016613366726451015231 0ustar czchenczchen#!/bin/sh # this next line is ignored by jq, which otherwise does not continue comments \ exec jq -nef "$0" "$@" true jq-jq-1.6/tests/optional.test0000600000175000017500000000115113366726451015603 0ustar czchenczchen# See tests/jq.test and the jq manual for more information. # strptime() is not available on mingw/WIN32 [strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)] "2015-03-05T23:51:47Z" [[2015,2,5,23,51,47,4,63],1425599507] # Check day-of-week and day of year computations # (should trip an assert if this fails) # This date range last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ")) null [2037,1,11,1,2,3,3,41] # %e is not available on mingw/WIN32 strftime("%A, %B %e, %Y") 1435677542.822351 "Tuesday, June 30, 2015" jq-jq-1.6/tests/jqtest0000700000175000017500000000013613366726451014315 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" $VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/jq.test jq-jq-1.6/tests/mantest0000700000175000017500000000030713366726451014456 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" # We set PAGER because there's a mantest for `env` that uses it. (cd $JQBASEDIR/docs && rake mantests) | env PAGER=less $VALGRIND $Q $JQ -L "$mods" --run-tests jq-jq-1.6/tests/optionaltest0000700000175000017500000000014413366726451015527 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" $VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/optional.test jq-jq-1.6/tests/modules/0000700000175000017500000000000013366726451014525 5ustar czchenczchenjq-jq-1.6/tests/modules/test_bind_order1.jq0000600000175000017500000000003213366726451020305 0ustar czchenczchendef sym0: 1; def sym1: 1; jq-jq-1.6/tests/modules/a.jq0000600000175000017500000000004213366726451015277 0ustar czchenczchenmodule {version:1.7}; def a: "a"; jq-jq-1.6/tests/modules/.jq0000600000175000017500000000010713366726451015140 0ustar czchenczchendef foo: "baz"; def f: "wat"; def f: "foo"; def g: "bar"; def fg: f+g; jq-jq-1.6/tests/modules/b/0000700000175000017500000000000013366726451014746 5ustar czchenczchenjq-jq-1.6/tests/modules/b/b.jq0000600000175000017500000000003013366726451015516 0ustar czchenczchendef a: "b"; def b: "c"; jq-jq-1.6/tests/modules/test_bind_order0.jq0000600000175000017500000000001513366726451020305 0ustar czchenczchendef sym0: 0; jq-jq-1.6/tests/modules/test_bind_order2.jq0000600000175000017500000000003213366726451020306 0ustar czchenczchendef sym1: 2; def sym2: 2; jq-jq-1.6/tests/modules/lib/0000700000175000017500000000000013366726451015273 5ustar czchenczchenjq-jq-1.6/tests/modules/lib/jq/0000700000175000017500000000000013366726451015705 5ustar czchenczchenjq-jq-1.6/tests/modules/lib/jq/e/0000700000175000017500000000000013366726451016131 5ustar czchenczchenjq-jq-1.6/tests/modules/lib/jq/e/e.jq0000600000175000017500000000002013366726451016703 0ustar czchenczchendef bah: "bah"; jq-jq-1.6/tests/modules/lib/jq/f.jq0000600000175000017500000000002413366726451016464 0ustar czchenczchendef f: "f is here"; jq-jq-1.6/tests/modules/test_bind_order.jq0000600000175000017500000000025513366726451020233 0ustar czchenczchenimport "test_bind_order0" as t; import "test_bind_order1" as t; import "test_bind_order2" as t; def check: if [t::sym0,t::sym1,t::sym2] == [0,1,2] then true else false end; jq-jq-1.6/tests/modules/syntaxerror/0000700000175000017500000000000013366726451017125 5ustar czchenczchenjq-jq-1.6/tests/modules/syntaxerror/syntaxerror.jq0000600000175000017500000000000513366726451022056 0ustar czchenczchenwat; jq-jq-1.6/tests/modules/c/0000700000175000017500000000000013366726451014747 5ustar czchenczchenjq-jq-1.6/tests/modules/c/c.jq0000600000175000017500000000126113366726451015527 0ustar czchenczchenmodule {whatever:null}; import "a" as foo; import "d" as d {search:"./"}; import "d" as d2{search:"./"}; import "e" as e {search:"./../lib/jq"}; import "f" as f {search:"./../lib/jq"}; import "data" as $d; def a: 0; def c: if $d::d[0] != {this:"is a test",that:"is too"} then error("data import is busted") elif d2::meh != d::meh then error("import twice doesn't work") elif foo::a != "a" then error("foo::a didn't work as expected") elif d::meh != "meh" then error("d::meh didn't work as expected") elif e::bah != "bah" then error("e::bah didn't work as expected") elif f::f != "f is here" then error("f::f didn't work as expected") else foo::a + "c" + d::meh + e::bah end; jq-jq-1.6/tests/modules/c/d.jq0000600000175000017500000000002013366726451015520 0ustar czchenczchendef meh: "meh"; jq-jq-1.6/tests/modules/data.json0000600000175000017500000000005613366726451016334 0ustar czchenczchen{ "this": "is a test", "that": "is too" } jq-jq-1.6/tests/shtest0000700000175000017500000002474013366726451014324 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" PATH=$JQBASEDIR:$PATH $JQBASEDIR/tests/jq-f-test.sh > /dev/null if [ -f "$JQBASEDIR/.libs/libinject_errors.so" ]; then # Do some simple error injection tests to check that we're handling # I/O errors correctly. ( libinject=$JQBASEDIR/.libs/libinject_errors.so cd $d LD_PRELOAD=$libinject $JQ . /dev/null touch fail_read LD_PRELOAD=$libinject $JQ . fail_read && exit 2 touch fail_close LD_PRELOAD=$libinject $JQ . fail_close && exit 2 true ) fi printf 'a\0b\nc\0d\ne' > $d/input $VALGRIND $Q $JQ -Rse '. == "a\u0000b\nc\u0000d\ne"' $d/input $VALGRIND $Q $JQ -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input ## Test constant folding ## XXX If we add a builtin to list the program's disassembly then we can ## move all of these into tests/all.test # String constant folding (addition only) nref=$($VALGRIND $Q $JQ -n --debug-dump-disasm '"foo"' | wc -l) # Numeric constant folding (not all ops yet) n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '1+1' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '1-1' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '2*3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9/3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9==3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9!=3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9<=3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9>=3' | wc -l) if [ $n -ne $nref ]; then echo "Constant expression folding for strings didn't work" exit 1 fi ## Test JSON sequence support cat > $d/expected < /dev/null 2> $d/out cmp $d/out $d/expected cat > $d/expected < /dev/null 2> $d/out cmp $d/out $d/expected # Note that here jq sees no inputs at all but it still succeeds because # --seq ignores parse errors cat > $d/expected < $d/out 2>&1 cmp $d/out $d/expected # Numeric values truncated by EOF are ignored cat > $d/expected < $d/out 2>&1 cmp $d/out $d/expected cat > $d/expected <:1): Unfinished abandoned text at EOF at line 2, column 0 EOF if printf '1\n' | $JQ -cen --seq '[inputs] == []' >/dev/null 2> $d/out; then printf 'Error expected but jq exited successfully\n' 1>&2 exit 2 fi cmp $d/out $d/expected # Regression test for #951 printf '"a\n' > $d/input if $VALGRIND $Q $JQ -e . $d/input; then printf 'Issue #951 is back?\n' 1>&2 exit 2 fi # Regression test for #1534 echo "[1,2,3,4]" > $d/expected printf "[1,2][3,4]" | $JQ -cs add > $d/out 2>&1 cmp $d/out $d/expected printf "[1,2][3,4]\n" | $JQ -cs add > $d/out 2>&1 cmp $d/out $d/expected ## Test streaming parser ## If we add an option to stream to the `import ... as $symbol;` directive ## then we can move these tests into tests/all.test. $VALGRIND $Q $JQ -c '. as $d|path(..) as $p|$d|getpath($p)|scalars_or_empty|[$p,.]' < "$JQTESTDIR/torture/input0.json" > $d/out0 $VALGRIND $Q $JQ --stream -c '.|select(length==2)' < "$JQTESTDIR/torture/input0.json" > $d/out1 diff $d/out0 $d/out1 ## XXX This test can be moved to tests/all.test _now_ clean=false if which seq > /dev/null 2>&1; then # XXX We should try every prefix of input0.json, but that makes this # test very, very slow when run with valgrind, and the whole point # is to run it with valgrind. # #len=$(wc -c < "$JQTESTDIR/torture/input0.json") if [ -z "$VALGRIND" ]; then start=1 end=$(wc -c < "$JQTESTDIR/torture/input0.json") else start=120 end=151 fi for i in $(seq $start $end); do dd "if=tests/torture/input0.json" bs=$i count=1 2>/dev/null | $VALGRIND $JQ -c . > $d/out0 2>$d/err || true if [ -n "$VALGRIND" ]; then grep '^==[0-9][0-9]*== ERROR SUMMARY: 0 errors' $d/err > /dev/null else tail -1 $d/err | egrep -i 'assert|abort|core' && false fi dd "if=tests/torture/input0.json" bs=$i count=1 2>/dev/null | $VALGRIND $JQ -cn --stream 'fromstream(inputs)' > $d/out1 2>$d/err || true if [ -n "$VALGRIND" ]; then grep '^==[0-9][0-9]*== ERROR SUMMARY: 0 errors' $d/err > /dev/null else tail -1 $d/err | egrep -i 'assert|abort|core' && false fi diff $d/out0 $d/out1 done else echo "Not doing torture tests" fi ## Fuzz parser ## XXX With a $(urandom) builtin we could move this test into tests/all.test clean=false if dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null; then # Have a /dev/urandom, good $VALGRIND $Q $JQ --seq . $d/rand >/dev/null 2>&1 $VALGRIND $Q $JQ --seq --stream . $d/rand >/dev/null 2>&1 dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null $VALGRIND $Q $JQ --seq . $d/rand >/dev/null 2>&1 $VALGRIND $Q $JQ --seq --stream . $d/rand >/dev/null 2>&1 dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null $VALGRIND $Q $JQ --seq . $d/rand >/dev/null 2>&1 $VALGRIND $Q $JQ --seq --stream . $d/rand >/dev/null 2>&1 fi clean=true ## Test library/module system # Check handling of ~/.jq; these can't move into jq_test.c yet because # they depend on $HOME if [ "$(HOME="$mods" $VALGRIND $Q $JQ -nr fg)" != foobar ]; then echo "Bug #479 appears to be back" 1>&2 exit 1 fi if [ $(HOME="$mods" $VALGRIND $Q $JQ --debug-dump-disasm -n fg | grep '^[a-z]' | wc -l) -gt 3 ]; then echo "Binding too many defs into program" 1>&2 exit 1 fi cd "$JQBASEDIR" # so that relative library paths are guaranteed correct if ! $VALGRIND $Q $JQ -L ./tests/modules -ne 'import "test_bind_order" as check; check::check==true'; then echo "Issue #817 regression?" 1>&2 exit 1 fi cd "$JQBASEDIR" if ! $VALGRIND $Q $JQ -L tests/modules -ne 'import "test_bind_order" as check; check::check==true'; then echo "Issue #817 regression?" 1>&2 exit 1 fi ## Halt if ! $VALGRIND $Q $JQ -n halt; then echo "jq halt didn't work as expected" 1>&2 exit 1 fi if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(1)'; then echo "jq halt_error(1) didn't work as expected" 1>&2 exit 1 elif [ $? -ne 1 ]; then echo "jq halt_error(1) had wrong error code" 1>&2 exit 1 fi if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(11)'; then echo "jq halt_error(11) didn't work as expected" 1>&2 exit 1 elif [ $? -ne 11 ]; then echo "jq halt_error(11) had wrong error code" 1>&2 exit 1 fi if [ -n "$($VALGRIND $Q $JQ -n 'halt_error(1)' 2>&1)" ]; then echo "jq halt_error(1) had unexpected output" 1>&2 exit 1 fi if [ -n "$($VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>/dev/null)" ]; then echo "jq halt_error(1) had unexpected output on stdout" 1>&2 exit 1 fi if [ "$($VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>&1)" != xyz ]; then echo "jq halt_error(1) had unexpected output" 1>&2 exit 1 fi # Check $JQ_COLORS $JQ -Ccn . > $d/color printf '\033[1;30mnull\033[0m\n' > $d/expect cmp $d/color $d/expect JQ_COLORS='4;31' $JQ -Ccn . > $d/color printf '\033[4;31mnull\033[0m\n' > $d/expect cmp $d/color $d/expect JQ_COLORS='1;30:0;31:0;32:0;33:0;34:1;35:1;36' \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color ( printf '\033[1;35m[\033[1;36m{' printf '\033[0m\033[34;1m"a"\033[' printf '0m\033[1;36m:\033[0m\033[' printf '0;32mtrue\033[0m\033[1' printf ';36m,\033[0m\033[34;1m' printf '"b"\033[0m\033[1;36m:\033' printf '[0m\033[0;31mfalse\033' printf '[0m\033[1;36m\033[1;36' printf 'm}\033[0m\033[1;35m,\033[' printf '0;33m123\033[0m\033[1;' printf '35m,\033[1;30mnull\033' printf '[0m\033[1;35m\033[1;35' printf 'm]\033[0m\n' ) > $d/expect cmp $d/color $d/expect # Check garbage in JQ_COLORS. We write each color sequence into a 16 # char buffer that needs to hold ESC [ m NUL, so each color # sequence can be no more than 12 chars (bytes). These emit a warning # on stderr. set -vx echo 'Failed to set $JQ_COLORS' > $d/expect_warning $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/expect JQ_COLORS='garbage;30:*;31:,;3^:0;$%:0;34:1;35:1;36' \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning cmp $d/color $d/expect cmp $d/warning $d/expect_warning JQ_COLORS='1234567890123456789;30:0;31:0;32:0;33:0;34:1;35:1;36' \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning cmp $d/color $d/expect cmp $d/warning $d/expect_warning JQ_COLORS='1;31234567890123456789:0;31:0;32:0;33:0;34:1;35:1;36' \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning cmp $d/color $d/expect cmp $d/warning $d/expect_warning JQ_COLORS='1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456' \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning cmp $d/color $d/expect cmp $d/warning $d/expect_warning JQ_COLORS="0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:" \ $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning cmp $d/color $d/expect cmp $d/warning $d/expect_warning exit 0 jq-jq-1.6/tests/onigtest0000700000175000017500000000014013366726451014632 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" $VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/onig.test jq-jq-1.6/tests/base64.test0000600000175000017500000000131013366726451015037 0ustar czchenczchen# Tests are groups of three lines: program, input, expected output # Blank lines and lines starting with # are ignored @base64 "<>&'\"\t" "PD4mJyIJ" # decoding encoded output results in same text (@base64|@base64d) "<>&'\"\t" "<>&'\"\t" # regression test for #436 @base64 "foóbar\n" "Zm/Ds2Jhcgo=" @base64d "Zm/Ds2Jhcgo=" "foóbar\n" # optional trailing equals padding (With padding, this is cWl4YmF6Cg==) @base64d "cWl4YmF6Cg" "qixbaz\n" # invalid base64 characters (whitespace) . | try @base64d catch . "Not base64 data" "string (\"Not base64...) is not valid base64 data" # invalid base64 (too many bytes, QUJD = "ABCD" . | try @base64d catch . "QUJDa" "string (\"QUJDa\") trailing base64 byte found" jq-jq-1.6/tests/base64test0000700000175000017500000000014213366726451014764 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" $VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/base64.test jq-jq-1.6/tests/onig.test0000600000175000017500000000503313366726451014715 0ustar czchenczchen# match builtin [match("( )*"; "g")] "abc" [{"offset":0, "length":0, "string":"", "captures":[]},{"offset":1, "length":0, "string":"", "captures":[]},{"offset":2, "length":0, "string":"", "captures":[]}] [match("( )*"; "gn")] "abc" [] [match("a"; "gi")] "āáàä" [] [match(["(bar)"])] "foo bar" [{"offset": 4, "length": 3, "string": "bar", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": null}]}] # offsets account for combining codepoints and multi-byte UTF-8 [match("bar")] "ā bar with a combining codepoint U+0304" [{"offset": 3, "length": 3, "string": "bar", "captures":[]}] # matches with combining codepoints still count them in their length [match("bār")] "a bār" [{"offset": 2, "length": 4, "string": "bār", "captures":[]}] [match(".+?\\b")] "ā two-codepoint grapheme" [{"offset": 0, "length": 2, "string": "ā", "captures":[]}] [match(["foo (?bar)? foo", "ig"])] "foo bar foo foo foo" [{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}] #test builtin [test("( )*"; "gn")] "abc" [false] [test("ā")] "ā" [true] capture("(?[a-z]+)-(?[0-9]+)") "xyzzy-14" {"a":"xyzzy","n":"14"} # jq-coded utilities built on match: # # The second element in these tests' inputs tests the case where the # fromstring matches both the head and tail of the string [.[] | sub(", "; ":")] ["a,b, c, d, e,f", ", a,b, c, d, e,f, "] ["a,b:c, d, e,f",":a,b, c, d, e,f, "] sub("^(?.)"; "Head=\(.head) Tail=") "abcdef" "Head=a Tail=bcdef" [.[] | gsub(", "; ":")] ["a,b, c, d, e,f",", a,b, c, d, e,f, "] ["a,b:c:d:e,f",":a,b:c:d:e,f:"] gsub("(?\\d)"; ":\(.d);") "a1b2" "a:1;b:2;" gsub("a";"b") "aaaaa" "bbbbb" gsub( "(.*)"; ""; "x") "" "" [.[] | scan(", ")] ["a,b, c, d, e,f",", a,b, c, d, e,f, "] [", ",", ",", ",", ",", ",", ",", ",", "] [.[]|[[sub(", *";":")], [gsub(", *";":")], [scan(", *")]]] ["a,b, c, d, e,f",", a,b, c, d, e,f, "] [[["a:b, c, d, e,f"],["a:b:c:d:e:f"],[",",", ",", ",", ",","]],[[":a,b, c, d, e,f, "],[":a:b:c:d:e:f:"],[", ",",",", ",", ",", ",",",", "]]] [.[]|[[sub(", +";":")], [gsub(", +";":")], [scan(", +")]]] ["a,b, c, d, e,f",", a,b, c, d, e,f, "] [[["a,b:c, d, e,f"],["a,b:c:d:e,f"],[", ",", ",", "]],[[":a,b, c, d, e,f, "],[":a,b:c:d:e,f:"],[", ",", ",", ",", ",", "]]] # reference to named captures gsub("(?.)[^a]*"; "+\(.x)-") "Abcabc" "+A-+a-" # utf-8 sub("(?.)"; "\(.x)!") "’" "’!" jq-jq-1.6/tests/utf8test0000700000175000017500000000032313366726451014567 0ustar czchenczchen#!/bin/sh . "${0%/*}/setup" "$@" if [ "$($VALGRIND $Q $JQ -nf $JQTESTDIR/utf8-truncate.jq)" != "true" ]; then echo "UTF-8 byte sequences that span the jv_load_file read buffer are mangled" exit 1 fi exit 0 jq-jq-1.6/tests/torture/0000700000175000017500000000000013366726451014561 5ustar czchenczchenjq-jq-1.6/tests/torture/input0.json0000600000175000017500000000022713366726451016676 0ustar czchenczchen0 12 true false null [] {} [{}] [[]] [0,01,[12,22,[34,[45,56],7]],[]] {"a":[1]} {"a":[{}]} {"a":[{},[],[[]]]} {"a":[{"b":[]}]} {"a":[{"b":[]},{},[2]]} jq-jq-1.6/tests/setup0000700000175000017500000000143113366726451014142 0ustar czchenczchen#!/bin/sh # This is meant to be included by each test's shell script driver. if [ -n "$TRACE_TESTS" ]; then set -x fi set -eu JQTESTDIR=$(cd "$(dirname "$0")" && pwd) JQBASEDIR=$JQTESTDIR/.. JQ=$JQBASEDIR/jq if [ -z "${NO_VALGRIND-}" ] && which valgrind > /dev/null; then VALGRIND="valgrind --error-exitcode=1 --leak-check=full \ --suppressions=$JQTESTDIR/onig.supp" VG_EXIT0=--error-exitcode=0 Q=-q else VALGRIND= VG_EXIT0= Q= fi mods=$JQTESTDIR/modules clean=true d= clean () { if ! $clean; then echo "See temp files in $d!" elif [ -n "$d" ]; then rm -rf "$d" fi } trap clean EXIT d=$(mktemp -d -t jqXXXXXX || true) if [ -z "$d" ]; then echo "Your OS does not support mktemp(1) -d" 1>&2 exit 1 fi jq-jq-1.6/tests/onig.supp0000600000175000017500000000035713366726451014731 0ustar czchenczchen{ onig node recycling Memcheck:Leak ... fun:onig_parse_make_tree ... } { onig unicode case insensitivity 1 Memcheck:Leak ... fun:setup_tree ... } { onig unicode case insensitivity 2 Memcheck:Leak ... fun:onig*unicode* ... } jq-jq-1.6/tests/utf8-truncate.jq0000600000175000017500000001003313366726451016121 0ustar czchenczchendef fill:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; def e:"日本語"; e == "日本語" jq-jq-1.6/tests/jq.test0000600000175000017500000007167313366726451014410 0ustar czchenczchen# Tests are groups of three lines: program, input, expected output # Blank lines and lines starting with # are ignored # # Simple value tests to check parser. Input is irrelevant # true null true false null false null 42 null 1 null 1 -1 null -1 # FIXME: much more number testing needed {} null {} [] null [] {x: -1} null {"x": -1} # The input line starts with a 0xFEFF (byte order mark) codepoint # No, there is no reason to have a byte order mark in UTF8 text. # But apparently people do, so jq shouldn't break on it. . "byte order mark" "byte order mark" # We test escapes by matching them against Unicode codepoints # FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs) "Aa\r\n\t\b\f\u03bc" null "Aa\u000d\u000a\u0009\u0008\u000c\u03bc" . "Aa\r\n\t\b\f\u03bc" "Aa\u000d\u000a\u0009\u0008\u000c\u03bc" "inter\("pol" + "ation")" null "interpolation" @text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64,(@base64 | @base64d) "<>&'\"\t" "<>&'\"\t" "\"<>&'\\\"\\t\"" "1,\"<>&'\"\"\t\"" "1\t<>&'\"\\t" "<>&'"\t" "%3C%3E%26'%22%09" "'<>&'\\''\"\t'" "PD4mJyIJ" "<>&'\"\t" # regression test for #436 @base64 "foóbar\n" "Zm/Ds2Jhcgo=" @base64d "Zm/Ds2Jhcgo=" "foóbar\n" @uri "\u03bc" "%CE%BC" @html "\(.)" "" "<script>hax</script>" [.[]|tojson|fromjson] ["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]] ["foo",1,["a",1,"b",2,{"foo":"bar"}]] # # Dictionary construction syntax # {a: 1} null {"a":1} {a,b,(.d):.a,e:.b} {"a":1, "b":2, "c":3, "d":"c"} {"a":1, "b":2, "c":1, "e":2} {"a",b,"a$\(1+1)"} {"a":1, "b":2, "c":3, "a$2":4} {"a":1, "b":2, "a$2":4} %%FAIL {(0):1} jq: error: Cannot use number (0) as object key at , line 1: %%FAIL {non_const:., (0):1} jq: error: Cannot use number (0) as object key at , line 1: # # Field access, piping # .foo {"foo": 42, "bar": 43} 42 .foo | .bar {"foo": {"bar": 42}, "bar": "badvalue"} 42 .foo.bar {"foo": {"bar": 42}, "bar": "badvalue"} 42 .foo_bar {"foo_bar": 2} 2 .["foo"].bar {"foo": {"bar": 42}, "bar": "badvalue"} 42 ."foo"."bar" {"foo": {"bar": 20}} 20 [.[]|.foo?] [1,[2],{"foo":3,"bar":4},{},{"foo":5}] [3,null,5] [.[]|.foo?.bar?] [1,[2],[],{"foo":3},{"foo":{"bar":4}},{}] [4,null] [..] [1,[[2]],{ "a":[1]}] [[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1] [.[]|.[]?] [1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]] [1,[2,[[3]]],{},{"a":[1,[2]]}] [.[]|.[1:3]?] [1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]] [null,"bc",[],[2,3],[2]] # # Negative array indices # try (.foo[-1] = 0) catch . null "Out of bounds negative array index" try (.foo[-2] = 0) catch . null "Out of bounds negative array index" .[-1] = 5 [0,1,2] [0,1,5] .[-2] = 5 [0,1,2] [0,5,2] # # Multiple outputs, iteration # .[] [1,2,3] 1 2 3 1,1 [] 1 1 1,. [] 1 [] [.] [2] [[2]] [[2]] [3] [[2]] [{}] [2] [{}] [.[]] ["a"] ["a"] [(.,1),((.,.[]),(2,3))] ["a","b"] [["a","b"],1,["a","b"],"a","b",2,3] [([5,5][]),.,.[]] [1,2,3] [5,5,[1,2,3],1,2,3] {x: (1,2)},{x:3} | .x null 1 2 3 .[-2] [1,2,3] 2 [range(0;10)] null [0,1,2,3,4,5,6,7,8,9] [range(0,1;3,4)] null [0,1,2, 0,1,2,3, 1,2, 1,2,3] [range(0;10;3)] null [0,3,6,9] [range(0;10;-1)] null [] [range(0;-5;-1)] null [0,-1,-2,-3,-4] [range(0,1;4,5;1,2)] null [0,1,2,3,0,2, 0,1,2,3,4,0,2,4, 1,2,3,1,3, 1,2,3,4,1,3] [while(.<100; .*2)] 1 [1,2,4,8,16,32,64] [(label $here | .[] | if .>1 then break $here else . end), "hi!"] [0,1,2] [0,1,"hi!"] [(label $here | .[] | if .>1 then break $here else . end), "hi!"] [0,2,1] [0,"hi!"] %%FAIL . as $foo | break $foo jq: error: $*label-foo is not defined at , line 1: [.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]] [1,2,3,4,5] [1,2,6,24,120] [label $out | foreach .[] as $item ([3, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1])] [11,22,33,44,55,66,77,88,99] [11,22,33] [foreach range(5) as $item (0; $item)] null [0,1,2,3,4] [foreach .[] as [$i, $j] (0; . + $i - $j)] [[2,1], [5,3], [6,4]] [1,3,5] [foreach .[] as {a:$a} (0; . + $a; -.)] [{"a":1}, {"b":2}, {"a":3, "b":4}] [-1, -1, -4] [limit(3; .[])] [11,22,33,44,55,66,77,88,99] [11,22,33] [first(range(.)), last(range(.)), nth(0; range(.)), nth(5; range(.)), try nth(-1; range(.)) catch .] 10 [0,9,0,5,"nth doesn't support negative indices"] # Check that first(g) does not extract more than one value from g first(1,error("foo")) null 1 # # Check that various builtins evalute all arguments where appropriate, # doing cartesian products where appropriate. # # Check that limit does work for each value produced by n! [limit(5,7; range(9))] null [0,1,2,3,4,0,1,2,3,4,5,6] # Same check for nth [nth(5,7; range(9;0;-1))] null [4,2] # Same check for range/3 [range(0,1,2;4,3,2;2,3)] null [0,2,0,3,0,2,0,0,0,1,3,1,1,1,1,1,2,2,2,2] # Same check for range/1 [range(3,5)] null [0,1,2,0,1,2,3,4] # Same check for index/1, rindex/1, indices/1 [(index(",","|"), rindex(",","|")), indices(",","|")] "a,b|c,d,e||f,g,h,|,|,i,j" [1,3,22,19,[1,5,7,12,14,16,18,20,22],[3,9,10,17,19]] # Same check for join/1 join(",","/") ["a","b","c","d"] "a,b,c,d" "a/b/c/d" [.[]|join("a")] [[],[""],["",""],["","",""]] ["","","a","aa"] # Same check for flatten/1 flatten(3,2,1) [0, [1], [[2]], [[[3]]]] [0,1,2,3] [0,1,2,[3]] [0,1,[2],[[3]]] # # Slices # [.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] [0,1,2,3,4,5,6] [[], [2,3], [0,1,2,3,4], [5,6], [], []] [.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] "abcdefghi" ["","","abcdefg","hi","",""] del(.[2:4],.[0],.[-2:]) [0,1,2,3,4,5,6,7] [1,4,5] .[2:4] = ([], ["a","b"], ["a","b","c"]) [0,1,2,3,4,5,6,7] [0,1,4,5,6,7] [0,1,"a","b",4,5,6,7] [0,1,"a","b","c",4,5,6,7] # Slices at large offsets (issue #1108) # # This is written this way because [range()] is # significantly slower under valgrind than .[] = value. # # We range down rather than up so that we have just one realloc. reduce range(65540;65536;-1) as $i ([]; .[$i] = $i)|.[65536:] null [null,65537,65538,65539,65540] # # Variables # 1 as $x | 2 as $y | [$x,$y,$x] null [1,2,1] [1,2,3][] as $x | [[4,5,6,7][$x]] null [5] [6] [7] 42 as $x | . | . | . + 432 | $x + 1 34324 43 1 as $x | [$x,$x,$x as $x | $x] null [1,1,1] [1, {c:3, d:4}] as [$a, {c:$b, b:$c}] | $a, $b, $c null 1 3 null . as {as: $kw, "str": $str, ("e"+"x"+"p"): $exp} | [$kw, $str, $exp] {"as": 1, "str": 2, "exp": 3} [1, 2, 3] .[] as [$a, $b] | [$b, $a] [[1], [1, 2, 3]] [null, 1] [2, 1] . as $i | . as [$i] | $i [0] 0 . as [$i] | . as $i | $i [0] [0] %%FAIL IGNORE MSG . as [] | null jq: error: syntax error, unexpected ']', expecting '$' or '[' or '{' (Unix shell quoting issues?) at , line 1: %%FAIL IGNORE MSG . as {} | null jq: error: syntax error, unexpected '}' (Unix shell quoting issues?) at , line 1: # [.,(.[] | {x:.},.),.,.[]] # # Builtin functions # 1+1 null 2 1+1 "wtasdf" 2.0 2-1 null 1 2-(-1) null 3 1e+0+0.001e3 "I wonder what this will be?" 20e-1 .+4 15 19.0 .+null {"a":42} {"a":42} null+. null null .a+.b {"a":42} 42 [1,2,3] + [.] null [1,2,3,null] {"a":1} + {"b":2} + {"c":3} "asdfasdf" {"a":1, "b":2, "c":3} "asdf" + "jkl;" + . + . + . "some string" "asdfjkl;some stringsome stringsome string" "\u0000\u0020\u0000" + . "\u0000\u0020\u0000" "\u0000 \u0000\u0000 \u0000" 42 - . 11 31 [1,2,3,4,1] - [.,3] 1 [2,4] [10 * 20, 20 / .] 4 [200, 5] 1 + 2 * 2 + 10 / 2 null 10 [16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2] null [2, 8, 10, 14] 25 % 7 null 4 49732 % 472 null 172 1 + tonumber + ("10" | tonumber) 4 15 [{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]] {"object": {"a":42}, "num":10.0} [true, true, false, false, false, false, false, false, false] [true, true, false, false, false, false, false, false, false] [false, false, true, true, false, false, false, false, false] [false, false, true, true, false, false, false, false, false] [false, false, false, false, true, false, false, false, false] [false, false, false, false, false, true, false, false, false] [false, false, false, false, false, false, true, false, false] [false, false, false, false, false, false, false, true, false] [false, false, false, false, false, false, false, false, true ] [.[] | length] [[], {}, [1,2], {"a":42}, "asdf", "\u03bc"] [0, 0, 2, 1, 4, 1] utf8bytelength "asdf\u03bc" 6 [.[] | try utf8bytelength catch .] [[], {}, [1,2], 55, true, false] ["array ([]) only strings have UTF-8 byte length","object ({}) only strings have UTF-8 byte length","array ([1,2]) only strings have UTF-8 byte length","number (55) only strings have UTF-8 byte length","boolean (true) only strings have UTF-8 byte length","boolean (false) only strings have UTF-8 byte length"] map(keys) [{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}] [[], ["abc","abcd","abcde"], ["x","y","z"]] [1,2,empty,3,empty,4] null [1,2,3,4] map(add) [[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]] [null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}] map_values(.+1) [0,1,2] [1,2,3] # # User-defined functions # Oh god. # def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g 3.0 106.0 105.0 def f: (1000,2000); f 123412345 1000 2000 def f(a;b;c;d;e;f): [a+1,b,c,d,e,f]; f(.[0];.[1];.[0];.[0];.[0];.[0]) [1,2] [2,2,1,1,1,1] # Test precedence of 'def' vs '|' def a: 0; . | a null 0 # Many arguments def f(a;b;c;d;e;f;g;h;i;j): [j,i,h,g,f,e,d,c,b,a]; f(.[0];.[1];.[2];.[3];.[4];.[5];.[6];.[7];.[8];.[9]) [0,1,2,3,4,5,6,7,8,9] [9,8,7,6,5,4,3,2,1,0] ([1,2] + [4,5]) [1,2,3] [1,2,4,5] true [1] true null,1,null "hello" null 1 null [1,2,3] [5,6] [1,2,3] [.[]|floor] [-1.1,1.1,1.9] [-2, 1, 1] [.[]|sqrt] [4,9] [2,3] (add / length) as $m | map((. - $m) as $d | $d * $d) | add / length | sqrt [2,4,4,4,5,5,7,9] 2 # Should write a test that calls the -lm function from C (or bc(1)) to # check that they match the corresponding jq functions. However, # there's so little template code standing between that it suffices to # test a handful of these. The results were checked by eye against # bc(1). atan * 4 * 1000000|floor / 1000000 1 3.141592 [(3.141592 / 2) * (range(0;20) / 20)|cos * 1000000|floor / 1000000] null [1,0.996917,0.987688,0.972369,0.951056,0.923879,0.891006,0.85264,0.809017,0.760406,0.707106,0.649448,0.587785,0.522498,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459] [(3.141592 / 2) * (range(0;20) / 20)|sin * 1000000|floor / 1000000] null [0,0.078459,0.156434,0.233445,0.309016,0.382683,0.45399,0.522498,0.587785,0.649447,0.707106,0.760405,0.809016,0.85264,0.891006,0.923879,0.951056,0.972369,0.987688,0.996917] def f(x): x | x; f([.], . + [42]) [1,2,3] [[[1,2,3]]] [[1,2,3],42] [[1,2,3,42]] [1,2,3,42,42] # test multiple function arities and redefinition def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f] 1 [33,101] # test closures and lexical scoping def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x) "more testing" [1,100,2100.0,100,2100.0] # test def f($a) syntax def x(a;b): a as $a | b as $b | $a + $b; def y($a;$b): $a + $b; def check(a;b): [x(a;b)] == [y(a;b)]; check(.[];.[]*2) [1,2,3] true # test backtracking through function calls and returns # this test is *evil* [[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f] 999999999 [[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]] # test recursion def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac] [1,2,3,4] [1,2,6,24] # test stack overflow and reallocation # this test is disabled for now, it takes a realllllly long time. # def f: if length > 1000 then . else .+[1]|f end; f | length # [] # 1001 reduce .[] as $x (0; . + $x) [1,2,4] 7 reduce .[] as [$i, {j:$j}] (0; . + $i - $j) [[2,{"j":1}], [5,{"j":3}], [6,{"j":4}]] 5 reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j) null 14 # This, while useless, should still compile. reduce . as $n (.; .) null null # Destructuring . as {$a, b: [$c, {$d}]} | [$a, $c, $d] {"a":1, "b":[2,{"d":3}]} [1,2,3] . as {$a, $b:[$c, $d]}| [$a, $b, $c, $d] {"a":1, "b":[2,{"d":3}]} [1,[2,{"d":3}],2,{"d":3}] # Destructuring with alternation .[] | . as {$a, b: [$c, {$d}]} ?// [$a, {$b}, $e] ?// $f | [$a, $b, $c, $d, $e, $f] [{"a":1, "b":[2,{"d":3}]}, [4, {"b":5, "c":6}, 7, 8, 9], "foo"] [1, null, 2, 3, null, null] [4, 5, null, null, 7, null] [null, null, null, null, null, "foo"] # Destructuring DUP/POP issues .[] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a [[3],[4],[5],6] # Runtime error: "jq: Cannot index array with string \"c\"" .[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a [[3],[4],[5],6] # Runtime error: "jq: Cannot index array with string \"c\"" [[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a null # Runtime error: "jq: Cannot index array with string \"c\"" [[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a null # Runtime error: "jq: Cannot index array with string \"c\"" .[] | . as {a:$a} ?// {a:$a} ?// $a | $a [[3],[4],[5],6] [3] [4] [5] 6 .[] as {a:$a} ?// {a:$a} ?// $a | $a [[3],[4],[5],6] [3] [4] [5] 6 [[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// $a | $a null [3] [4] [5] 6 [[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// $a | $a null [3] [4] [5] 6 .[] | . as {a:$a} ?// $a ?// {a:$a} | $a [[3],[4],[5],6] [3] [4] [5] 6 .[] as {a:$a} ?// $a ?// {a:$a} | $a [[3],[4],[5],6] [3] [4] [5] 6 [[3],[4],[5],6][] | . as {a:$a} ?// $a ?// {a:$a} | $a null [3] [4] [5] 6 [[3],[4],[5],6] | .[] as {a:$a} ?// $a ?// {a:$a} | $a null [3] [4] [5] 6 .[] | . as $a ?// {a:$a} ?// {a:$a} | $a [[3],[4],[5],6] [3] [4] [5] 6 .[] as $a ?// {a:$a} ?// {a:$a} | $a [[3],[4],[5],6] [3] [4] [5] 6 [[3],[4],[5],6][] | . as $a ?// {a:$a} ?// {a:$a} | $a null [3] [4] [5] 6 [[3],[4],[5],6] | .[] as $a ?// {a:$a} ?// {a:$a} | $a null [3] [4] [5] 6 . as $dot|any($dot[];not) [1,2,3,4,true,false,1,2,3,4,5] true . as $dot|any($dot[];not) [1,2,3,4,true] false . as $dot|all($dot[];.) [1,2,3,4,true,false,1,2,3,4,5] false . as $dot|all($dot[];.) [1,2,3,4,true] true # # Paths # path(.foo[0,1]) null ["foo", 0] ["foo", 1] path(.[] | select(.>3)) [1,5,3] [1] path(.) 42 [] try path(.a | map(select(.b == 0))) catch . {"a":[{"b":0}]} "Invalid path expression with result [{\"b\":0}]" try path(.a | map(select(.b == 0)) | .[0]) catch . {"a":[{"b":0}]} "Invalid path expression near attempt to access element 0 of [{\"b\":0}]" try path(.a | map(select(.b == 0)) | .c) catch . {"a":[{"b":0}]} "Invalid path expression near attempt to access element \"c\" of [{\"b\":0}]" try path(.a | map(select(.b == 0)) | .[]) catch . {"a":[{"b":0}]} "Invalid path expression near attempt to iterate through [{\"b\":0}]" path(.a[path(.b)[0]]) {"a":{"b":0}} ["a","b"] [paths] [1,[[],{"a":2}]] [[0],[1],[1,0],[1,1],[1,1,"a"]] [leaf_paths] [1,[[],{"a":2}]] [[0],[1,1,"a"]] ["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) {"bar": 42, "foo": ["a", "b", "c", "d"]} "b" {"bar": 42, "foo": ["a", 20, "c", "d"]} {"bar": 42, "foo": ["a", "c", "d"]} map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]])) [[0], [0,1], [0,1,2]] [null, null, 2] [[0,null,42], [0,1,42], [0,1,42]] [[0], [0,1], [0,1]] map(delpaths([[0,"foo"]])) [[{"foo":2, "x":1}], [{"bar":2}]] [[{"x":1}], [{"bar":2}]] ["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) {"bar":false} null {"bar":false, "foo": [null, 20]} {"bar":false} delpaths([[-200]]) [1,2,3] [1,2,3] try delpaths(0) catch . {} "Paths must be specified as an array" del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x) {"foo": [0,1,2,3,4], "bar": [0,1]} null {"foo": [0,1,2,3,4], "bar": [0,1]} {"foo": [1,4], "bar": [1]} {"bar": [1]} del(.[1], .[-6], .[2], .[-3:9]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 3, 5, 6, 9] # # Assignment # .message = "goodbye" {"message": "hello"} {"message": "goodbye"} .foo = .bar {"bar":42} {"foo":42, "bar":42} .foo |= .+1 {"foo": 42} {"foo": 43} .[] += 2, .[] *= 2, .[] -= 2, .[] /= 2, .[] %=2 [1,3,5] [3,5,7] [2,6,10] [-1,1,3] [0.5, 1.5, 2.5] [1,1,1] [.[] % 7] [-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7] [0,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,0] .foo += .foo {"foo":2} {"foo":4} .[0].a |= {"old":., "new":(.+1)} [{"a":1,"b":2}] [{"a":{"old":1, "new":2},"b":2}] def inc(x): x |= .+1; inc(.[].a) [{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}] [{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] # #1358, getpath/1 should work in path expressions .[] | try (getpath(["a",0,"b"]) |= 5) catch . [null,{"b":0},{"a":0},{"a":null},{"a":[0,1]},{"a":{"b":1}},{"a":[{}]},{"a":[{"c":3}]}] {"a":[{"b":5}]} {"b":0,"a":[{"b":5}]} "Cannot index number with number" {"a":[{"b":5}]} "Cannot index number with string \"b\"" "Cannot index object with number" {"a":[{"b":5}]} {"a":[{"c":3,"b":5}]} .[2][3] = 1 [4] [4, null, [null, null, null, 1]] .foo[2].bar = 1 {"foo":[11], "bar":42} {"foo":[11,null,{"bar":1}], "bar":42} try ((map(select(.a == 1))[].b) = 10) catch . [{"a":0},{"a":1}] "Invalid path expression near attempt to iterate through [{\"a\":1}]" try ((map(select(.a == 1))[].a) |= .+1) catch . [{"a":0},{"a":1}] "Invalid path expression near attempt to iterate through [{\"a\":1}]" def x: .[1,2]; x=10 [0,1,2] [0,10,10] try (def x: reverse; x=10) catch . [0,1,2] "Invalid path expression with result [2,1,0]" .[] = 1 [1,null,Infinity,-Infinity,NaN,-NaN] [1,1,1,1,1,1] # # Conditionals # [.[] | if .foo then "yep" else "nope" end] [{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] ["yep","yep","yep","yep","nope","nope","yep","nope"] [.[] | if .baz then "strange" elif .foo then "yep" else "nope" end] [{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] ["yep","yep","yep","yep","nope","nope","yep","nope"] [if 1,null,2 then 3 else 4 end] null [3,4,3] [if empty then 3 else 4 end] null [] [if 1 then 3,4 else 5 end] null [3,4] [if null then 3 else 5,6 end] null [5,6] [.[] | [.foo[] // .bar]] [{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}] [[1,2], [1], [3], [42], [41]] .[] //= .[0] ["hello",true,false,[false],null] ["hello",true,"hello",[false],"hello"] .[] | [.[0] and .[1], .[0] or .[1]] [[true,[]], [false,1], [42,null], [null,false]] [true,true] [false,true] [false,true] [false,false] [.[] | not] [1,0,false,null,true,"hello"] [false,false,true,true,false,false] # Check numeric comparison binops [10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20] {} [true,false,false,false,false,true] [10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20] {} [true,true,false,false,true,true] # And some in/equality tests [ 10 == 10, 10 != 10, 10 != 11, 10 == 11] {} [true,false,true,false] ["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ] {} [true,false,false,true] [[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]] {} [true,false,false,true] [{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}] {} [true,false,true,false] # ugly complicated thing [{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}] {} [true,false] # containment operator [("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))] {} [true, true, false] # Try/catch and general `?` operator [.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .] [0,1,2,3] ["foo","Cannot index number with string \"a\"",3] [.[]|(.a, .a)?] [null,true,{"a":1}] [null,null,1,1] [[.[]|[.a,.a]]?] [null,true,{"a":1}] [] try error("\($__loc__)") catch . null "{\"file\":\"\",\"line\":1}" # string operations [.[]|startswith("foo")] ["fo", "foo", "barfoo", "foobar", "barfoob"] [false, true, false, true, false] [.[]|endswith("foo")] ["fo", "foo", "barfoo", "foobar", "barfoob"] [false, true, true, false, false] [.[] | split(", ")] ["a,b, c, d, e,f",", a,b, c, d, e,f, "] [["a,b","c","d","e,f"],["","a,b","c","d","e,f",""]] split("") "abc" ["a","b","c"] [.[]|ltrimstr("foo")] ["fo", "foo", "barfoo", "foobar", "afoo"] ["fo","","barfoo","bar","afoo"] [.[]|rtrimstr("foo")] ["fo", "foo", "barfoo", "foobar", "foob"] ["fo","","bar","foobar","foob"] [(index(","), rindex(",")), indices(",")] "a,bc,def,ghij,klmno" [1,13,[1,4,8,13]] indices(1) [0,1,1,2,3,4,1,5] [1,2,6] indices([1,2]) [0,1,2,3,1,4,2,5,1,2,6,7] [1,8] indices([1,2]) [1] [] indices(", ") "a,b, cd,e, fgh, ijkl" [3,9,14] [.[]|split(",")] ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] [["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] [.[]|split(", ")] ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] [["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] [.[] * 3] ["a", "ab", "abc"] ["aaa", "ababab", "abcabcabc"] [.[] / ","] ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] [["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] [.[] / ", "] ["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] [["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] map(.[1] as $needle | .[0] | contains($needle)) [[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]] [true, true, true, false, false] map(.[1] as $needle | .[0] | contains($needle)) [[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]] [true, true, false] [({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))] {} [true, true, false] {foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}}) {} true {foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}}) {} false sort [42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null] [null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}] (sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2) [{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}] [{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] [{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] [{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}] [[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]] [[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]] unique [1,2,5,3,5,3,1,3] [1,2,3,5] unique [] [] [min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])] [[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]] [[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]] [min,max,min_by(.),max_by(.)] [] [null,null,null,null] .foo[.baz] {"foo":{"bar":4},"baz":"bar"} 4 .[] | .error = "no, it's OK" [{"error":true}] {"error": "no, it's OK"} [{a:1}] | .[] | .a=999 null {"a": 999} to_entries {"a": 1, "b": 2} [{"key":"a", "value":1}, {"key":"b", "value":2}] from_entries [{"key":"a", "value":1}, {"Key":"b", "Value":2}, {"name":"c", "value":3}, {"Name":"d", "Value":4}] {"a": 1, "b": 2, "c": 3, "d": 4} with_entries(.key |= "KEY_" + .) {"a": 1, "b": 2} {"KEY_a": 1, "KEY_b": 2} map(has("foo")) [{"foo": 42}, {}] [true, false] map(has(2)) [[0,1], ["a","b","c"]] [false, true] keys [42,3,35] [0,1,2] [][.] 1000000000000000000 null map([1,2][0:.]) [-1, 1, 2, 3, 1000000000000000000] [[1], [1], [1,2], [1,2], [1,2]] # Test recursive object merge {"k": {"a": 1, "b": 2}} * . {"k": {"a": 0,"c": 3}} {"k": {"a": 0, "b": 2, "c": 3}} {"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * . {"k": {"a": 0,"c": 3}, "hello": 1} {"k": {"a": 0, "b": 2, "c": 3}, "hello": 1} {"k": {"a": 1, "b": 2}, "hello": 1} * . {"k": {"a": 0,"c": 3}, "hello": {"x": 1}} {"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}} {"a": {"b": 1}, "c": {"d": 2}, "e": 5} * . {"a": {"b": 2}, "c": {"d": 3, "f": 9}} {"a": {"b": 2}, "c": {"d": 3, "f": 9}, "e": 5} [.[]|arrays] [1,2,"foo",[],[3,[]],{},true,false,null] [[],[3,[]]] [.[]|objects] [1,2,"foo",[],[3,[]],{},true,false,null] [{}] [.[]|iterables] [1,2,"foo",[],[3,[]],{},true,false,null] [[],[3,[]],{}] [.[]|scalars] [1,2,"foo",[],[3,[]],{},true,false,null] [1,2,"foo",true,false,null] [.[]|values] [1,2,"foo",[],[3,[]],{},true,false,null] [1,2,"foo",[],[3,[]],{},true,false] [.[]|booleans] [1,2,"foo",[],[3,[]],{},true,false,null] [true,false] [.[]|nulls] [1,2,"foo",[],[3,[]],{},true,false,null] [null] flatten [0, [1], [[2]], [[[3]]]] [0, 1, 2, 3] flatten(0) [0, [1], [[2]], [[[3]]]] [0, [1], [[2]], [[[3]]]] flatten(2) [0, [1], [[2]], [[[3]]]] [0, 1, 2, [3]] flatten(2) [0, [1, [2]], [1, [[3], 2]]] [0, 1, 2, 1, [3], 2] try flatten(-1) catch . [0, [1], [[2]], [[[3]]]] "flatten depth must not be negative" transpose [[1], [2,3]] [[1,2],[null,3]] ascii_upcase "useful but not for é" "USEFUL BUT NOT FOR é" bsearch(4) [1,2,3] -4 # strptime tests are in optional.test strftime("%Y-%m-%dT%H:%M:%SZ") [2015,2,5,23,51,47,4,63] "2015-03-05T23:51:47Z" strftime("%A, %B %d, %Y") 1435677542.822351 "Tuesday, June 30, 2015" gmtime 1425599507 [2015,2,5,23,51,47,4,63] # module system import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a] null ["a","b","c","a"] import "c" as foo; [foo::a, foo::c] null [0,"acmehbah"] include "c"; [a, c] null [0,"acmehbah"] %%FAIL module (.+1); 0 jq: error: Module metadata must be constant at , line 1: %%FAIL include "a" (.+1); 0 jq: error: Module metadata must be constant at , line 1: %%FAIL include "a" []; 0 jq: error: Module metadata must be an object at , line 1: %%FAIL include "\ "; 0 jq: error: Invalid escape at line 1, column 4 (while parsing '"\ "') at , line 1: %%FAIL include "\(a)"; 0 jq: error: Import path must be constant at , line 1: modulemeta "c" {"whatever":null,"deps":[{"as":"foo","is_data":false,"relpath":"a"},{"search":"./","as":"d","is_data":false,"relpath":"d"},{"search":"./","as":"d2","is_data":false,"relpath":"d"},{"search":"./../lib/jq","as":"e","is_data":false,"relpath":"e"},{"search":"./../lib/jq","as":"f","is_data":false,"relpath":"f"},{"as":"d","is_data":true,"relpath":"data"}]} %%FAIL IGNORE MSG import "syntaxerror" as e; . jq: error: syntax error, unexpected ';', expecting $end (Unix shell quoting issues?) at /home/nico/ws/jq/tests/modules/syntaxerror/syntaxerror.jq, line 1: %%FAIL IGNORE MSG %::wat jq: error: syntax error, unexpected '%', expecting $end (Unix shell quoting issues?) at , line 1: import "test_bind_order" as check; check::check null true try -. catch . "very-long-string" "string (\"very-long-...) cannot be negated" join(",") ["1",2,true,false,3.4] "1,2,true,false,3.4" .[] | join(",") [[], [null], [null,null], [null,null,null]] "" "" "," ",," .[] | join(",") [["a",null], [null,"a"]] "a," ",a" try join(",") catch . ["1","2",{"a":{"b":{"c":33}}}] "string (\"1,2,\") and object ({\"a\":{\"b\":{...) cannot be added" try join(",") catch . ["1","2",[3,4,5]] "string (\"1,2,\") and array ([3,4,5]) cannot be added" {if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,include:15,module:16} null {"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"include":15,"module":16} try (1/.) catch . 0 "number (1) and number (0) cannot be divided because the divisor is zero" try (1%.) catch . 0 "number (1) and number (0) cannot be divided (remainder) because the divisor is zero" %%FAIL 1/0 jq: error: Division by zero? at , line 1: # Basic numbers tests: integers, powers of two [range(-52;52;1)] as $powers | [$powers[]|pow(2;.)|log2] == $powers null true [range(-99/2;99/2;1)] as $orig | [$orig[]|pow(2;.)|log2] as $back | ($orig|keys)[]|. as $k | (($orig|.[$k])-($back|.[$k]))|if . < 0 then . * -1 else . end|select(.>.00005) null %%FAIL IGNORE MSG } jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at , line 1: (.[{}] = 0)? null INDEX(range(5)|[., "foo\(.)"]; .[0]) null {"0":[0,"foo0"],"1":[1,"foo1"],"2":[2,"foo2"],"3":[3,"foo3"],"4":[4,"foo4"]} JOIN({"0":[0,"abc"],"1":[1,"bcd"],"2":[2,"def"],"3":[3,"efg"],"4":[4,"fgh"]}; .[0]|tostring) [[5,"foo"],[3,"bar"],[1,"foobar"]] [[[5,"foo"],null],[[3,"bar"],[3,"efg"]],[[1,"foobar"],[1,"bcd"]]] range(5;10)|IN(range(10)) null true true true true true range(10;12)|IN(range(10)) null false false IN(range(10;20); range(10)) null false IN(range(5;20); range(10)) null true # Regression test for #1347 (.a as $x | .b) = "b" {"a":null,"b":null} {"a":null,"b":"b"} # Regression test for #1368 (.. | select(type == "object" and has("b") and (.b | type) == "array")|.b) |= .[0] {"a": {"b": [1, {"b": 3}]}} {"a": {"b": 1}} isempty(empty) null true isempty(range(3)) null false isempty(1,error("foo")) null false jq-jq-1.6/Dockerfile0000600000175000017500000000300313366726451013703 0ustar czchenczchenFROM debian:8 COPY . /app # get dependencies, build, and remove anything we don't need for running jq. # valgrind seems to have trouble with pthreads TLS so it's off. RUN apt-get update && \ apt-get install -y \ build-essential \ autoconf \ libtool \ git \ bison \ flex \ ruby \ wget \ ruby-dev && \ wget https://github.com/kkos/oniguruma/archive/v5.9.6.tar.gz -O onig-5.9.6.tar.gz && \ sha512sum onig-5.9.6.tar.gz | grep 6b048d345e148c9da38af7a8df76d4a358eb3d4db91fa7834076e511f526a63544284f212b0d56badbbd33112c8b458a5fff02bfbbda012ecfe478bc436ea679 && \ tar zxvf onig-5.9.6.tar.gz && \ (cd oniguruma-5.9.6 && \ touch NEWS ChangeLog && \ autoreconf -i && \ ./configure --prefix=/usr/local && \ make && \ make install ) && \ gem install bundler && \ (cd /app/docs && bundle install) && \ (cd /app && \ autoreconf -i && \ ./configure --disable-valgrind --enable-all-static --prefix=/usr/local && \ make -j8 && \ make check && \ make install && \ make distclean ) && \ (cd oniguruma-5.9.6 && \ make uninstall ) && \ apt-get purge -y \ build-essential \ autoconf \ libtool \ bison \ git \ flex \ ruby \ ruby-dev && \ apt-get autoremove -y && \ rm -rf oniguruma-5.9.6 && \ rm -rf /var/lib/apt/lists/* /var/lib/gems ENTRYPOINT ["/usr/local/bin/jq"] CMD [] jq-jq-1.6/modules/0000700000175000017500000000000013366726451013363 5ustar czchenczchenjq-jq-1.6/modules/oniguruma/0000700000175000017500000000000013366726451015371 5ustar czchenczchenjq-jq-1.6/m4/0000700000175000017500000000000013366726451012233 5ustar czchenczchenjq-jq-1.6/m4/ax_prog_bison_version.m40000600000175000017500000000373513366726451017105 0ustar czchenczchen# =========================================================================== # Modified from # http://www.gnu.org/software/autoconf-archive/ax_prog_perl_version.html # =========================================================================== # # SYNOPSIS # # AX_PROG_BISON_VERSION([VERSION],[ACTION-IF-TRUE],[ACTION-IF-FALSE]) # # DESCRIPTION # # Makes sure that bison supports the version indicated. If true the shell # commands in ACTION-IF-TRUE are executed. If not the shell commands in # ACTION-IF-FALSE are run. Note if $PERL is not set the macro will fail. # # Example: # # AC_PROG_YACC # AX_PROG_BISON_VERSION([3.0.0],[ ... ],[ ... ]) # # This will check to make sure that the bison you have supports at least # version 3.0.0. # # NOTE: This macro uses the $YACC variable to perform the check. # AX_WITH_YACC can be used to set that variable prior to running this # macro. The $BISON_VERSION variable will be set with the detected # version. # # LICENSE # # Copyright (c) 2009 Francesco Salvestrini # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 12 AC_DEFUN([AX_PROG_BISON_VERSION],[ AC_REQUIRE([AC_PROG_SED]) AC_REQUIRE([AC_PROG_GREP]) AS_IF([test -n "$YACC"],[ ax_bison_version="$1" AC_MSG_CHECKING([for bison version]) changequote(<<,>>) bison_version=`$YACC --version 2>&1 \ | $GREP bison \ | $SED -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'` changequote([,]) AC_MSG_RESULT($bison_version) AC_SUBST([BISON_VERSION],[$bison_version]) AX_COMPARE_VERSION([$ax_bison_version],[le],[$bison_version],[ : $2 ],[ : $3 ]) ],[ AC_MSG_WARN([could not find bison]) $3 ]) ]) jq-jq-1.6/m4/ax_compare_version.m40000600000175000017500000001465213366726451016372 0ustar czchenczchen# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_compare_version.html # =========================================================================== # # SYNOPSIS # # AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # # DESCRIPTION # # This macro compares two version strings. Due to the various number of # minor-version numbers that can exist, and the fact that string # comparisons are not compatible with numeric comparisons, this is not # necessarily trivial to do in a autoconf script. This macro makes doing # these comparisons easy. # # The six basic comparisons are available, as well as checking equality # limited to a certain number of minor-version levels. # # The operator OP determines what type of comparison to do, and can be one # of: # # eq - equal (test A == B) # ne - not equal (test A != B) # le - less than or equal (test A <= B) # ge - greater than or equal (test A >= B) # lt - less than (test A < B) # gt - greater than (test A > B) # # Additionally, the eq and ne operator can have a number after it to limit # the test to that number of minor versions. # # eq0 - equal up to the length of the shorter version # ne0 - not equal up to the length of the shorter version # eqN - equal up to N sub-version levels # neN - not equal up to N sub-version levels # # When the condition is true, shell commands ACTION-IF-TRUE are run, # otherwise shell commands ACTION-IF-FALSE are run. The environment # variable 'ax_compare_version' is always set to either 'true' or 'false' # as well. # # Examples: # # AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) # AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) # # would both be true. # # AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) # AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) # # would both be false. # # AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) # # would be true because it is only comparing two minor versions. # # AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) # # would be true because it is only comparing the lesser number of minor # versions of the two values. # # Note: The characters that separate the version numbers do not matter. An # empty string is the same as version 0. OP is evaluated by autoconf, not # configure, so must be a string, not a variable. # # The author would like to acknowledge Guido Draheim whose advice about # the m4_case and m4_ifvaln functions make this macro only include the # portions necessary to perform the specific comparison specified by the # OP argument in the final configure script. # # LICENSE # # Copyright (c) 2008 Tim Toolan # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 dnl ######################################################################### AC_DEFUN([AX_COMPARE_VERSION], [ AC_REQUIRE([AC_PROG_AWK]) # Used to indicate true or false condition ax_compare_version=false # Convert the two version strings to be compared into a format that # allows a simple string comparison. The end result is that a version # string of the form 1.12.5-r617 will be converted to the form # 0001001200050617. In other words, each number is zero padded to four # digits, and non digits are removed. AS_VAR_PUSHDEF([A],[ax_compare_version_A]) A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/[[^0-9]]//g'` AS_VAR_PUSHDEF([B],[ax_compare_version_B]) B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/[[^0-9]]//g'` dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary dnl # then the first line is used to determine if the condition is true. dnl # The sed right after the echo is to remove any indented white space. m4_case(m4_tolower($2), [lt],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` ], [gt],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` ], [le],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` ], [ge],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` ],[ dnl Split the operator from the subversion count if present. m4_bmatch(m4_substr($2,2), [0],[ # A count of zero means use the length of the shorter version. # Determine the number of characters in A and B. ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` # Set A to no more than B's length and B to no more than A's length. A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` ], [[0-9]+],[ # A count greater than zero means use only that many subversions A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` ], [.+],[ AC_WARNING( [illegal OP numeric parameter: $2]) ],[]) # Pad zeros at end of numbers to make same length. ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" B="$B`echo $A | sed 's/./0/g'`" A="$ax_compare_version_tmp_A" # Check for equality or inequality as necessary. m4_case(m4_tolower(m4_substr($2,0,2)), [eq],[ test "x$A" = "x$B" && ax_compare_version=true ], [ne],[ test "x$A" != "x$B" && ax_compare_version=true ],[ AC_WARNING([illegal OP parameter: $2]) ]) ]) AS_VAR_POPDEF([A])dnl AS_VAR_POPDEF([B])dnl dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. if test "$ax_compare_version" = "true" ; then m4_ifvaln([$4],[$4],[:])dnl m4_ifvaln([$5],[else $5])dnl fi ]) dnl AX_COMPARE_VERSION jq-jq-1.6/sig/0000700000175000017500000000000013366726451012475 5ustar czchenczchenjq-jq-1.6/sig/v1.3/0000700000175000017500000000000013366726451013164 5ustar czchenczchenjq-jq-1.6/sig/v1.3/sha256sum.txt0000600000175000017500000000073413366726451015470 0ustar czchenczchencf4b0b3e505958843375ac153d84ad6ee40196aa76ee373b0409f858b3cbf925 jq-linux-x86 dbacac81ebc00a7387e4c5e539a7a475981a1ac6ada20a2f6b1d8f950027751e jq-linux-x86_64 4095a48b50b754c8b3199b9c069fa9c3da0e737d8809af3205d1bf69f87cab6e jq-osx-x86 bce52800c943ff10d1ffd9c1a7e7aeddbc7bcecd7a2b05c6afb828399dbfa39f jq-osx-x86_64 c8e2ffb5b0536a50430ef9b4708a40686e4352db5c01ae90a98ba96660e5bc36 jq-win32.exe d041fdfd8b3aa3832eedf2aafad5002f2b47fb59373b71190a01422de825911f jq-win64.exe jq-jq-1.6/sig/v1.3/jq-osx-x86_64.asc0000600000175000017500000000146313366726451016037 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGziCAAoJEK8ZBAxxUjQCkGcQAMpxoK6aHzDIeVjp5kQ4MOFu dOE0xjXlPpudUfUORKqfsnPvvxJTvqlDrUhMq6d6S1Eic9ZAbfpmHGbw02tvQ7+4 hvyy/KbfBUcLZRIwZ1eej/rFUXd2fmxPwoj07SpTOgjKF+8duUzSlxBg+Kes77pj ZuMO3KUa3isaOp+kufh0+miAqHisEWzQWCfHBrVIwW63L2bf2ZSUvp4JqwfRKfr2 3hBfoll0M+iaLHr4/wGUIxRL+23afTtvQikyOXzB3rKXf+QmDePZ29isZVg94yi4 /XiDVmDM/6GpfI7u0fIoDcEgH+LE35HL/CT4zA8IzA/QwB5bCO1bPEA7DfDQq01R oghTvH5Qu0eu8tjap7EXAmch25nPys4NzPoIRnbu4i4wQUsFqGzmSFKEuU6pwDIm lsEBPbHRcGcaui2KidUNi7iQmje819KgRDhRxX65Bye9sKsD/HvMzQRce1RFYQg3 uqISuhWu8qj67J/ii2zFF+lo5HgHLqzXKcye1kY5KYq2dTIa3khq89Xkf3trsnNL IKZ+8P2eiVDjDr3UPIvVCX1JvNTdcQDMUlmoKk8g1qwuTp2PTi+c6FKicZGUMMnv o/hThK2JwnL/Iv0bJHvBU6aCXSnKLTLIdT02UvUppiN/GTMHsagvE0DRA1B1yFim /o/n4edysfzI1Pxe0b3c =ZOqW -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.3/jq-win64.exe.asc0000600000175000017500000000146313366726451016021 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGziKAAoJEK8ZBAxxUjQCP14P/jkKiLUgiZkXYVQs+hBJqJD2 rOsHVGmoxpjnQ8L9gUSxfZ0E80+1iTx/9JNWHs0xEKrvWKmCovfuQd8xfxZ30g5z wMa/7QWZtLdfIGkQje79WgA/+xDWSLa0T+dbcMjCwDdYPQyQE5Vf0I7qoNOj2Dcr CmkSMO6dtoilv/o2WX1x8Z1K5sqGMl64WyM5yR8sEMRBVuVqWRvELrv9+YMW2UYU yXj7tFwulyqZEoUBLlUxZhr7gjah31gBP2qqLeZhq/phdUsi9ckPusW3Un5SMaRf xEPo+y83Qrpusm9UDGsm3t90TWva3oMiExjFatFKuKvLbFCZRK0EIqdUOUMlptV6 kS3xKsvAq+vM53FfwcWx/9gcZQv5XTw7McmWZ9vZmHH24AUjuFFFZLoWxPg73Vek HaD5xTsxfYEqGRaJbYoSkSs39fw+tH0ajRoMVcZw2O2c94qAgzR/Qne9t2nQ0ypg hOzsIIdIxj9XZShkkVmlK8naf0YH0Wc3Yo3wBxkTy7QKVWtcF+x67wth2XjKHBbh lh9Gc3R/CUvRhk9gOSqHYAsy7lEIpFGjuIqYTZcXRO3BCJgjc3hV9jBiIy1iQWGg Kuh+egThw8U9z3PueP++yVjImze9Y/+78dc/rHAwXkFIysBs6vle/9ILDl5Komvv 91rFNBLkjvmJoRw4hUpG =WNFH -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.3/jq-osx-x86.asc0000600000175000017500000000146313366726451015526 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzh+AAoJEK8ZBAxxUjQCyb4QALrsqZ/QkI2a/uVPnwAh22B4 mtbplxOS8swCB4g56tG+JMuria9l1ACmpbc9eTJ/Q9z9LLwq3vBkpy0utAQp/iAQ ijZbTG7E3S/LtOzZ5MSbkGeyuDWu4YCI7DuxTZH+R43fiA/ShHUHf7rO/vofAjTu MZr7TpBRjut5htbkNrUKoYjK97hehvY0S3h1oqMSSuHS4Em9xMvv5bAsEn37+L3g o5Y5kHQ55Lu3qH1Uk87VMz+gtO4pxN+ZrNNrY2lX/hwnV7p5YDza2H0uOopBrTi4 X99x9JzdHoSrySRwS1ZDc+XYDgXP7JO9qDmNh18nXJlyCWFzDHajQS+4Hn0O5zM0 PHWasVC8tmxT0XXZlG1kPuaCkg0BFh1NXNjCUa7Ru9XHfOkaW4TyKGQa3eboyx56 q7MDvODUd3JSW4xmLJ7NFSYRCPMRiCMorQ8u8afy2OWt2W13M8OIH378dMcFoubW GB00i127dhuI95lwNsTV1seKO59bMbAJjP7ITqqDiOafg6AmTric6BJNakSsrc66 yCzhOfxqwfeRekPSirrf8nv9D7dW45yLEiXn9Irn+KghdL4KCLCB8dyc6OalITVF c01SK5xNzPRfFVzTqMdmi/dYpDL0KSMwg3O3DhPQ0LrRZDXKXj6A4KGAthpVLmRr IHj2hlGlr4hqLXUU1wE5 =I3PU -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.3/jq-linux-x86_64.asc0000600000175000017500000000146313366726451016365 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzhxAAoJEK8ZBAxxUjQCb2EP/08Lgfoc0rDv5IirCjXIFWxM 8C56aVfaF/8gaG75SuN3QUVBuWx3VB60Cgh/msTFrZ/jkWr+tYArNzwNAjI8a9T8 nE40SJH3kvwVrr9ALSgk9fEvikfZVKwYTc0k4h1hBataE5Z+7Di7VYPUmdLTd/ih dcJfDe6zPcYmaW6fl0glP2GLaDuLI3vxAid0MzDS1RU52z+usc/fjXWmaJwPVp76 Sd8/Uz3X6ZiFwpkfhp6XTcqkQhMZd4GS9cjnPAdRp9HFLI7S2UJ4RNj/YQIHrhQL 5Ko3KdXEWeSvD8wcr8VcZ66NfmyCls0diEU+TUv1I7VmLzVQGx9/4/uVWpfxry9D 8MLEHEJTTDT5W3hX47g29JRFJzv8PUyugkpXjQrpSoFbe/3vRxZeQNO9rjTx9t3x hdkWIjbwTg8SGH0XK+UNfR50A3VlrlPDpW5Ju2Io7/OuKRWN4i6ktFx0akFMmHUV D5u/FhEux7kJCNHnF7a7YJ1Ys4OSPUzoH3z35zhyVcW+iiX7KIKHNamWaPBw3iYa MwYVKHT6NUQiVQbTZvWJZXGArEnwq/z6jAjRQNKoTjbcVoYJunY/DZcJjnRIux9T yYnTa7IbpWZmWZWQfnb8zuSoXnhaSHZItIeUklH4K03xjpEXW7pGPLPznyDoBQeC 1WVcwpviS/Y24eSmmxaA =MlHu -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.3/jq-linux-x86.asc0000600000175000017500000000146313366726451016054 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzhJAAoJEK8ZBAxxUjQC8xMQAMgSl+2aYa/c/LuYKDWtVnHO G16rAq+oruc/IrEwEKqcwqTvK8M7xlAxUt5aPtAY0kRkvxadFtX8vZy1SEqAFcC5 sw7J+Oswl+5dJF0Y3j3I0hA6JuOe/+RP4r64pGlwQ0mVIJuTWqinzO9JqTOskeXy 5vSN50A0vFXzDu661UFr1v8q5+b8WhIrJhuOZtaPap33Gu9bS6fz93VBm4LE3a04 KjDydbNUoyA5HX4QVTgFON8oeVJevtqV3krnsU/ncOeTB0ThT9yywcq8HCbh7dqo EhfyATOmk3RZf3RsSAOG5CZ9cFjof6/SqVG8Cbp14MRVMkKi37mvHkqFER/WRtsv Ul1OzFfv8Jwv+cItTSB5c80kbbRfrqwf6NgZW9oTxZP3G3I5JjCTYhCaWlzO0wZ1 QAAJDBAv5GNB4rn9LXGdj2DhcCh00lJY83Bpup4LRc2RuJuJH7IUIkfRW4CAVoRd p6w5W4jYNdrrTkhfkNxSqi7FtKN23+eD2r0knPvhsWxyUcXrGhFqaImHmzQWIsX5 AviEK+nQUcuhTKolsN/97NPkRYUT9C4IVNdI8m1+EsAuwGodSvRrUwBIOoVQlYA7 MFLZZ23Su/8T5NnruP+iXmy0Y3Nf9W+GLHNn3H+9027zAs5Esd2Hy4R6W65BwWuI yYHFx8O6bi5ke72ByCUI =ru7X -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.3/jq-win32.exe.asc0000600000175000017500000000146313366726451016014 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGziHAAoJEK8ZBAxxUjQCG6cP/RNQd9g+Te+fgpFvnFDhAVyW EKZ+2B+V64m4GzrRFnGwBMqju4kFf0dZ4craagJ2gLVQ74FhvorMtphjX0MYgxtt wXyIqImlBake3GEkjUI6XW8QmlFmPix7+2fG4oEdLHwgE2/ii1DFlhTQgYYwH+Be z6wo6Nfl9OxykEHG1hl0A4AiHT1ubWogbjD87yZRO2M1rp2nHJu1TujqkjwWLXtr 0xVBSgtutfYRBvpPGhtLb4nqzkeW+lFW99A2aUo1KroyBdcrEKTJ4XMRqjYJLkSN 68e4gZqHmR6M1nA4h81bU26lhrN3LbYiOwBu67N5PnSOQE87zFPK6Dac4QyG7vzt pR2xht0QwwraxOeIrMrKs9vhUOKm9RDpNcD3z1xa+k6Qej+nP8A2zmMvUz3+C8F9 ITESyjk/28Eku5jEUs5Niz/znGvuU3A2fk/qHs7U0I8tvNWsxj29GCTzgPDoH3Vs sJKuSozoSqecBf+2x23PnGjGttL6BK6lo762qeVw6npCi/nqGFbMWpuopgJ2PiNP t2RdNzvX7v1V7ZfBOGl5iAeG/XJ18uD+hplCiUePN7Jv/3bpaJ9jlo//kIwddUP2 p89zfLPjeptAIxvkWph4ZL59pj7zsid7rSMd0WIOOqE61PKpl5jBgrdiBgKbcmJe AIRZewlOq/0l0cVtK10b =3nBK -----END PGP SIGNATURE----- jq-jq-1.6/sig/jq-release.key0000600000175000017500000000456613366726451015254 0ustar czchenczchen-----BEGIN PGP PUBLIC KEY BLOCK----- Comment: jq Release Signing Key 0xAF19040C71523402 mQINBFYayakBEADL6biudAZ3V1//e3gdWMN6lbz+m5o6avu1vnbtCul/MxOF2Ygd SeXCn5gV3+F+xdrA3/Mhv9cWXzzDTp0D2U75mHw1n4OcArFvKm/RV5yOlc/wvblp WfWd/hCIIZhdHpRWdCko2ByXx9XlBRn46nGyxCFScLcHbicPjq2kIpZ7oad29gs4 bThj65G5XS48FyqixGHy0o2S0nQXNIyB1XTPm4mwHNAeOMFaYj6nhb6b5vu+RhlK syirTGU7pZYd13FM5y9NZrbxfKnGz5bdKPgb4jRi3YmBTKRzlW4IZl3SGO3kPi9a wJR4vP+cf1eEAWulrt9i4DA3/3/lpPpFh5bSo7nKW49PdOjfLWzBLtCsVhJReF97 sSPVpNmf6RrfBXnPlEBw+aU4wGRbmpA9AGV2rKsvxZbU2AqDS93Pn6S0u4XNNnjH TGxe2P9gSEH+xNuEyFcDbt0XP6Te3vSE7oMGIPhXq1FKuXygEQGV2fXa2Dovxi+q YTvbtzG5PV5lQqeexoNMUHzgHVWib/NIt7JTkBflJt33re7oayZd5DSE5zy/4n5d LH9prYJWqVGgb92Lzlk6uYDp3ztBlFdoRR+6L25iUCPRq5yS7J4Rk3ahask2CsQc aANz5xkESnS5DcakDoVtbiC1UY8eM+vXui/UF2uYCTLrz+SIIZJHPQ0fdQARAQAB tC5qcSBSZWxlYXNlIFNpZ25pbmcgS2V5IDxqcUBzdGVkb2xhbi5naXRodWIuaW8+ iQI4BBMBAgAiBQJWGsmpAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCv GQQMcVI0AoHwD/4s1L10OiimSELwTZ3x+btmKdaFBdlUhjBZd/GDE3P1pV+63D2+ LhLpZLQ3yOrplUDHGBG02Hx7/lY2YE5rRVNMyR8FqD9aMrOwQE3uERjkwFQ7Q69r S6kFLiOxUWDXNQ0wXeZ/Na6uYi6AI/oCYWnDIO3lugDUoXtHanYqK1BvJsBEvWpe UB18VcqxBQuOMPD+xNzVDSFn1uYiKKBkv4XJNLHRqdLpwmAVcLDkKTHWEy+223CZ 4zwahQKw9wPdUlakXP7v+wIxzGXaGk0yJOsYVPykQ4AkoMgvqqxyx23d6hjncR1o 36muLAiL+qELZ/rxUytduaK1Owr8cNJgPzRAEbeSRdw2hZYyEnHgNPC3atUN0wIk khzP/GHMBYIU90CdiWjGvjJeHv047Ty4S9PEYPZLQx1s8cdIeozEFt/lTqFp+9Si f2DWUKHpzQ3wfUudTD4pzSy28XJ760Kq54f1cf2CWqp577xrxRfcKKoyufF8pwqf sAno03N6MpxiK0qVwZ/0Qb2BfAI2rpxxc98qhuErlBE8WjPj7M93eaHNMTtCqCIG Hke6kMjYuwGu8NK/O0SqGqsHoXxxIvlZNda9MCPJo9xl77dphurOq/D/d9rfy/Uk 5D8ssNUBhM3s6sQl27L1avnSS12UuRdGSQwU33XbAuNG7y0a/zRnX+GqE4kCHAQQ AQIABgUCVhrKpwAKCRD5uhQ7lf9tgo26D/0WFFkhmUjSV9K/q8zSSJhuQfzoeM+n Z2DfYBfKdawbJH9/Yfg1nIFzih2lnvzk/kx8WbLSDMb0sHn6ZUZ6cVoBvrBxSgah bwPF9M8Xf4J/oBYHq8ZYjPHOWnHbEPuoNzPreqOhtSLBtP5dfZlEI6DLR2+WKj22 EmHfmBFoKZ90N2MQWQLV98DAFfsxS7Bqc9xSCbh405qCIGr/4r4P0BaIdJ2jW/9k AHwnsmJnf5lI7dn8BEMYgM3vcG4dyaWNf+eoI3cJnq+Y782KoyFjof4t1bLF2H1C vbiJ9xWv4uCeTVlTwiVJFkSUxBgmsyVofcboHF7/06HtVXDpG/ihZEOoqNPKPD4d 56V2t0hEulUpyqfs0Ic8r+sGWgvDCSqAVrXak2n0ULJ+RkpRB6ekcgOY7HdYKX1H UifaytQSxjUBXfo9HAC0Br2fyGh9s5zBcnrIrWHxwQw/RCIIv9CkZB7s6/NFYBKw Q27xTpXjHnGoEHNAA2xSyMXw9VoH3qoze/1iraK0zSFGAXpmL57HMBnOrLj7oq+O tB/cLd9o4+XFJjXHGAsXoOD5cCADkgMSMcouvPFKySfK0Kz5ScWVSsc5FvG44j/L T1WvjBgIrNAeQJ2efPYVmg5f/ALkjgnKy+lkafBk4OwXtWZCTf+9vcOF7FQ2B6N+ 7qQPymxSJxBSEg== =AiyG -----END PGP PUBLIC KEY BLOCK----- jq-jq-1.6/sig/v1.6/0000700000175000017500000000000013366726451013167 5ustar czchenczchenjq-jq-1.6/sig/v1.6/jq-linux64.asc0000600000175000017500000000150113366726451015577 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqF8ACgkQrxkEDHFS NAK+UhAAx+mFwf7XRYLznTU4HoDzW3yn/H+amvYpphvtcn/eB0GsZRpZ3+EIkMUe VF9Qxidw39tZeLROdX84iaYSu1bTpd6vAdx54rMUZyPVry6m5hqUD78LccPvIDCk EyFuRuAAqlRicyNuqtlr9BUBoI7Z2c4YOA3Y4oXlOXtleL3SXKXcGvhcWQ4owZp5 MN6tT/lNu6sj8l5v0Y4GxtfDg8YOap2s9MdaNNkAgt3O3J98gadAin+vQ34ekKWr zsB7QjZaAr8OSGyWCY7BTc7UJbsTIzFzSPvYGeIP2iCBSpR+qcM5/bgrE+Q4l2TA t+p6vF1UVo9MkhEhD/2Ru/ovjqRKIVKA0J5YvEzr2jWjSAWpc75y80rovqW/vjE4 GHBJootozgrUwe/ESTATzdyfFQG8Du8znnQp1Wv3upRPieCKiLiUkRZenOTvBIvW rHhgBR73vfhyD92exum4Z97hglwzjWJZRYohyKz+D0tYffH5lG4DV2pToREMqslq 9Yr3eGREMD/6j/zIsfzKxFeC/Ar+vmZrate9bUPwX7WYdwwbRcmx7zI6hIszFDnc RONpuJokLxcgfVC3Q8Tzo5cRsAJ0eiQwO/K3oU/ZSAjJkV17E+cy3KFm/C0BSvXx BHxK2D3vqCsbtlfkLurxzjvFKIAQtFO3yfSI5mIMnWfRCgvoHcU= =O93f -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.6/jq-linux32.asc0000600000175000017500000000150113366726451015572 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqFAACgkQrxkEDHFS NAIQNRAArTVuw4JwSbxKzC4Y+ib1Vaz5d8ntQwmiR1FBJ5hbi5dMhqD81Kl4slU0 0IDBia7Bnvthk0/H+UZHjUdkCNW8rubg87zJOt8t5XXQmIK81cPgKhxqhPA6JPA8 RNmBGs5gAxycm8x4X0EMOVTsVqAfq8NYakO6MctRlv4ut3lXfWe+prIMBpFQZr7l CqvmBY8xhLF09LLBEB+RTjhDeW7E02cYFJIW9bbcvpECmnX/fJTjGwN6+Blaq4ln nJwODDZ61TGC2ZAYcAoqcA8bkU8ms0x7RCpZrRbYG4HJstSYM8RaxUxvtW2zn6Si 7FpMvrXnDn6MWr3wph96oRTEkKQsRhxoxo7zumaiKkaAGKjyN7fFx9z89QrKoxrO CAzGdHjt6YSmXH60ArGUWKGRilyvxieKddXD3fMJ5Sz/u+yHFRNDIuOQt+KZaOfC UlvKXQamexkXmK3PEl5ZPxcuK+G/NLFawPlLLHVvdLx6B5o4r94ycfLLYqzDCiby v0EDZrgVropUMUkJJMjSmioXoizK9KZ/fl5fj+2miHEZS+s+YwZ5aoPi5IvrFUhi sYz3wEKHp3aYzTPhPt3wBU9aaWoTradSCP4pt4eJV/VeVND+vygILdoD6xFWJoc4 BT2tBrUR6uqdQXaCn3BF0lCfI17fab1elYwBkh291wZeKF7qQ8Y= =cTl4 -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.6/jq-osx-amd64.asc0000600000175000017500000000150113366726451016010 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGUACgkQrxkEDHFS NAJT0hAAvuHiFUoXoHh6fJ16dY+ZvY4lWqAUap+mAWABuMpWGT0nfjq30eqvCY0e Vjk46ENJPNjCNQnxMRq3kWMqsaniWLgU/sbXK1Ws7RldhJbxjnL8y/jHtNHQtrKV +RhcJ3FhDzLSHOnIsTYYzHFZ1D+qeU+XMafVnnZSCj4SjEMZjzo9VyHJqyu+iG0w z2spLd6zFp1+6EtZkdv10NsiB69HhNxKUn0BUE2KOfasLpFZSoMN2OXNx9qatTJ0 l/+PKzSb8DkRh3uHtowyV7VHrPUQ8/WcyRqwNKMmJaB3WDDwsPJxgQT3sJVpVQaj 4d768B23NbR3LZbqPgczu50kn/6Q/DJTm7J/TNxKIOfnCsIMsiIhQ+oLe7/8c0ph xXhNZqzi06jB0kulmGus/YQY+Nojquzc+mMOysoxYo1iWscxWoK3GrImO3YMACk5 0zH6pHgLBlWZ/5ScKmvAn8tI/EfSUnsR/QMmFEZsc/80tkZBAJd3HPeamRtLqCon T8aSP+xwwzo1BFhtdVddYXlXFWIwtn9VhEYJvXN12eyhW+S+dKyUlJCdCAozkntD QxOILIc8RF4F3KvQTo+t91hbjyPGm760/iQgHU20UpRxaIkJylQ6p6rWsV1wEMMr QEENmAs0IyawgJGJ1Jr7AKOR1fqEqW2BJxQ9zi9re+dXOvVWMI0= =J3nx -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.6/sha256sum.txt0000600000175000017500000000060713366726451015472 0ustar czchenczchen319af6123aaccb174f768a1a89fb586d471e891ba217fe518f81ef05af51edd9 jq-linux32 af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 jq-linux64 5c0a0a3ea600f302ee458b30317425dd9632d1ad8882259fcaf4e9b868b2b1ef jq-osx-amd64 0012cb4c0eb6eaf97b842e676e423a69a8fea95055d93830551b4a5a54494bd8 jq-win32.exe a51d36968dcbdeabb3142c6f5cf9b401a65dc3a095f3144bd0c118d5bb192753 jq-win64.exe jq-jq-1.6/sig/v1.6/jq-win64.exe.asc0000600000175000017500000000150113366726451016015 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGsACgkQrxkEDHFS NAK2Sg/+O0nHwVg185EwKoORwL7TQ9V8VSV04Ska0lKJ0Biu0w0U8k+bkzhY5IZ5 IdNUZb5lhnPpLosCLlzHlWD3fNkBbpYLgxDrGDS7EujXcp8lI1WrwVNuY2Gbz7c3 09YAeF2ENnutS4KP0BUfdEVMlEqQIVpGldl5jKgHxlZ8nP80z6IpNL9FnX/qwkZu /fyRthz1XAEcDHfzBtSbJCJ58QoGwl4S96TsuLZnnBuNgNk97SIeiDzg7tN1FmF2 wyg7mPZhIdRaGfgRLCqO7+qFGiYS4esJj4SbLiWDREVKhqqnT5B/vF5SmshsssUm 6MH+eK0BAGIntTs0fIGtgkiiuZI4bJpkxgfr5P6/F85U2d1YAJSyJubVMJhxx1U3 vKjUYzj4ub9GieT80FFmNAbuUTOFY3y5ZLDl3WRXD23/FpXTxWvTMNWs/Ypt67iT SLcpC1iAETTsaAzLnPxPtwjApFJv/+Wy+CXZl1xAwDHH4vnSddRaSjjl9qsi7yNh U4KUAVzpHfgmsK0DY1P4wXbBEuXCVcUBhGIQ7z5/Bw0F943C1+tFz7MckQmbsvay O3+D8NeQGNb0wCIt/Ro/xIqSb8yrI4tL3+phnUxiZGxc0z6nTw1O9BdMRX6l5/5O 84QzUal8MoDWTy0pb1eqUaXp6Cek4c9Lqvc8WPq/csAE2jb/PGM= =xykx -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.6/jq-win32.exe.asc0000600000175000017500000000150113366726451016010 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGgACgkQrxkEDHFS NAKFsQ//fWWzw7pWIt2v0vCn3VOAqWv0oB3+YEhqmYNv3UJ5cJBeOndR4C0kT2uH MnTIR5WCLw9iSiNkHUX+oiWT3Rw8i8oHkA1Ub8sIin1RksV0TosTQa++9XqdO92q HBBLUe1Y9BcIU0MgEKDtLXKwg6wqW6zAl0WI5/fIPT8Du7evhI32s9zVTMH4AahO ZUM+OXmNCRDVvVp5LS3fZR8E+R3+Mt2ZplGb+aLmoX+hvVTc15GfVtFLGMz2bF1p LVG2/qs9XQP9nCaPm/JCB1F04nHTAuUb5BzJtdJUpqBJGN8diQrxLigjyxKsPVNw 118r4Q/6FELTlMg9+6qOPal++ihjuCNWex5Mqz2+ZnANvtRYVx/5bF+DY848JmqP V2k0JF/pRnwaCd9ONzYjkWcCMeoAAvwPygl20NFBUgTHC/xdh/0j+glG0IUW7Wt8 2cJyG2OpGqR8CHTc0WDwV1SM937B9CuPjYANottrl2mDmdhhykO5gpDSjf7/is4y 9sWr4MqI62aI8xBVdLmTsF4kzoWfC5go4SAHvXlsBr7bMW90hkcuuY0giB0mWZng VQtRtpddKmhstLGoKMWf7TblX4uXA5Ym03LzvwhGl3PlwKh4HvkVYBE7EaSKuJ1g s3WxN0vP6mP1nMFF26fC9IapYWIcTXjyJ7Hmo0Okwn543E37cQk= =KfqY -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/0000700000175000017500000000000013366726451013165 5ustar czchenczchenjq-jq-1.6/sig/v1.4/sha256sum.txt0000600000175000017500000000120013366726451015456 0ustar czchenczchen1dee4bd2516f699723f373b2629c9173ce123b92c3a72520d0e25fcd8e3df45c jq-linux-x86 b9b63aa4b3cc85df8bdca884effb69d66e9ebd717454a9e212d9423ffe3e955f jq-linux-x86_64 6ab184edfa04d6f662a696d8594f19532ed78bc6fd05acf4cf506e789914300f jq-osx-x86 335a99a68eb9a1ecacfc947550003f103cfed627d3116c8bcae9ac11dd26d337 jq-osx-x86_64 2c1382b65a91fa27f2b9373331684de1e4ca577abeb724c3c79f4733af89b854 jq-solaris11-32 da5e4ab2879022f365b77b9babbcfbc01f30a98b884a31120031e218d676ac71 jq-solaris11-64 e08ca23dc637e8c0fe1577f592a9367e036f5b3f4c10b189c53943b0c8868866 jq-win32.exe 1c96fd65fd60763b92a28c7ee1290eeb974bc69b71b963d0e45cbfca1625565e jq-win64.exe jq-jq-1.6/sig/v1.4/jq-osx-x86_64.asc0000600000175000017500000000146313366726451016040 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzj0AAoJEK8ZBAxxUjQCeTYP/jt8hRyHNKtFfahjW50EcAab nSqUr5VXz80BL6vSrvixMoffv1RDFuzEZgFlYZGXErXjK7Rpo3NAE+e7EUH5vbgk kJaZjzGfw8Kx0ZCZFlmDmI5U93xYiSeIPPP8jaUEg/Al01CH/Y584bgNjyxi3zC5 RKO+/HTYd7KR5jAWNzKqV3tGvXJdi+a7Msw65OiJLRoVFX4ps/CGaI8Dh+vbbPi4 zPjG2DChoMdaRNxjQNcj/FW7M53rXIUHbXAmR1jDOSsTk16V3kX0eX9RSBsgTkjn b6/BrXnTMmTJR1osHIznuzuQ0sB84+k5CF1ZBwqYjmIr697qEl7r+bWODCN7NcJ+ Lo8re6lTAJZBqy7IKgPb6lwFg2tqd2P7UCv0Qx6dLUQGzqeUBDv5GDR5DjTQ1LqV CpyZbOFkA4UaKbmTp+DOGE5TV2D/hzUAcX/HzEo3RVAKSJIutlcxHpMyIazZbRQr RWNU4GiNr0Sbpl00oa6GU7OO/d+j+BFcEKrJ8Ud6zv0M4OCFkP0FW5CKxbl0aql2 y8cxcrdMmUhMwn/Jv7pX/N4Hqa4Jjzyy719LKRXp9yjtXV/AnEY2aQQqR+I1Q3VN DBvw7qLJe2hzQhacwOGDNHc7L5JgOC49wDw7L7MNlBGHPdEGKDMdEc/k+Nn/VnOU l2JS9gVb1lP3p1TPKSIG =PKfI -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-win64.exe.asc0000600000175000017500000000146313366726451016022 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzj2AAoJEK8ZBAxxUjQCao4QALTCDd7W7SZ8tyVNSmOsILmi 7OcRdEdw3WNEpIXhYIo/NX1Sddtq1KgzHAgRb1tR3imB4UwVQfxD2olgIgoJTaMj P5jnRBIK98ht4iPXFkFmz3zOCBHSH/pXTy0jMjG0uC9czT2OWMlazA+brqaMI6oD JtgJEPIK1HfRdv0EfYCToj5LLi6iz4PVukk7i+8dqW/xFt1Lqny2ehggBFaIpG5D FAuuDbvnmzHODy7xpzPnRgBttpo6bIsQriVHrw81IWyhQJOKefjdiETB6N84+Lox ylgvk4Y5JiIrFVSoQGxbo3FlFth0CZlLSE8GuNSekAqYd+TjPIgtBmaHPuQc00sy CSw+NuaOkhq3y+rSkJgr5b04KDfvDhce2vF9maXtVPVEQ/r62WXNuCUoBVyift1J ViA+Ko9RISDlypHQBUj9KaWAUxTe14pKkaVggkrbM5c1+fvmjxr+FAZVSrnz8EOF K0J2834mNKNZWTo36NZ033BzoWE40HtSG+iDo6vGXNbAp8pFdRrjSaBO+Lklw6o4 uMixmPDABielcUIaZiu23vW2qzOhuEIav5d7lu72XI+8ZdHrUymwra3EAs9Mmg7i 0NPAwMHOPjdliswo8rp2hY7Shd7oMyNqxWj8BaPLCsxl82ofoPAtTcI4HCW2JXt4 u2hOqXKZ5LihwcfgQHpO =By7M -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-osx-x86.asc0000600000175000017500000000146313366726451015527 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzjzAAoJEK8ZBAxxUjQCmKQQAIei/vszKSXY7gyirM1FmUEO jPEzjHPJvYOEIw06/I9Jkn8ufsTQYtP+S19C8kGLRcZIonlXZxqQmbYQUv02NqEn UDRlCxiQZiGLO6i54vBWi5VhB4VNWgEbrctQ6oR+mV6MXs7RZo+2SGEJT+r4z8om HpFGQkfdfwNRbAXkIDHCx259pzL1gy/YrWV/8F0VdAH70dmqidWCQ4Lt+oymUhno 0f9gBOwIFZJXJRhhGz8unHk9XHdDlrpdvEb5XpuU1Alvt3BKa8vJslbTY48a+nI8 3XsfQhAXItAhUIWHTq1dnqmJzDvFzkZVEQQ2Zt+e7HsA9TG4StLCaYCIorUp7fbo BbxS6I9TUCbp5YBa2EHiq65/jNBlRz8+GB1cjZ3oCTpQQUTd2pFR/fdXXKxvKerr 5Gxc2CZqaOGW30iLwvtowNt5y34nf33WCTtBAPpY44aD3TkFkHcQq0hnZQI9gpV3 eB5uUoLrr3znLVIs3IX4B2/L0cKP+Dbal7iDQ5nsmEHGBy5P3yOgLbHUg80pG+lX b2wQMTCyTrl/lahJY/0n2r31yBgmHHMBNmY0dWLsXwzemh4TlVQ6IiFl1rF1PPQC F7bhTHQh1/DrhW94C/uFU83O0LveTb/U0FobAMCMgrd9Ew3C+gwXOKP96DHx6mmY ldHhiLTbmjLbfiDMaWw4 =dzyb -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-linux-x86_64.asc0000600000175000017500000000146313366726451016366 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzjzAAoJEK8ZBAxxUjQCTVwQAKoTbAddyfciZzgaxSWqc0Yz f9njjvPHRtUfa0X5lDQghlpTrDY2CxqU71KknLgxW4VpnErf13KlVoMBvGJwCmU5 JcO2aE3iQDxpf6GnYa57caSsj2pR5Fi+/5nvxr2ovoCzGea3k61j7G+GV+znccAr K3c+03aq7p1LGfsqQSGPGH/nLnrqtcFonNVPxsaBuFpGMpaP4DuI+zhd5bGGo4g4 k23mWbOee/SEdYgiMNtVnkoQMZmSdae9O8VuTMoTSv8R2aLmDTrrLa/3z8m7rQAe rGQkaHhRg/fH6gLgBGJe7enqDgbNCmKp+zxfI6p5RbFvs5oH+M/wV1edG2KFUpI+ fv79Aj5XTVinNSCewrJLVHkzd328KhAeXzm319wJ0Q2sRbvO/3rhqslkRPCW9Uan uxo5dLMYqfzksy2Cwhv+c9r7Xfi7tjKidPLbwdz620eERquXSx7+kZLQyOw/mx6O ufnZKMDfhxZXt/KkNzraqkYKvZ6eO7r270ao7tdSZLDC+zELMGzIXINkOPXQDA0x m3Zz97jmfMQ6JwWU/1jCIOs+CpMueBYw3VyyqKNAUvYIjkw9A6SZFYhwcTc9k0Xh 0XSGZNjiKxTe1UJdBXG/nwNb6p5rMWwyesbiqvWiCIQ6aBX6SluODZsd4479bcVb 1fYsg+Lu9V7FiGY7ZlCz =oGfI -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-linux-x86.asc0000600000175000017500000000146313366726451016055 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzjvAAoJEK8ZBAxxUjQC5KsQAMt1IwBrIn8dvTe44djO3QYk H/qaNSRgChJI81GA+B4suTcgh+PUJcHF1EWls7CCfjfp1haocikXpNSuY7mF+43M SMdz2ZA4+N542ratUTv7YlG/igIvz6IbDysjowU3QQsCcjthmO6OF/6fd2qRhggB 3K80AMESOBBi7TkFTLw1G4M8uU6VvN+LWrG6fOcPXoRLlqShGzskDqxpobL1+o6J Cf56Z+ZVLQ6DPjW2pxK2R88788DjoJ4j53wsBqzgnHMeGzWVzvQ836rYSD4dhpSn YopiAesAYLu0hJszHbJrigt/jUGsNekQ+/ihqGXhbtDVjpYLU7At42b2w2AgjNSK GFBJmICTQZapz4o/lCCli82PngGmIVqgf6fc7qZq+HHtrcBeNYjMHMPMJroGqanJ DhCnLmE1906RpJ3VvCM5Sd9lSQQpSVzmFAh5MMnprxvsAmDbL+Ty//a/cOU4WPAJ MgXH2REmeByg8EX6SPhtshxxdIQf0CX4uh2r1aHQFtg/sNPL/EWGcRq+DVbaz/8R TJCfu9Ov0GhJlZSMHbkN9cvrjeE5CNM+7Q66JbhOMp7uOo4I9+JyknNJP7L0Nvot IcC6qsw8jMINHexW3RSDbX6b2S+0BskojAxp6IYHv0y82aaJ5I3+4UD3A5+Q34iM g1kqYu4WnPOZ+cihvGKO =8ZEt -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-solaris11-32.asc0000600000175000017500000000146313366726451016333 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzj1AAoJEK8ZBAxxUjQC7PUP/3J4uNx0zpw4SlnB+yBzrKqn Zk2JahOg8wWRFbMACjMAjTMGmi9vYlLQRjesdi9WdvvlD1iW12Pi9ogA8OzFRkH+ A6It1g74k63WVtyfNHfLTeh2euQsLUc1UJZ+36FPs7lxZPsJmAL4kB1M6aHy3w/K aZT+h4iZhFeC3/JFvLhd2j6/RDf+FkAiEbRlSXoNmeBTLnNo82liEinZFUvEsglO EIVyPBGp3mtlBE8sf6WGd57IpxV4bl1mXZAap1xPVpK8hrjwXS5t2midd11ENBmK UeKcvZpmFb6qLTiep9kaUljB6nOzTYyhg6S9gaJAUDl6Wam7gtBFTIMmndoGiwQO NV+Lsj3dVFz+7kO+sZx/acC0PXfnMO2o6zBTva1RpDoHJFLmKTnPDbUFl0rZtMB9 R+HBlqRR35PWKWC3ggYPzfU5bZOVp8G97WFJlKkf5hMWzKiNoiR4hAM6IZYn1rGB S31YKV1yvLIcI4mbhogBlfvXEcgqkQt4cZip16qqpy+X5n1zEA/w6cXcpushbhpf 8G6b27PysLT/u5wViSPklpN7x/SbS4rVGzLGPvN7hYR3FgaPqADDrkZYZEIpl6Zd +Gj1M4YjcSDwInnBeaSA+n41Cg9hbudM5R2cdyZzcViLgebnjyt80H+03TDRi/aT dGs6zbsjUQWFGqP+FpgT =bERX -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-solaris11-64.asc0000600000175000017500000000146313366726451016340 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzj1AAoJEK8ZBAxxUjQCZ9UQAIlJSA4wU8lFakjVI1QfJ8AA v0FUY8KPmk2RgSHzeCRZaWuA0FJAOVNmJtW7kKT1Zg4iJ2f8enX+6QTRbWO5pges 6Ib8CltoSa1yS/RZOecyn4LtmJEkh22mhuOJdPzf7+6UmSGaMhE8yep5wg63i+GA o9vaQvllmiReR7qOjvI9cFYTdy/4iX+Nf4TBQPSJPBcXtwtH+iq72IzizgPo7fFd YoLz3V55DlAWBJsyMhVF4rgWcBcCOcMOwaZ/AsKVcKE7+NiPfFsZw0qqtBKAkYBP mKDheZmbX43rDp9Un17w3JbZZ6Uj7nglPpE8SVk9tlJJhznCmFg1ls8vdzkwjkht Cr8IlnFT8ZS3i7w9CLO8nCLuMYY2UECre/wELY915fwRGY+fP3EinO65/xBWxUdl zsq3DchIxKDorD3OiAduZIqC1ZW3XSSsiXzs2+PJwzW5ryvLxGSptVTTr35qVDdo 0igUW41pdwV1Mnltncah1+NOAWcSQzXx2E4dtPmGjEcTXSsGvt1vqTZtfFL60GKs nwONhA+mK/eMOjZ+rtJhHecpqB2moTixToq2RkOWmIhToq9jLWpOnswrUklIj5pC yxkdCU/OZDTYYU+AMXlGSQoiXems5typ+/oJsKVGPGHt6XkC+WTIX6Si1N4q7CFN 8qiYRPH8G2A1QOv5uj6L =+6XW -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.4/jq-win32.exe.asc0000600000175000017500000000146313366726451016015 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzj2AAoJEK8ZBAxxUjQCJygQAJfXI0fZjCgdicjtwN2RbKP4 nLpao2BLcEkHQFmbCZXKQzy/oAGBR5xupoDbIFShum7ZEYe88Bn5W1LrKa8ELJ1C 5EQGzEsxRe5jT6YsdAJfjjEIZiDL0U6HtSq2U08iNOIIuSC6sI0FElotMUOKYqKf oExRULIkDVfYKBQEmbbUEi/gJ0d6kD8AT0ts7VQU0Ad3jCZnDnFGLZvk4XCiL9wY OIjqmA/okDHuuD4+lgexiABxgYYtJFDIIunk02LPJ6R/S5Qn7mou+HgqBJo2g16P CFwTf+f/MxBNjQOhGJtS297tAJWjjRlkZyUOraYFa0eos8Drc9PpwjJyjgyuMiPt lvFa/Lx9kwnotP8cuzZV1GY7SV60NcItIKyeoTK1QNv/Qw6DeXJ8txSGOqo8x+bV LJxLRXNHseY6+C0/1WJCIz0PF4wt7tAXmqpA9wRBJWKRaLaLnshhDMDRR18d0Ejw /4oggqNc2S1J+lF5ziUyGYyL8HjtX4ngghMhvvWlmH+UDGQlwUA/R8G/u/ESHUXU bebc9bHbo9b5TAolkCbmb8/tT+ZFTZ7oAexYgOjsv9auB82ggMTlKnN0SJag7wUy owPgSReGONDlFJ3JKWL4HLvJT/UFvqV0dnbHMmlKlebvre9v3B1sV9hl1R5Sl5du T0dL3Ha3NTeppVXq+HRr =wUMJ -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc1/0000700000175000017500000000000013366726451013574 5ustar czchenczchenjq-jq-1.6/sig/v1.5rc1/sha256sum.txt0000600000175000017500000000036713366726451016102 0ustar czchenczchendfbb2d073d2df9ee0bec44b60bd81e213b7881b27fc38d7438daee9eded2b75e jq-linux-x86_64-static bc370e22a11c85c525ba1965760faa5273aa3cd0ae69b030de4091fc776538b2 jq-win32.exe 6fb6f39d847df57b481d36941216c770ccf5f97c36e5373a0c93e63b5506c9f8 jq-win64.exe jq-jq-1.6/sig/v1.5rc1/jq-win64.exe.asc0000600000175000017500000000146313366726451016431 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzk6AAoJEK8ZBAxxUjQC2tMP/jitj5yEkh4PL/apImNJBZSd PPRWhOgdBvSV5kycstS3ni/sU2ZcHtf122BLonIJHJN0cL/jVZypJNIZ2gNTA4sn pi85vNuwhEKYfgMbLnVG03KoMtdjZ/kPSJn8UK+5ShEwm9HgbLbe1Pp7XQJW1nAG hdNT6ldqbyZ0j74r0yekN516VdpPPVp1R5rDVxcZMOKNeTbNlX4juEK+BsKvp1gT /SWxC6Y048ogMT1YnVySnh6Q1/2/riIhlw2u26Is4MHP9Dc7wxNyegTU6UU4uAXd Ejg81FpkhRZzF876ZOEfjlDsKLAYIEAYd5ozq/VpCA+rB30Y20tOCPuhilnuWQ53 9Le9ybgfJOGwloh3vfU3O9fbQKrL6TjA+crK/OmZvjSsyj2ef98vjWdFG8fhUr4f HVTnmJcg6R7WIBHxVnxfT9D3hIV0+l8qCWu3TtTlfdGcXqI0NcY7Exee6WbNdmkj 0nESSddP2nEb1eCEw9IlVliZevJYqwNaSmcN92EWTzJEwj9g7+yK0XWs+ZfDNTUD +iQoFY+W/skSfNr5XCyj2wlSElIHUlQtCVZvrnbRz/QmjZfUMTBdKkkVucDGvldy YyauXVEIvhFdeY59E2FzoZpdn7LzyzGbg2dA2QwGI7WB39L1QnLMrRPOmBwMJRvc JBQycWmsgKzAre48vKpD =LnUB -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc1/jq-linux-x86_64-static.asc0000600000175000017500000000146313366726451020262 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzk2AAoJEK8ZBAxxUjQCJ8cQAJJQoCwUiFf5MHqGHGaSC07L bKyvm2OmSzVscld2lb7MsTHRUJ3RR613CpuYsNEG/nVbtaUntzgmtyMWabuGap0I QRQb08by5wP5Mve7QCOxfO9o56k/m2MJluMFv2xdJrfbLBlJuffV8rTWCNSNFpaD MLTiMnSx0APGxEoN34wOZ7ZDL6yIb+7z3Ip9P8lni4f3B+fJGCwKr9Sr5NztZVGL fDwfTBDLHwgpPvXlddg+oEDqVoSodWW9LZYKVo3q0IrT1nI4TdCsc1Fautpz7Y31 J2rcZR5qwE9jy+fkm3+vX9Hbg/QlND07ZX21bJh1suE+1ggELrVwh1iCEfBIVEZN 3tat62QUePL5ymAaElbeX5nIxuNW8U5jP5fjtVu2uEZKRdPz0I1JcGzILi479TCK /AcBhoXvdvd6sXjqgNs8ZbNBoklNqwrbLw1ESzwzaGfKytvMIO8rN8E26CO82d0v 3gob2Bc9KE6+hdobdax/SdXzjdAsyL4fDezRQGk1LvLV3I5rLs6cdR2HRjbH2uFU auNQgDLTo1PAYgsTDvDUt61O6K1m2Fx4RZgc6pligHDxlcvPJw+DkSsU+QRD0ODk 2IYhdakVFOaG+OMcCfcLFJ/3mJnhmFl/VQVdLlrELy5r6NaQo7bkLL1asU9LJLlL N5ZHYSx7HvRCAz++hd3r =GYEt -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc1/jq-win32.exe.asc0000600000175000017500000000146313366726451016424 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzk5AAoJEK8ZBAxxUjQCJZ8QAIlRnnKW3A6yLpAGSNsQ/Rm1 34ElcCgwwGsQFaBz7ZjzYPwFNmbrQYi4uJA14Sh0MAWgJiIfjJs+nS2YiC/6etdh uCEg4NdYtF6kPlBMQ1MJN1+OLv7ACst3kajezpoCHraEQkcFgF4CSTq2zBVeSltp QxVJkMn6CM308cbimWI+wB8z//28Sxer7KyeIbZ9Mjt0YGHYsxIeNhPtDNgVzutd 4lYGS/YRbRKrVKje/20BCmU7QnyGnWIZDx3A8ofhGbLUnKX80eBuHLWUkruGbyL7 KMuXPbKAyqtMiGRziqiqkvKRe+aQc+kvJVNUYORnV/nSBFYqqp9WpaZGy0VrXcVS szAiIxPFULeAPBiycaLyPrs2sANnbLSaK5OYc/OQl3fQd8K4HycHn7gWzTrsSiJ0 O2u7YpIZwhnJusZQFbvicU5TEnTnUs68PkBRjI/MJYGna5CrE0dhEjxQ7xfqHds4 daBHctNOo/v/Wx6cUOklpdygZK0l0DWdNNgf4u0ID/qrI/r+F2WYTMO9FTxuquID AnOcKJAnW/uoDx3eEl/Xf92Be1pvsBSU2gwJZ97UYXT1DKPdsoO8ubUNTjDL2BCL 7mVQShAasKvCB416kg2v0NqR83v2iLzfERbmyZhhR67WwjSi23r9UXPjgBGsux6h 0uohePJ5f76elv2W/8rO =4Qhb -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc2/0000700000175000017500000000000013366726451013575 5ustar czchenczchenjq-jq-1.6/sig/v1.5rc2/sha256sum.txt0000600000175000017500000000061713366726451016101 0ustar czchenczchen7ece904edef3f951ca0c6f68cdb14e782d52aa46624fe2de9cfabfbd98fe5797 jq-linux-x86 cced974e1f2c50d6203e09c2e3ede44075c4f35601233378deaf7bfedb2b4aa9 jq-linux-x86_64 25f930463c94414fbff1ba5d76a9547259073e8acb19f709880e913ab58586e1 jq-osx-x86_64 8bf6bad065dfe5bfaa4c22a9ca80fddf8bdffc06b8fc768040e4fd88fbe5f58a jq-win32.exe f8301b1279ac95e09fa9bfb1b38d411e68590bedcbe70d27ede7a70955d855dd jq-win64.exe jq-jq-1.6/sig/v1.5rc2/jq-osx-x86_64.asc0000600000175000017500000000146313366726451016450 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlXAAoJEK8ZBAxxUjQC3YwP/23mNNa+XscTIWHKPihfNoNn TIgGXP5znWOUmkNarIDl2ir5qi8oq6kBLoz917uz1X/KY3yB1rDoz+apB3Co1Ayh S+oRMeHUCE2V5OIBPsJyMHM3cgfdbVx3fNwZitpv4Tw7s9ilRG7SWD/2Ev2GKx/h Qn4j9JLrxJdvoSS488oyjK7rIXLSDM5R97qFS52Cl4beO0dJTdy8D7QOCF84soHb SUgrXZkNuzjs7qQRMJb60mhQeIkuUC55VoOUufoXTwpBZJNNoyqT1eNNlZXE884Z kvKJ2q0FYD7jh1Ct9KFr6gfx0HIjxMb2YYtCEilkKSCHG8JUHgCY5cpAMDZrVa5K ss9Iu+dkor96Qv7nyJ+zhjEscl1rpfvYKJbMLhC+M/lHpZc+5rtZBPFmZv8wedzB a3/hkyPpxp2YPa32CAZ4UVFE3h+lBLvT/PV5pB8UvYPbDrWzLx/V2aNLP1joZKH0 y4DECAVMAD2a3dv+xuee7ZJcmL4gK8a6sZbYuOWgnIjjGQ4W951Dz/CoiojqofKO 9QmcDiEOQQdnjpfuObSX6wNV3583z4GEHIuKLQiM7rtmIvzA2x1RCF/txZoxg9+T vwvyz+689ju/mIdUjdCGwPW6qljoMWRPXHJ11mD0CBTQmNEJiMbhE5lt/yH3/big mVPHv/DkOEM8pLdpMHlj =+eGW -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc2/jq-win64.exe.asc0000600000175000017500000000146313366726451016432 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlYAAoJEK8ZBAxxUjQC6bQP/3WzIL/KgnUmI3ziUJcaKgLx EOkMA7Ib5o5w75gg9img3pvYCLZnlf40TiRAhc/gQ+uIw8uLs2wqZFPSNE6G1U6i L2AQbcl4e7P4glvsd+PjXKz9lzCFyPtCgEeFA/T4UP07ef3yRpreFmYcW4A/5Saf WWn8nKyVL6vLS2vG4zZi6g61Rw9mlWFC6VeVLe+/IHMH3y6WtT71SDw3h0OFH+Ui DVSFXeu5Fiu/NEiZUyb2BLK/a817zhQ5xq2/mCoYrKLgLzbKaSuqMe47Qn6Yjkbo 82F+lDakDS3bsoIcvDkR2w8ABsPwPX+xDk8Z2Usyq2wcCEvSTKoB0AiczYfe6Eod uz66AoDZY/v3XXs9F3NWJqLRfRzu+fHcw9fvfzLET6gVkmbc5+Z7fDNXOtJlS2oT KKImd1vuEoffjgblera4Dg+v2f7rY7yFCQEsYGLnhyEb1/G4nb/MvQJ49NOB9AXJ pVvslWnwWJVy0pzo0VqjpldKGg2EAFcJ19+pgUOFFCF2Ax1HsvJhuxiyOKBDbn2q N8ivnjv79B62epIdUc/zGLP1In9yoQcDfenYlBI1PJL7nw4JdxFGBS5ajE/SftAD FwsuKvIlToO2s0bn+d6famRnxigWOhjr+/Bm/8AP7HAJdD3Jes6AJLsuUANNUfW6 aOU4u35qnEb1KZIrvozH =ZZtM -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc2/jq-linux-x86_64.asc0000600000175000017500000000146313366726451016776 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlWAAoJEK8ZBAxxUjQCmrMP/1jfOkKVG7FZgArN0ZYBgJiR jP5L6/zlMG9SDecNnEVJ+PX7YdiBlnIQ7icV6iveBsSvkVyY1Mp+0ibzIePtNV8I 0SScmo4Da1gQwxaX8lKIfCTemOSvhxl0xqdOsYHq7nRxnxSmh4MFnDqXLS8ryW02 /N4NsFaUCjEvyFVfNjRGDwxyR3RhXr643p3vp+JZvZ8wLeZhM+ZXg0VDdXAFFVHJ EjIvedxV/IU4brVkqgRDPmAKdaVbLRezGl2uj7PYg1fcNXevUSHEY6bsxp9FSanU LWkD/0rElPdsV+B5ST2f3K7vTSjDOBA+6L3AEUR2tViCe/FOJGTMvVrJTG3/r7hh STARqZHX9q/1oEFQAZY1PX3mKCUrXKTBLkTWNWg78jtYXtV8JagQQPV5CcmaqPVA u0M5lNKDhgx5Qwy/Ap3FYkKtFHFuDxK6Y+px4J7hGRuH2casB1lYX+4YS4J8h4my 9dnmly/fpOI5nuONzXweX9zizNcmolqf9RAvWE0umurBj8Qyo5vMeEfiHVU3DSxw tIDAnT1bQv6s0mEBvFX2Fx6AJHc8wY4+iyu/fOMpSdMfbJ8lXeXYFH+SzV4uwseq c4j9eQ+Ab8SoCUMsFiS7KDAhsiMYk6rKn+aJthl/72GVBbnoLbYJs0frjlAsZmz+ ReXdr1sBmm2qFoJ43w+9 =UxxZ -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc2/jq-linux-x86.asc0000600000175000017500000000146313366726451016465 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlUAAoJEK8ZBAxxUjQC6acQAIREc1GPBwDiL3STKWVEUYC5 r+Cti+tWisdNGvAapCdGcwcavZ06PM3lMejBpQ5v2WghvsXhFvxDnwyQHKhbjIdG Uw3btu3ykjabHfqi5L5neQjFJwK//zsan59VSTzRWxjPHpAEoFsEihddKdx8kWzI KfPx1sn7ftO5weFcl0MU9ks9kbADJpviAvCk1mxKeNbv5KE9EYiUMjEg0kW6JmBM rIJoWc4fUR7rHbnK8fKpEuBzPLRBppFOG+ma7dx/4tCMjZmzmGIzqrCwgRStoS6j mr1Ws1f+j4y9ZdCXqubDVpzesIQ5fWc99H3kNuzIHBku4o70OOaGKdqvi5JBSiJf E6hD6FrN0ugu92IvRHNG4Vkg1hfNiff97ZX4lEHicFy2Bbpql56yHc136wAlLkvN 8ns9xIE1APTZI6J/6lHfUDStWgKOlsgf51UuT/sJQeE0N2RscSJ7cxhR2F96RQ+Y Vb+hMg8QLA99B4OWbohkKR9eePoEjLz9u9kgG/AOJy24m76pjO3az8Xee9I4LFMp ws78NtQgGi+ZJ6JP4QsEx9ZakpdWGnpDgIskQE+LL7MH0mXR07CpXXEMTnEIBMj9 ll/pFQ7bFJiPSXZZhRQVSiNhL3a6DeLe/Ik9xY/djH4WAMuOpEQsbI3cRxOktp26 FsUWKNf6I+CrLaXbbrBB =lABc -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5rc2/jq-win32.exe.asc0000600000175000017500000000146313366726451016425 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlXAAoJEK8ZBAxxUjQCwSQP/3hRFwE5XzTNpBbMFiThq8GP xON/4/6H0ql/weVZPRyoVVuToKY+oym5j6IhwBp8eswQKMF0p5FpBjFJFFeXM9RC iSmYL7NGKSZJMLQHFLM37I/eDbaIOo4OobtzD0CAg8ZN8nlIHajxZ+vsmcuU/L6k nC5++MJEBWoUMYUdqII0F+ivlZFwngrnCgeLIVwWt55ojntbBjjs32S2gT8HugPz 11zAnx5C7KfDTnDNgMPFDPL3T/yVoyasVxidTB4sQrFeR2vns4F3uDugEBAWMb9A 3WRSzcuYdlrVOzB8WoHS3E0esEeuqtPIz7y3mvrvYsZcpIPpXaagmKALG8zZ7p87 /s9zpQdFWTe+boI6CdjG1+UKY/QhDAl3zalIQiqFSiBsbk/yhDtNug6oqGO/2fQw /xWlj3CmQsSMI9KjlrcIv2k4ugbYtIxVRvEJqqI8Qb47Lmav+F+VNhrwCcFdlMiD swD4xfT2Knfkx7OOfDv7fCRE7njBBOTumYu1o7G8HbZokSkAtS5sovrzLWEzXRPz lV4Of454G5Quif+MQIw0P55XgFCLLCEpcbt4vOtkCK/GaLuV2+JKoyJMoFEG2mAr T2ufop5Re1z0hvNe2VLsMhcUpSUKsFl4h1aM+YYhsOnlGG7uXA0bTAMmU1+n2uKE k/rOwLp4Lyh9gw78IB8T =A99j -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/0000700000175000017500000000000013366726451013166 5ustar czchenczchenjq-jq-1.6/sig/v1.5/jq-linux64.asc0000600000175000017500000000146313366726451015605 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlxAAoJEK8ZBAxxUjQCFqIQAL2JdiZNp9GZm+SK115N5Twz mjI66fKWg+1ckSfiCdYwSzg0u7AFSECy+S8vq9WzSKsvIptGPJokIehnIrwpuw+l s7sfDA585XkRxpVoEwAdPalTRqXvmku8Sc4P1519K2DZDvtvWPY3N+tWIRE9jfcW 04KDTuWB35N8e+Mgg8z9P0NJiDanP6SUHp4r8Pc7KFQ/gOK4HKq9dFa3nsmkAq25 XJTFYPS6VU5tj/ZyTPNtMh1Nb0vp7+qeXGV+pBMwohmNoBFUyx65olwegD+dSswb Cfh59P+aWec7V6exJq7EW7ptIaZzp1LeoPfcABDjSOTEeez6ywIcsPnmfIjAh98e WovH2iiWH9XvLT6EC7gUcdlMJtvz46OkXilQAo0rK3X3pFX0T2aSqp/afdPtIvpK bErpQKt65XNsRprBkBlM1Jo6K7Cwh/SYKlbSJuvYvieORQ9w+0oqmYRCk7MZ7OOG Nxz3yEvEnzQPaXnSnJ/cK0xb9Yjj72LRV3Ors+ni9HfLm14xN7zBx+g6rudf9hy1 UFcvrxJSRFP5bZZxorWc6busisRi5E9sRQm/kGlTUUbrJrV1iae7h7s7+OqnY3aG H2lF4Gfb1eVsltQZ3DCRNiBiTD+3cmzEayD0ktHRnubOQCc331RoEwSNBsV02rkH 2ZijCFT/AmHOl/fqmX86 =b++s -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/jq-linux32.asc0000600000175000017500000000146313366726451015600 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWKaLsAAoJEK8ZBAxxUjQCv7UQAIEPihgkz3swMhxQVsRSd/dy d8sQMup6KFP3jAOABLSYyi6MrmCZhejodNd276ydxSgB/6rPClPZC6kSQsrv8fyj 2CSaQOv1cItzPSObL39xNHAoj8LOkSbb+4jultfbntWU8iuAaBzrKc6jH9VR6zgJ YfeQun2LI8AqqBK0nClSwXLXFcjxmzR7KDwX5EPeBQdM1uG0iHO+CsZWKjC+pHPy WkNqOubVpo1zFFx6pTFmDNraPOxdLvMVMZ7fuHBbdv95NEi31CE4cmQzIhLd6BbX 0xrOfT5xsib/uqlyhtUMjcqdrVcex8hptdsxrh5w9cZyhAinmpIEt59yS7Sbcg1m Cs6EdXJjjRGk5KVlyjuZJYOZxtCmdOYrLZVp7Nv2swIrUebkqWnXUAQk7Jdu8XmV 2UzkhFvlWaTVb4dJ5s1zb6C9Yv9hqACsjXzM3vstcu2tiGmaH/TGCggqmubn72Ag Ok+YdhveZxnL8iF2odz7IdpC+kC/vVfCY9ndHv3Sf3i6uLuohuT2x45R3iz8GIMI ZuexdQGz/69blhw+ZsqVpxodgZIqnCOwIk2nTkrLOw2cJbtlDWh1s/9udW+nMmPJ Z2lzSP5A9jJzMCnkb5104LdpyqbGcUO016Tn1OKyLQNssViKlV/kcP/jZ4T0vuPI NIP2sEDPJQFBmsBmFR4B =f2Mr -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/jq-linux32-no-oniguruma.asc0000600000175000017500000000146313366726451020216 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzltAAoJEK8ZBAxxUjQCbHIQAMU3mjBvi24X5CfTkyOmDOb9 sxxsuTOiH8Zjiy+5NP7a1YLDD/yznLmuu4fm+XzCE8DYdzkq57jSyjr8rqN5poTM bk3+9P/Jf0Kae+tpTP8mjvtJ6bVPweV/Zg+m8YwE6/iUHQHi00AyQ6/obfXx7otn X9k2IIJATAvR7WJ+5+Uc390MkVNNvuINfuuLWdSPdIbwFf9lvZ2ZmwanrWo6Gzdr /TW3MCwZjWjENGQBYNTJwswTdWww5Mo7MrEK/eHEy4DlozJyyzwe5xsWRz+ERKSJ CXe3+DTSVoc4DVuFEhU3T+7jU+Fev0FOqLlu0jD64Ma7epk8/gP/gzzV8JmkX1tV xwqZAdlPl+wNs1ZQw+9dsTZiRIT/qQBxa4y7bunnjkxaSa2ovTz6EUaioeUk62z2 e6zXNh92ZfjNKJT8An0Diq8i1P5EDH3dBHX3+2b9Qc15SFXE8vdXLqjIvTzwZ7hw fkxF37eMPfc527YKvktVUuXKkx458ossrHaYdcFnVh+etl6T/hQASE8Kp5rgVd/g 5/P6/JrkrY3JTHM8qVC74YLqQh/w3xaiBzcz+za255EcKwU3qb8G0iPJ6i30yvCC l/Hj2IREDHyogPefgq8PYah5RvVXbiWJ/UKz+qm/vinm1DUU23F18Ezakxd0Q726 Ok76sclCtAR8SCEu/3eU =fBEA -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/jq-osx-amd64.asc0000600000175000017500000000146313366726451016016 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlyAAoJEK8ZBAxxUjQCiLMQAKBpyaj6un1mLebzt4JDQmEf qzzYK/q5RLS0vGzcYAl/ylBPdbBv6j7oT2JfPA9YfZaUPOd5O6eqk6R8OVrEipNe kvfIMnzk5joo+LDomdZvOhv6A0f/Q6eKnlr8k36INQxtbQA/xzUAuQthoo5Xn5ze d7lEHcWCpyaRne3dC5W642qG6X1hUN27lfx4MRHfSvFsSS4YIyPQHPiKPkQ9BzyZ p2XmWLwNSQRaVQU/fG8LbcRFeu7CBIFlQ0IvRfARNvJGFO+Eu6JDNZg+w8b1GL4O Td3rEb5tiJ1LEZxe2HDUV/b7/k2BywrRGnMbLFbohtyr2BmFCAzqy6UySNA/DU3H 5owLGNw0F3tWHg9yqGkTMcMTp0nfIbNKbjQcfSWKO+9gkoNyn+o78ggerRJAAt0U 0vcoLbfTMBTRfxNHaRB0dd3YxbkHvr3tAY9A1sqkBGB0SJoZ6WkLsso2tBsivq/X K9x4pXY15LSKu8opFJ1PY82Ng2FdRlkYs1CDNB5IM52mp5IAgmxGzHCy4S/XRBCq i0RSwg5xwCRllqmfRkgHQDFT8lKOLwMnLrzqQVsBneddvT//A13pHAI+vsloHbiB PwNgEdbvFVrxMaY8ampzQZ7vw15qOGcmGTJolIEhPdxvP9J9zTcf5nzF4WSJPdMC IF2dukcTW+c/C37F5nQu =+j1U -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/sha256sum.txt0000600000175000017500000000060713366726451015471 0ustar czchenczchen264118228c08abf4db8d9e907b9638914f3eadb5cd50dc1471a84463f7991be0 jq-linux32 c6b3a7d7d3e7b70c6f51b706a3b90bd01833846c54d32ca32f0027f00226ff6d jq-linux64 386e92c982a56fe4851468d7a931dfca29560cee306a0e66c6a1bd4065d3dac5 jq-osx-amd64 1860c77bc2816b74f91705b84c7fa0dad3a062b355f021aa8c8e427e388e23fc jq-win32.exe ebecd840ba47efbf66822868178cc721a151060937f7ac406e3d31bd015bde94 jq-win64.exe jq-jq-1.6/sig/v1.5/jq-win64.exe.asc0000600000175000017500000000146313366726451016023 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIbBAABAgAGBQJWGzlzAAoJEK8ZBAxxUjQCW1AP9j8jyZcTFFzBAqLbxrn5Ljbj 6VXIDrutvXqcL1/KiPt2zT0deAqh08yCZh0RIQuq/o86nOXKumcK1E1jIcZPQ0p0 g3Pik4yvnJo7HCrJzzOXatfb9PokTEmM1Vo7qrJvQ1DPC8PyCS+fd2BLV4km2Uvp IMF3Wsa1nfRlcb/6Xmq/QJDhOG8MoIJ6Ldo+nFlCDqvKzGpQmmGo1gj3x32LkTUE aA8wtZ4RsCSEY959w5NVxhcX6H0iv0yzGtpOPRwaOOmymxuA0w7035P9TDrGExhU t3tMiTBWLucJgHPYH8XunBKjVzFDj8TT7gOq18MzuCJxUhPaJZZWAndFxQ8rxgNA ZUzmOpaX95DuRF9a5SMP8OpAOCOsM0lySCyJGWLoQ0Su55uaThLOVaDqBmXF13IU lviJ0DWPzDTPZ1Tn77kwhAWXglcsE6S2pBhkLID76kW2GtXVhatBPls0mE2Oe0AS p69dW/ynDuXyf0UjpcIH9UPVwsmXPX5LXztAh/2r6raW6VdnvGCjb2dWmPgFJP38 HuIKPnx6F3MeNSwKHlky3xfe7X4GNiciwTqbd6EEeIHGHMDbKSA91tuhbiBDGulC f+lGNWXPZTN+zqbUaDAArLno3vg+vQKhvvkdwfRhFYYqeFJwdKGXo5aEH5g/t/n0 8sewgmHKqlUa73LzABM= =WcIy -----END PGP SIGNATURE----- jq-jq-1.6/sig/v1.5/jq-win32.exe.asc0000600000175000017500000000146313366726451016016 0ustar czchenczchen-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWGzlyAAoJEK8ZBAxxUjQCopgQAIII75ovkAQmCS2aU5Ph2Yfk xLj40CVL7KThOWcfqKYy2Sv4+QV2dIjx14Uni/4w/dVGG0shtw1teUlfotBczR88 iyWvSVavbJLlyHESN7z4M4uApHikncNDlFJm/CuQWSVkGhtWvZZzJdoS/prypfai 1+sjtiDidNTZEJj5nfkvZ90UKpzNPJD8RGpsN5qvGyVnIYmXWZ1xe4pwKiNaUthJ 0wUBUcisTruZWCdSkV5cQMvb23sH8Qt+OZYgxJvx0Eq3+fG7Bh1J4YJLluMB1cSQ lqpOn7yJ4VptkgYeXoPMhbvN3I1/t+w43fL2P4jo5YTYuyVjxkS+VhEIGOgl7khN P0zzc1fdUw857dTP1/gXHTgMq3mWVAoQZnqcZEl1wnM155i+PYuN2kG0N2q4nt3p oQd58tZD/KmCV+QTegtt/gpavztxqWr6aagI+UynBfVKaOxY6AB+0+1oaQTX6iYK iqCqbhDi6ak8e7sXflAKrMhlnA4X+XCINdSwnYR96t/JY9tMFDiDapvbfgzdCi/D +0v7S8G0cu4pEjQw8rcotoPU2qF9cjMpmi2RsFDHpGsLv5r7avz5jDFxu9yEVMMd sp85aDh09bZfBACY4+t9m1AHCZO5LbkLremHv+nHA8ds/wiMmcCmRIgJQKvOA5l+ C/380YfJeepNvCnoIpr1 =NVrs -----END PGP SIGNATURE----- jq-jq-1.6/src/0000700000175000017500000000000013366726451012502 5ustar czchenczchenjq-jq-1.6/src/jv.h0000600000175000017500000002347713366726451013311 0ustar czchenczchen#ifndef JV_H #define JV_H #include #include #include 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); int jv_object_has(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_keys_foreach(t, k) \ for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \ for (jv k; \ jv_object_iter_valid((t), jv_i__) ? \ (k = jv_object_iter_key(t, jv_i__), \ 1) \ : 0; \ jv_i__ = jv_object_iter_next(t, jv_i__)) #define JV_OBJECT_1(k1) (jv_object_set(jv_object(),(k1),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_9(k1,v1,k2,v2,k3,v3,k4,v4,k5) \ (jv_object_set(JV_OBJECT_8((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4)),(k5),jv_null())) #define JV_OBJECT_10(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5) \ (jv_object_set(JV_OBJECT_8((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4)),(k5),(v5))) #define JV_OBJECT_11(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6) \ (jv_object_set(JV_OBJECT_10((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5)),(k6),jv_null())) #define JV_OBJECT_12(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6) \ (jv_object_set(JV_OBJECT_10((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5)),(k6),(v6))) #define JV_OBJECT_13(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7) \ (jv_object_set(JV_OBJECT_12((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6)),(k7),jv_null())) #define JV_OBJECT_14(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7) \ (jv_object_set(JV_OBJECT_12((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6)),(k7),(v7))) #define JV_OBJECT_15(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8) \ (jv_object_set(JV_OBJECT_14((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6),(k7),(v7)),(k8),jv_null())) #define JV_OBJECT_16(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8) \ (jv_object_set(JV_OBJECT_14((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6),(k7),(v7)),(k8),(v8))) #define JV_OBJECT_17(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9) \ (jv_object_set(JV_OBJECT_16((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6),(k7),(v7),(k8),(v8)),(k9),jv_null())) #define JV_OBJECT_18(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9) \ (jv_object_set(JV_OBJECT_16((k1),(v1),(k2),(v2),(k3),(v3),(k4),(v4),(k5),(v5),(k6),(v6),(k7),(v7),(k8),(v8)),(k9),(v9))) #define JV_OBJECT_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,NAME,...) NAME #define JV_OBJECT(...) \ JV_OBJECT_IDX(__VA_ARGS__, \ JV_OBJECT_18, JV_OBJECT_17, JV_OBJECT_16, JV_OBJECT_15, \ JV_OBJECT_14, JV_OBJECT_13, JV_OBJECT_12, JV_OBJECT_11, \ JV_OBJECT_10, JV_OBJECT_9, 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_COLOUR = 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? */ jq-jq-1.6/src/main.c0000600000175000017500000005406413366726451013605 0ustar czchenczchen#include #include #include #include #include #include #include #include #ifdef WIN32 #include #include #include #include #include #include #include #endif #if !defined(HAVE_ISATTY) && defined(HAVE__ISATTY) #undef isatty #define isatty _isatty #endif #if defined(HAVE_ISATTY) || defined(HAVE__ISATTY) #define USE_ISATTY #endif #include "compile.h" #include "jv.h" #include "jq.h" #include "jv_alloc.h" #include "util.h" #include "src/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, int keep_it_short) { FILE *f = stderr; if (code == 0) f = stdout; int ret = fprintf(f, "jq - commandline JSON processor [version %s]\n" "\nUsage:\t%s [options] [file...]\n" "\t%s [options] --args [strings...]\n" "\t%s [options] --jsonargs [JSON_TEXTS...]\n\n" "jq is a tool for processing JSON inputs, applying the given filter to\n" "its JSON text inputs and producing the filter's results as JSON on\n" "standard output.\n\n" "The simplest filter is ., which copies jq's input to its output\n" "unmodified (except for formatting, but note that IEEE754 is used\n" "for number representation internally, with all that that implies).\n\n" "For more advanced filters see the jq(1) manpage (\"man jq\")\n" "and/or https://stedolan.github.io/jq\n\n" "Example:\n\n\t$ echo '{\"foo\": 0}' | jq .\n" "\t{\n\t\t\"foo\": 0\n\t}\n\n", JQ_VERSION, progname, progname, progname); if (keep_it_short) { fprintf(f, "For a listing of options, use %s --help.\n", progname); } else { (void) fprintf(f, "Some of the options include:\n" " -c compact instead of pretty-printed output;\n" " -n use `null` as the single input value;\n" " -e set the exit status code based on the output;\n" " -s read (slurp) all inputs into an array; apply filter to it;\n" " -r output raw strings, not JSON texts;\n" " -R read raw strings, not JSON texts;\n" " -C colorize JSON;\n" " -M monochrome (don't colorize JSON);\n" " -S sort keys of objects on output;\n" " --tab use tabs for indentation;\n" " --arg a v set variable $a to value ;\n" " --argjson a v set variable $a to JSON value ;\n" " --slurpfile a f set variable $a to an array of JSON texts read from ;\n" " --rawfile a f set variable $a to a string consisting of the contents of ;\n" " --args remaining arguments are string arguments, not files;\n" " --jsonargs remaining arguments are JSON arguments, not files;\n" " -- terminates argument processing;\n\n" "Named arguments are also available as $ARGS.named[], while\n" "positional arguments are available as $ARGS.positional[].\n" "\nSee the manpage for more options.\n"); } 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, EXIT_STATUS_EXACT = 8192, SEQ = 16384, RUN_TESTS = 32768, /* debugging only */ DUMP_DISASM = 65536, }; static int options = 0; static const char *skip_shebang(const char *p) { if (strncmp(p, "#!", sizeof("#!") - 1) != 0) return p; const char *n = strchr(p, '\n'); if (n == NULL || n[1] != '#') return p; n = strchr(n + 1, '\n'); if (n == NULL || n[1] == '#' || n[1] == '\0' || n[-1] != '\\' || n[-2] == '\\') return p; n = strchr(n + 1, '\n'); if (n == NULL) return p; return n+1; } 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) { if (options & ASCII_OUTPUT) { jv_dumpf(result, stdout, JV_PRINT_ASCII); } else { 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 (jq_halted(jq)) { // jq program invoked `halt` or `halt_error` options |= EXIT_STATUS_EXACT; jv exit_code = jq_get_exit_code(jq); if (!jv_is_valid(exit_code)) ret = 0; else if (jv_get_kind(exit_code) == JV_KIND_NUMBER) ret = jv_number_value(exit_code); else ret = 5; jv_free(exit_code); jv error_message = jq_get_error_message(jq); if (jv_get_kind(error_message) == JV_KIND_STRING) { fprintf(stderr, "%s", jv_string_value(error_message)); } else if (jv_get_kind(error_message) == JV_KIND_NULL) { // Halt with no output } else if (jv_is_valid(error_message)) { error_message = jv_dump_string(jv_copy(error_message), 0); fprintf(stderr, "%s\n", jv_string_value(error_message)); } // else no message on stderr; use --debug-trace to see a message fflush(stderr); jv_free(error_message); } else 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 ARGS = jv_array(); /* positional arguments */ jv program_arguments = jv_object(); /* named arguments */ #ifdef WIN32 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_strings = 0; int further_args_are_json = 0; int args_done = 0; int jq_flags = 0; size_t short_opts = 0; jv lib_search_paths = jv_null(); for (int i=1; 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; 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; 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++; continue; } if (isoption(argv[i], 0, "seq", &short_opts)) { options |= SEQ; continue; } if (isoption(argv[i], 0, "stream", &short_opts)) { parser_flags |= JV_PARSE_STREAMING; continue; } if (isoption(argv[i], 0, "stream-errors", &short_opts)) { parser_flags |= JV_PARSE_STREAM_ERRORS; 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, "args", &short_opts)) { further_args_are_strings = 1; further_args_are_json = 0; continue; } if (isoption(argv[i], 0, "jsonargs", &short_opts)) { further_args_are_strings = 0; further_args_are_json = 1; continue; } 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(); } if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), jv_string(argv[i+2])); i += 2; // skip the next two arguments 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(); } if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) { jv v = jv_parse(argv[i+2]); if (!jv_is_valid(v)) { fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname); die(); } program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v); } i += 2; // skip the next two arguments continue; } if (isoption(argv[i], 0, "argfile", &short_opts) || isoption(argv[i], 0, "rawfile", &short_opts) || isoption(argv[i], 0, "slurpfile", &short_opts)) { int raw = isoption(argv[i], 0, "rawfile", &short_opts); const char *which; if (isoption(argv[i], 0, "argfile", &short_opts)) which = "argfile"; else if (raw) which = "rawfile"; else which = "slurpfile"; if (i >= argc - 2) { fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which); die(); } if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) { jv data = jv_load_file(argv[i+2], raw); 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); ret = 2; goto out; } if (strcmp(which, "argfile") == 0 && jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1) data = jv_array_get(data, 0); program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), data); } i += 2; // skip the next two arguments continue; } if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) { options |= DUMP_DISASM; continue; } if (isoption(argv[i], 0, "debug-trace=all", &short_opts)) { jq_flags |= JQ_DEBUG_TRACE_ALL; if (!short_opts) continue; } if (isoption(argv[i], 0, "debug-trace", &short_opts)) { jq_flags |= JQ_DEBUG_TRACE; continue; } if (isoption(argv[i], 'h', "help", &short_opts)) { usage(0, 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(); } } } #ifdef USE_ISATTY if (isatty(STDOUT_FILENO)) { 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 } #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 (getenv("JQ_COLORS") != NULL && !jq_set_colors(getenv("JQ_COLORS"))) fprintf(stderr, "Failed to set $JQ_COLORS\n"); 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)); #ifdef USE_ISATTY if (!program && (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO))) program = "."; #endif if (!program) usage(2, 1); 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)))); ARGS = JV_OBJECT(jv_string("positional"), ARGS, jv_string("named"), jv_copy(program_arguments)); program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS)); compiled = jq_compile_args(jq, skip_shebang(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? ARGS = JV_OBJECT(jv_string("positional"), ARGS, jv_string("named"), jv_copy(program_arguments)); program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS)); 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(ARGS); 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 && !(options & EXIT_STATUS_EXACT)) return 0; return ret; } jq-jq-1.6/src/jv_alloc.c0000600000175000017500000000776413366726451014457 0ustar czchenczchen#include #include #include #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_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 jq-jq-1.6/src/jv_alloc.h0000600000175000017500000000120013366726451014437 0ustar czchenczchen#ifndef JV_ALLOC_H #define JV_ALLOC_H #include #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 jq-jq-1.6/src/bytecode.c0000600000175000017500000001120413366726451014444 0ustar czchenczchen#include #include #include #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; inclosures; 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; insubfunctions; 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; icode[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; insubfunctions; 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); } jq-jq-1.6/src/jv_parse.c0000600000175000017500000006207613366726451014474 0ustar czchenczchen#include #include #include #include #include "jv.h" #include "jv_dtoa.h" #include "jv_unicode.h" #include "jv_alloc.h" #include "jv_dtoa.h" typedef const char* presult; #ifndef MAX_PARSING_DEPTH #define MAX_PARSING_DEPTH (256) #endif #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; istackpos; 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 (p->stackpos >= MAX_PARSING_DEPTH) return "Exceeds depth limit for parsing"; if (jv_is_valid(p->next)) return "Expected separator between values"; push(p, jv_array()); break; case '{': if (p->stackpos >= MAX_PARSING_DEPTH) return "Exceeds depth limit for parsing"; 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 - 1)) { 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; itokenbuf[i] != pattern[i]) return "Invalid literal"; TRY(value(p, v)); } else { // FIXME: better parser p->tokenbuf[p->tokenpos] = 0; 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)); } jq-jq-1.6/src/exec_stack.h0000600000175000017500000000641113366726451014770 0ustar czchenczchen#ifndef EXEC_STACK_H #define EXEC_STACK_H #include #include #include #include #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 jq-jq-1.6/src/inject_errors.c0000600000175000017500000000552313366726451015525 0ustar czchenczchen #define _GNU_SOURCE /* for RTLD_NEXT */ #include #include #include #include #include #include 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-jq-1.6/src/jq_test.c0000600000175000017500000002536613366726451014335 0ustar czchenczchen#include #include #include #include #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); } } jq-jq-1.6/src/lexer.h0000600000175000017500000002162013366726451013775 0ustar czchenczchen#ifndef jq_yyHEADER_H #define jq_yyHEADER_H 1 #define jq_yyIN_HEADER 1 #line 6 "src/lexer.h" #line 8 "src/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 6 #define YY_FLEX_SUBMINOR_VERSION 0 #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 #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . 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 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,yy_size_t 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(yyscanner) (/*CONSTCOND*/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 #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 ); yy_size_t 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 ); int jq_yyget_column (yyscan_t yyscanner ); void jq_yyset_column (int _column_no ,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 130 "src/lexer.l" #line 365 "src/lexer.h" #undef jq_yyIN_HEADER #endif /* jq_yyHEADER_H */ jq-jq-1.6/src/builtin.h0000600000175000017500000000022013366726451014315 0ustar czchenczchen#ifndef BUILTIN_H #define BUILTIN_H #include "jq.h" #include "bytecode.h" #include "compile.h" int builtins_bind(jq_state *, block*); #endif jq-jq-1.6/src/opcode_list.h0000600000175000017500000000224613366726451015165 0ustar czchenczchenOP(LOADK, CONSTANT, 1, 1) OP(DUP, NONE, 1, 2) OP(DUPN, NONE, 1, 2) OP(DUP2, NONE, 2, 3) OP(PUSHK_UNDER, CONSTANT, 1, 2) 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) OP(DESTRUCTURE_ALT, BRANCH, 0, 0) OP(STOREVN, VARIABLE, 1, 0) jq-jq-1.6/src/linker.h0000600000175000017500000000024113366726451014136 0ustar czchenczchen#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 jq-jq-1.6/src/jv_dtoa.h0000600000175000017500000000113313366726451014301 0ustar czchenczchen#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 jq-jq-1.6/src/jv_unicode.c0000600000175000017500000000752313366726451015004 0ustar czchenczchen#include #include #include "jv_unicode.h" #include "jv_utf8_tables.h" // jvp_utf8_backtrack returns the beginning of the last codepoint in the // string, assuming that start is the last byte in the string. // If the last codepoint is incomplete, returns the number of missing bytes via // *missing_bytes. If there are no leading bytes or an invalid byte is // encountered, NULL is returned and *missing_bytes is not altered. const char* jvp_utf8_backtrack(const char* start, const char* min, int *missing_bytes) { assert(min <= start); if (min == start) { return min; } int length = 0; int seen = 1; while (start >= min && (length = utf8_coding_length[(unsigned char)*start]) == UTF8_CONTINUATION_BYTE) { start--; seen++; } if (length == 0 || length == UTF8_CONTINUATION_BYTE || length - seen < 0) { return NULL; } if (missing_bytes) *missing_bytes = length - seen; return start; } 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 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; } jq-jq-1.6/src/compile.c0000600000175000017500000013045113366726451014304 0ustar czchenczchen#ifndef _GNU_SOURCE #define _GNU_SOURCE // for strdup #endif #include #include #include #include #include #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); } block gen_op_pushk_under(jv constant) { assert(opcode_describe(PUSHK_UNDER)->flags & OP_HAS_CONSTANT); inst* i = inst_new(PUSHK_UNDER); i->imm.constant = constant; return inst_block(i); } int block_is_const(block b) { return (block_is_single(b) && (b.first->op == LOADK || b.first->op == PUSHK_UNDER)); } 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; } block gen_dictpair(block k, block v) { return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT)); } 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; } jv block_list_funcs(block body, int omit_underscores) { jv funcs = jv_object(); // Use the keys for set semantics. for (inst *pos = body.first; pos != NULL; pos = pos->next) { if (pos->op == CLOSURE_CREATE || pos->op == CLOSURE_CREATE_C) { if (pos->symbol != NULL && (!omit_underscores || pos->symbol[0] != '_')) { funcs = jv_object_set(funcs, jv_string_fmt("%s/%i", pos->symbol, pos->nformals), jv_null()); } } } return jv_keys_unsorted(funcs); } 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) { if (block_is_noop(a)) { return gen_op_simple(DUP); } if (block_is_single(a) && a.first->op == LOADK) { jv c = block_const(a); block_free(a); return gen_op_pushk_under(c); } 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 == PUSHK_UNDER) { k = jv_copy(i->imm.constant); i = i->next; } else 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; } else { k = jv_copy(i->next->imm.constant); i = i->next->next->next; } if (i != NULL && i->op == PUSHK_UNDER) { v = jv_copy(i->imm.constant); i = i->next; } else 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; } else { v = jv_copy(i->next->imm.constant); i = i->next->next->next; } if (i == NULL || i->op != INSERT) { is_const = 0; break; } if (jv_get_kind(k) != JV_KIND_STRING) { 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->op == STOREVN) && !i->bound_by) block_bind_subblock(inst_block(i), body, OP_HAS_VARIABLE, 0); } return BLOCK(matcher, body); } // Extract destructuring var names from the block // *vars should be a jv_object (for set semantics) static void block_get_unbound_vars(block b, jv *vars) { assert(vars != NULL); assert(jv_get_kind(*vars) == JV_KIND_OBJECT); for (inst* i = b.first; i; i = i->next) { if (i->subfn.first) { block_get_unbound_vars(i->subfn, vars); continue; } if ((i->op == STOREV || i->op == STOREVN) && i->bound_by == NULL) { *vars = jv_object_set(*vars, jv_string(i->symbol), jv_true()); } } } /* Build wrappers around destructuring matchers so that we can chain them * when we have errors. The approach is as follows: * DESTRUCTURE_ALT NEXT_MATCHER (unless last matcher) * existing_matcher_block * JUMP BODY */ static block bind_alternation_matchers(block matchers, block body) { block preamble = {0}; block altmatchers = {0}; block mb = {0}; block final_matcher = matchers; // Pass through the matchers to find all destructured names. while (final_matcher.first && final_matcher.first->op == DESTRUCTURE_ALT) { block_append(&altmatchers, inst_block(block_take(&final_matcher))); } // We don't have any alternations here, so we can use the simplest case. if (altmatchers.first == NULL) { return bind_matcher(final_matcher, body); } // Collect var names jv all_vars = jv_object(); block_get_unbound_vars(altmatchers, &all_vars); block_get_unbound_vars(final_matcher, &all_vars); // We need a preamble of STOREVs to which to bind the matchers and the body. jv_object_keys_foreach(all_vars, key) { preamble = BLOCK(preamble, gen_op_simple(DUP), gen_const(jv_null()), gen_op_unbound(STOREV, jv_string_value(key))); jv_free(key); } jv_free(all_vars); // Now we build each matcher in turn for (inst *i = altmatchers.first; i; i = i->next) { block submatcher = i->subfn; // If we're successful, jump to the end of the matchers submatcher = BLOCK(submatcher, gen_op_target(JUMP, final_matcher)); // DESTRUCTURE_ALT to the end of this submatcher so we can skip to the next one on error mb = BLOCK(mb, gen_op_target(DESTRUCTURE_ALT, submatcher), submatcher); // We're done with this inst and we don't want it anymore // But we can't let it free the submatcher block. i->subfn.first = i->subfn.last = NULL; } // We're done with these insts now. block_free(altmatchers); return bind_matcher(preamble, BLOCK(mb, final_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_alternation_matchers(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_alternation_matchers(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_destructure_alt(block matcher) { for (inst *i = matcher.first; i; i = i->next) { if (i->op == STOREV) { i->op = STOREVN; } } inst* i = inst_new(DESTRUCTURE_ALT); i->subfn = matcher; return inst_block(i); } 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 != NULL); inst *i = NULL; if (left.first->next->op == PUSHK_UNDER) { i = left.first->next; } else { assert(left.first->next->op == SUBEXP_BEGIN); assert(left.first->next->next->op == LOADK); i = left.first->next->next; } index = 1 + (int) jv_number_value(i->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 matchers, 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)); if (matchers.first && matchers.first->op == DESTRUCTURE_ALT) { block_append(&var, gen_op_simple(DUP)); } else { top = BLOCK(top, gen_op_simple(DUP)); } return BLOCK(top, gen_subexp(var), gen_op_simple(POP), bind_alternation_matchers(matchers, 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), BLOCK(gen_subexp(cond), gen_op_simple(POP)), 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
* * JUMP * * * 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 produces no value then FORK_OPT * will backtrack (propagate the `empty`, as it were. If * 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; cfuncimm.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; } #ifndef WIN32 extern char **environ; #endif static jv make_env(jv env) { if (jv_is_valid(env)) return jv_copy(env); jv r = jv_object(); if (environ == NULL) return r; for (size_t i = 0; environ[i] != NULL; i++) { const char *eq; if ((eq = strchr(environ[i], '=')) == NULL) r = jv_object_delete(r, jv_string(environ[i])); else r = jv_object_set(r, jv_string_sized(environ[i], eq - environ[i]), jv_string(eq + 1)); } return jv_copy(r); } // Expands call instructions into a calling sequence static int expand_call_arglist(block* b, jv args, jv *env) { 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 && curr->op == LOADV && strcmp(curr->symbol, "ENV") == 0) { curr->op = LOADK; *env = curr->imm.constant = make_env(*env); } else if (!curr->bound_by && curr->op == LOADV && jv_object_has(jv_copy(args), jv_string(curr->symbol))) { curr->op = LOADK; curr->imm.constant = jv_object_get(jv_copy(args), jv_string(curr->symbol)); } else 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 if (curr->op == LOADV) locfile_locate(curr->locfile, curr->source, "jq: error: $%s is not defined", curr->symbol); 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, args, env); 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, jv args, jv *env) { int errors = 0; int pos = 0; int var_frame_idx = 0; bc->nsubfunctions = 0; errors += expand_call_arglist(&b, args, env); 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_calloc(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, args, env); curr->subfn = gen_noop(); } } } else { bc->subfunctions = 0; } uint16_t* code = jv_mem_calloc(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, jv args) { 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_calloc(sizeof(struct cfunction), ncfunc); bc->globals->cfunc_names = jv_array(); bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null()); jv env = jv_invalid(); int nerrors = compile(bc, b, lf, args, &env); jv_free(args); jv_free(env); 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); } } jq-jq-1.6/src/compile.h0000600000175000017500000000727413366726451014317 0ustar czchenczchen#ifndef COMPILE_H #define COMPILE_H #include #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_op_pushk_under(jv constant); 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_dictpair(block k, block v); 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_destructure_alt(block matcher); 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); jv block_list_funcs(block body, int omit_underscores); int block_compile(block, struct bytecode**, struct locfile*, jv); 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 jq-jq-1.6/src/jv_utf8_tables.h0000600000175000017500000000644713366726451015607 0ustar czchenczchen#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}; jq-jq-1.6/src/parser.c0000600000175000017500000041446513366726451014162 0ustar czchenczchen/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 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 . */ /* 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.4" /* 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 "src/parser.y" /* yacc.c:339 */ #include #include #include #include #include "compile.h" #include "jv_alloc.h" #define YYMALLOC jv_mem_alloc #define YYFREE jv_mem_free #line 77 "src/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_SRC_PARSER_H_INCLUDED # define YY_YY_SRC_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* "%code requires" blocks. */ #line 11 "src/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 "src/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, ALTERNATION = 295, QQSTRING_START = 296, QQSTRING_TEXT = 297, QQSTRING_INTERP_START = 298, QQSTRING_INTERP_END = 299, QQSTRING_END = 300, FUNCDEF = 301, NONOPT = 302 }; #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 ALTERNATION 295 #define QQSTRING_START 296 #define QQSTRING_TEXT 297 #define QQSTRING_INTERP_START 298 #define QQSTRING_INTERP_END 299 #define QQSTRING_END 300 #define FUNCDEF 301 #define NONOPT 302 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 31 "src/parser.y" /* yacc.c:355 */ jv literal; block blk; #line 235 "src/parser.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; # 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_SRC_PARSER_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 124 "src/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; } /* Returns string message if the block is a constant that is not valid as an * object key. */ static jv check_object_key(block k) { if (block_is_const(k) && block_const_kind(k) != JV_KIND_STRING) { char errbuf[15]; return jv_string_fmt("Cannot use %s (%s) as object key", jv_kind_name(block_const_kind(k)), jv_dump_string_trunc(jv_copy(block_const(k)), errbuf, sizeof(errbuf))); } return jv_invalid(); } 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 438 "src/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 /* 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 /* 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 /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* 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 /* 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 1972 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 69 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 30 /* YYNRULES -- Number of rules. */ #define YYNRULES 162 /* YYNSTATES -- Number of states. */ #define YYNSTATES 313 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 302 #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, 62, 56, 2, 2, 60, 61, 54, 52, 48, 53, 64, 55, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 63, 59, 50, 49, 51, 58, 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, 65, 2, 66, 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, 67, 47, 68, 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, 46, 57 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 300, 300, 303, 308, 311, 322, 325, 330, 333, 338, 342, 345, 349, 353, 357, 360, 365, 369, 373, 378, 385, 389, 393, 397, 401, 405, 409, 413, 417, 421, 425, 429, 433, 437, 441, 445, 449, 455, 461, 465, 469, 473, 477, 481, 485, 489, 493, 498, 501, 518, 527, 534, 542, 553, 558, 564, 567, 572, 577, 584, 584, 588, 588, 595, 598, 601, 607, 610, 615, 618, 621, 627, 630, 633, 641, 645, 648, 651, 654, 657, 660, 663, 666, 669, 673, 679, 682, 685, 688, 691, 694, 697, 700, 703, 706, 709, 712, 715, 718, 721, 724, 727, 734, 738, 742, 754, 759, 760, 761, 762, 765, 768, 773, 778, 781, 786, 789, 794, 798, 801, 806, 809, 814, 817, 822, 825, 828, 831, 834, 837, 845, 851, 854, 857, 860, 863, 866, 869, 872, 875, 878, 881, 884, 887, 890, 893, 896, 899, 902, 905, 910, 913, 914, 915, 918, 921, 924, 927, 931, 935, 939, 947 }; #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", "RepPatterns", "Patterns", "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, 301, 124, 44, 61, 60, 62, 43, 45, 42, 47, 37, 302, 63, 59, 40, 41, 36, 58, 46, 91, 93, 123, 125 }; # endif #define YYPACT_NINF -158 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-158))) #define YYTABLE_NINF -152 #define yytable_value_is_error(Yytable_value) \ (!!((Yytable_value) == (-152))) /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int16 yypact[] = { 21, 772, 43, 63, -6, 12, -158, 80, -158, 122, 772, 193, 193, 772, 74, 5, -158, 772, 522, 10, 279, 455, 152, 1290, 772, -158, 8, -158, 3, 3, 772, 63, 680, 772, -158, -158, 67, 1646, 58, 130, 106, 133, -158, 135, -158, 20, 83, 1120, -158, -158, -158, 140, 80, 93, 86, -158, 917, -23, 91, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, 772, 151, 94, 97, 95, 113, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, -158, -158, 1814, 104, -7, 3, 388, 171, -158, -158, -158, 1814, 772, -158, -158, 1341, 1814, 59, -158, -158, 7, 772, 587, -7, -7, 652, 117, -158, 4, -158, -158, -158, -158, -158, -158, 345, -3, -158, -3, 1154, -158, -3, -3, -158, 345, 1882, 370, 370, 1848, 436, 1914, 1882, 1882, 1882, 1882, 1882, 1882, 370, 370, 1814, 1848, 1882, 370, 370, 20, 20, 129, 129, 129, -158, 184, -7, 834, 149, 143, 156, 134, 136, 772, 145, 867, 47, -158, -158, 772, -158, 23, -158, 200, 72, -158, 1392, -158, 1596, 146, 150, -158, -158, 772, -158, 772, -158, -11, -158, -3, 162, 51, 162, 148, 162, 162, -158, -158, -158, -24, 153, 154, 772, 209, 155, -15, -158, 158, -7, 772, -158, -158, 967, -158, 744, 157, -158, 215, -158, -158, -158, 7, 159, -158, 772, 772, -158, 772, 772, 1814, 1680, -158, -3, -3, -7, -158, -7, -7, 1188, 163, -7, 834, -158, -7, 185, 1814, 169, 170, 1017, -158, -158, -158, 772, 1730, 1780, 1443, 1494, -158, 162, 162, -158, -158, -158, 166, -7, -158, -158, -158, -158, -158, 172, 1545, -158, 772, 772, 772, -7, -158, -158, -158, 1596, 1222, 1067, -158, -158, -158, 772, -158, 1256, -158 }; /* 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, 160, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 0, 0, 158, 0, 0, 152, 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, 0, 110, 0, 0, 159, 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, 0, 117, 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, 154, 0, 162, 71, 155, 0, 157, 156, 153, 118, 121, 0, 0, 0, 0, 0, 0, 0, 123, 0, 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, 119, 0, 0, 0, 125, 0, 0, 120, 0, 116, 11, 95, 94, 0, 86, 50, 57, 0, 0, 0, 0, 0, 66, 69, 161, 122, 131, 127, 0, 0, 129, 124, 128, 92, 91, 93, 0, 68, 0, 0, 0, 0, 126, 90, 55, 0, 0, 0, 130, 67, 12, 0, 14, 0, 13 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -158, -158, -158, 201, 115, -1, -158, -158, 204, -8, -158, -5, 6, -158, -158, 110, -65, -131, -4, -158, 48, -158, 16, -149, -158, -158, -22, -157, -104, -158 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 3, 30, 118, 110, 31, 32, 115, 24, 197, 198, 25, 44, 127, 136, 249, 213, 26, 125, 126, 181, 182, 183, 222, 228, 229, 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, 4, 5, 6, 7, 8, 42, 38, 39, 37, 52, 195, 40, 111, 48, 215, 45, 47, 217, 218, 56, 112, 120, 230, 257, 143, 53, 15, 80, 119, 221, 123, 124, 264, 116, 116, 1, 143, 16, 211, 144, 49, 258, 27, 16, 145, 207, 208, 219, 209, 212, 239, 144, 265, 33, 178, 111, 18, 179, 19, 180, 20, 21, 111, 22, 207, 208, 43, 242, 196, 34, 131, 113, 114, 105, 106, 107, 147, 108, 28, 29, 254, 267, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 230, 283, 240, 284, 285, 120, 188, 288, 113, 114, 290, 192, 184, 193, 35, 113, 114, 281, 282, 36, 128, 199, 201, 129, 244, 205, 245, 133, 111, 41, 134, 300, 135, 214, 139, 214, 132, 137, 214, 214, 202, 203, 80, 306, 140, 141, 57, 146, 148, 58, 149, 80, 52, 150, 152, 177, 151, 206, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 189, 235, 227, 108, 220, 231, 232, 124, 233, 16, 113, 114, -115, 4, 5, 6, 7, 8, 234, 236, 243, 252, 250, 253, 214, 255, 251, 256, 78, 262, 79, 272, 259, 260, 263, 273, -151, 266, 275, 15, 261, -114, 287, 291, 292, 299, 301, 268, 121, 117, 16, 190, 271, 194, 307, 274, 241, 0, 289, 0, 0, 0, 276, 277, 0, 278, 279, 214, 214, 18, 0, 19, 0, 20, 21, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 0, 0, 0, 294, 0, 0, 0, 0, -72, 50, 0, 0, 51, -72, 0, 52, 0, -72, -72, -72, -72, -72, 0, 0, 303, 304, 305, -72, -72, -72, 0, 0, -72, -72, -72, 0, -72, 311, 0, 0, -72, -72, -72, -72, -72, -72, -72, -72, 0, 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, 210, -72, 0, 58, 0, 0, 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, 0, 0, -152, -152, 0, 0, 0, 0, 16, 0, 0, 185, 0, 0, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 9, 0, 0, 78, 10, 79, -152, -152, 11, 12, 0, -151, 0, 13, 0, 14, 15, 0, -152, -152, 103, 104, 105, 106, 107, 0, 108, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 85, 86, 18, 0, 19, 186, 20, 21, 187, 22, 54, 0, 0, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 96, 97, 0, 11, 12, 0, 0, 0, 13, 0, 14, 15, 101, 102, 103, 104, 105, 106, 107, 0, 108, 0, 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, 55, 22, 46, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 200, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 204, 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, 0, 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, 0, 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, 0, 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, 270, 22, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 18, 0, 19, 223, 20, 21, 224, 22, 0, 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, 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, 225, 0, 226, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 0, 0, 0, 0, 0, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 84, 85, 86, 87, 237, 0, 0, 238, 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, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 84, 85, 86, 87, 0, 0, 0, 269, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 84, 85, 86, 87, 0, 0, 0, 293, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 309, 0, 310, 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, 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, 0, 84, 85, 86, 87, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 88, 89, 216, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 0, 0, 84, 85, 86, 87, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 88, 89, 286, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 0, 0, 84, 85, 86, 87, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 88, 89, 308, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 0, 0, 84, 85, 86, 87, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 88, 89, 312, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 191, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 246, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 297, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 298, 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, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 302, 84, 85, 86, 87, 0, 0, 0, 0, 0, 0, 0, 247, 248, 0, 0, 0, 88, 89, 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 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, 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, 0, 280, 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, 295, 88, 89, 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 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, 296, 0, 0, 0, 0, 0, 88, 89, 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 0, 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, 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, -152, 85, 86, 0, 0, 0, 100, 101, 102, 103, 104, 105, 106, 107, 0, 108, 88, 89, 0, 0, 0, 0, 0, -152, -152, -152, -152, -152, -152, 96, 97, 0, 0, 85, 86, 0, 0, 0, 0, 0, -152, 101, 102, 103, 104, 105, 106, 107, 88, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 102, 103, 104, 105, 106, 107, 0, 108 }; static const yytype_int16 yycheck[] = { 1, 4, 5, 6, 7, 8, 1, 11, 12, 10, 7, 4, 13, 5, 4, 146, 17, 18, 149, 150, 21, 13, 30, 180, 48, 48, 20, 30, 22, 30, 179, 32, 33, 48, 28, 29, 15, 48, 41, 143, 63, 31, 66, 0, 41, 68, 42, 43, 152, 45, 53, 4, 63, 68, 60, 62, 5, 60, 65, 62, 67, 64, 65, 5, 67, 42, 43, 62, 45, 62, 58, 13, 64, 65, 54, 55, 56, 78, 58, 16, 17, 212, 231, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 264, 257, 62, 259, 260, 120, 114, 263, 64, 65, 266, 59, 113, 61, 41, 64, 65, 255, 256, 4, 60, 129, 130, 63, 59, 133, 61, 28, 5, 62, 4, 287, 4, 144, 1, 146, 13, 61, 149, 150, 131, 132, 143, 299, 58, 66, 1, 63, 4, 4, 63, 152, 7, 63, 48, 58, 68, 47, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 13, 186, 180, 58, 4, 40, 47, 192, 58, 41, 64, 65, 40, 4, 5, 6, 7, 8, 66, 58, 4, 206, 60, 208, 212, 47, 60, 63, 60, 4, 62, 58, 63, 63, 63, 4, 68, 63, 63, 30, 225, 40, 63, 58, 58, 63, 58, 232, 31, 29, 41, 120, 237, 127, 303, 244, 192, -1, 264, -1, -1, -1, 247, 248, -1, 250, 251, 255, 256, 60, -1, 62, -1, 64, 65, -1, 67, -1, -1, -1, -1, -1, -1, -1, -1, -1, 264, -1, -1, -1, 275, -1, -1, -1, -1, 0, 1, -1, -1, 4, 5, -1, 7, -1, 9, 10, 11, 12, 13, -1, -1, 296, 297, 298, 19, 20, 21, -1, -1, 24, 25, 26, -1, 28, 309, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, 41, -1, -1, 44, -1, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, -1, 61, -1, 63, 64, 65, 66, 1, 68, -1, 4, -1, -1, 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, -1, -1, 10, 11, -1, -1, -1, -1, 41, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, 14, -1, -1, 60, 18, 62, 38, 39, 22, 23, -1, 68, -1, 27, -1, 29, 30, -1, 50, 51, 52, 53, 54, 55, 56, -1, 58, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, 10, 11, 60, -1, 62, 63, 64, 65, 66, 67, 1, -1, -1, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, 14, -1, -1, -1, 18, 38, 39, -1, 22, 23, -1, -1, -1, 27, -1, 29, 30, 50, 51, 52, 53, 54, 55, 56, -1, 58, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, -1, -1, 60, -1, 62, -1, 64, 65, 66, 67, 1, -1, -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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, -1, -1, 60, -1, 62, -1, 64, 65, 1, 67, -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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, -1, -1, 60, -1, 62, -1, 64, 65, 1, 67, -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, -1, 4, 5, 6, 7, 8, -1, -1, -1, -1, 41, 14, -1, -1, -1, 18, -1, -1, -1, 22, 23, -1, 53, -1, 27, -1, 29, 30, -1, 60, -1, 62, -1, 64, 65, -1, 67, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, 60, -1, 62, -1, 64, 65, -1, 67, 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, -1, 4, 5, 6, 7, 8, -1, -1, -1, -1, 41, 14, -1, -1, -1, 18, -1, -1, -1, 22, 23, -1, 53, -1, 27, -1, 29, 30, -1, 60, -1, 62, -1, 64, 65, 66, 67, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, -1, -1, 60, -1, 62, 1, 64, 65, 4, 67, -1, 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, -1, -1, -1, -1, -1, -1, -1, -1, 41, 9, 10, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25, 26, 60, -1, 62, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 9, 10, 11, 12, 63, -1, -1, 66, -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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 9, 10, 11, 12, -1, -1, -1, 66, -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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 9, 10, 11, 12, -1, -1, -1, 66, -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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 9, 10, 11, 12, -1, -1, -1, 66, -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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, -1, 61, 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, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, 61, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, 61, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, 61, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, 61, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, 61, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 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, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, -1, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, 44, -1, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 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, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 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, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, -1, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, 12, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, -1, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, 9, 10, 11, -1, -1, -1, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 25, 26, -1, -1, -1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, 10, 11, -1, -1, -1, -1, -1, 49, 50, 51, 52, 53, 54, 55, 56, 25, 58, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 51, 52, 53, 54, 55, 56, -1, 58 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 15, 70, 71, 4, 5, 6, 7, 8, 14, 18, 22, 23, 27, 29, 30, 41, 53, 60, 62, 64, 65, 67, 74, 78, 81, 87, 0, 16, 17, 72, 75, 76, 60, 58, 41, 4, 74, 87, 87, 74, 62, 1, 62, 82, 74, 1, 74, 4, 31, 1, 4, 7, 81, 1, 66, 74, 1, 4, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 60, 62, 81, 96, 97, 98, 9, 10, 11, 12, 25, 26, 32, 33, 34, 35, 36, 37, 38, 39, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 74, 5, 13, 64, 65, 77, 81, 77, 73, 74, 78, 72, 59, 74, 74, 88, 89, 83, 60, 63, 19, 13, 13, 28, 4, 4, 84, 61, 61, 1, 58, 66, 66, 48, 63, 68, 63, 74, 4, 63, 63, 68, 48, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 58, 62, 65, 67, 90, 91, 92, 81, 1, 63, 66, 74, 13, 73, 59, 59, 61, 84, 4, 62, 79, 80, 74, 1, 74, 91, 91, 1, 74, 47, 42, 43, 45, 1, 97, 53, 86, 87, 86, 61, 86, 86, 97, 4, 92, 93, 1, 4, 60, 62, 81, 94, 95, 96, 40, 47, 58, 66, 74, 58, 63, 66, 4, 62, 89, 45, 4, 59, 61, 59, 20, 21, 85, 60, 60, 74, 74, 86, 47, 63, 48, 66, 63, 63, 74, 4, 63, 48, 68, 63, 92, 74, 66, 66, 74, 58, 4, 80, 63, 74, 74, 74, 74, 44, 86, 86, 92, 92, 92, 61, 63, 92, 95, 92, 58, 58, 66, 74, 24, 19, 59, 59, 63, 92, 58, 59, 74, 74, 74, 92, 85, 61, 59, 61, 74, 61 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 76, 76, 76, 77, 78, 78, 79, 79, 80, 80, 82, 81, 83, 81, 84, 84, 84, 85, 85, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 92, 92, 93, 93, 94, 94, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98 }; /* 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, 3, 1, 3, 1, 2, 3, 3, 1, 3, 1, 3, 2, 4, 3, 3, 3, 5, 3, 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, 3 }; #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 /* 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 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 1893 "src/parser.c" /* yacc.c:1257 */ break; case 5: /* FIELD */ #line 36 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 1899 "src/parser.c" /* yacc.c:1257 */ break; case 6: /* LITERAL */ #line 36 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 1905 "src/parser.c" /* yacc.c:1257 */ break; case 7: /* FORMAT */ #line 36 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 1911 "src/parser.c" /* yacc.c:1257 */ break; case 42: /* QQSTRING_TEXT */ #line 36 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 1917 "src/parser.c" /* yacc.c:1257 */ break; case 71: /* Module */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1923 "src/parser.c" /* yacc.c:1257 */ break; case 72: /* Imports */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1929 "src/parser.c" /* yacc.c:1257 */ break; case 73: /* FuncDefs */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1935 "src/parser.c" /* yacc.c:1257 */ break; case 74: /* Exp */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1941 "src/parser.c" /* yacc.c:1257 */ break; case 75: /* Import */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1947 "src/parser.c" /* yacc.c:1257 */ break; case 76: /* ImportWhat */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1953 "src/parser.c" /* yacc.c:1257 */ break; case 77: /* ImportFrom */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1959 "src/parser.c" /* yacc.c:1257 */ break; case 78: /* FuncDef */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1965 "src/parser.c" /* yacc.c:1257 */ break; case 79: /* Params */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1971 "src/parser.c" /* yacc.c:1257 */ break; case 80: /* Param */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1977 "src/parser.c" /* yacc.c:1257 */ break; case 81: /* String */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1983 "src/parser.c" /* yacc.c:1257 */ break; case 84: /* QQString */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1989 "src/parser.c" /* yacc.c:1257 */ break; case 85: /* ElseBody */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 1995 "src/parser.c" /* yacc.c:1257 */ break; case 86: /* ExpD */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2001 "src/parser.c" /* yacc.c:1257 */ break; case 87: /* Term */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2007 "src/parser.c" /* yacc.c:1257 */ break; case 88: /* Args */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2013 "src/parser.c" /* yacc.c:1257 */ break; case 89: /* Arg */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2019 "src/parser.c" /* yacc.c:1257 */ break; case 90: /* RepPatterns */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2025 "src/parser.c" /* yacc.c:1257 */ break; case 91: /* Patterns */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2031 "src/parser.c" /* yacc.c:1257 */ break; case 92: /* Pattern */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2037 "src/parser.c" /* yacc.c:1257 */ break; case 93: /* ArrayPats */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2043 "src/parser.c" /* yacc.c:1257 */ break; case 94: /* ObjPats */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2049 "src/parser.c" /* yacc.c:1257 */ break; case 95: /* ObjPat */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2055 "src/parser.c" /* yacc.c:1257 */ break; case 96: /* Keyword */ #line 36 "src/parser.y" /* yacc.c:1257 */ { jv_free(((*yyvaluep).literal)); } #line 2061 "src/parser.c" /* yacc.c:1257 */ break; case 97: /* MkDict */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2067 "src/parser.c" /* yacc.c:1257 */ break; case 98: /* MkDictPair */ #line 37 "src/parser.y" /* yacc.c:1257 */ { block_free(((*yyvaluep).blk)); } #line 2073 "src/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 300 "src/parser.y" /* yacc.c:1646 */ { *answer = BLOCK((yyvsp[-2].blk), (yyvsp[-1].blk), gen_op_simple(TOP), (yyvsp[0].blk)); } #line 2369 "src/parser.c" /* yacc.c:1646 */ break; case 3: #line 303 "src/parser.y" /* yacc.c:1646 */ { *answer = BLOCK((yyvsp[-2].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); } #line 2377 "src/parser.c" /* yacc.c:1646 */ break; case 4: #line 308 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 2385 "src/parser.c" /* yacc.c:1646 */ break; case 5: #line 311 "src/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 2399 "src/parser.c" /* yacc.c:1646 */ break; case 6: #line 322 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 2407 "src/parser.c" /* yacc.c:1646 */ break; case 7: #line 325 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-1].blk), (yyvsp[0].blk)); } #line 2415 "src/parser.c" /* yacc.c:1646 */ break; case 8: #line 330 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 2423 "src/parser.c" /* yacc.c:1646 */ break; case 9: #line 333 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_bind((yyvsp[-1].blk), (yyvsp[0].blk), OP_IS_CALL_PSEUDO); } #line 2431 "src/parser.c" /* yacc.c:1646 */ break; case 10: #line 338 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_bind_referenced((yyvsp[-1].blk), (yyvsp[0].blk), OP_IS_CALL_PSEUDO); } #line 2439 "src/parser.c" /* yacc.c:1646 */ break; case 11: #line 342 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_destructure((yyvsp[-4].blk), (yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2447 "src/parser.c" /* yacc.c:1646 */ break; case 12: #line 345 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_reduce((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk)); } #line 2455 "src/parser.c" /* yacc.c:1646 */ break; case 13: #line 349 "src/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 2463 "src/parser.c" /* yacc.c:1646 */ break; case 14: #line 353 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_foreach((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), gen_noop()); } #line 2471 "src/parser.c" /* yacc.c:1646 */ break; case 15: #line 357 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); } #line 2479 "src/parser.c" /* yacc.c:1646 */ break; case 16: #line 360 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "Possibly unterminated 'if' statement"); (yyval.blk) = (yyvsp[-2].blk); } #line 2488 "src/parser.c" /* yacc.c:1646 */ break; case 17: #line 365 "src/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 2497 "src/parser.c" /* yacc.c:1646 */ break; case 18: #line 369 "src/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 2506 "src/parser.c" /* yacc.c:1646 */ break; case 19: #line 373 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "Possibly unterminated 'try' statement"); (yyval.blk) = (yyvsp[-2].blk); } #line 2515 "src/parser.c" /* yacc.c:1646 */ break; case 20: #line 378 "src/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 2526 "src/parser.c" /* yacc.c:1646 */ break; case 21: #line 385 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_try((yyvsp[-1].blk), gen_op_simple(BACKTRACK)); } #line 2534 "src/parser.c" /* yacc.c:1646 */ break; case 22: #line 389 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_call("_assign", BLOCK(gen_lambda((yyvsp[-2].blk)), gen_lambda((yyvsp[0].blk)))); } #line 2542 "src/parser.c" /* yacc.c:1646 */ break; case 23: #line 393 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_or((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2550 "src/parser.c" /* yacc.c:1646 */ break; case 24: #line 397 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_and((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2558 "src/parser.c" /* yacc.c:1646 */ break; case 25: #line 401 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_definedor((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2566 "src/parser.c" /* yacc.c:1646 */ break; case 26: #line 405 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_definedor_assign((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2574 "src/parser.c" /* yacc.c:1646 */ break; case 27: #line 409 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_call("_modify", BLOCK(gen_lambda((yyvsp[-2].blk)), gen_lambda((yyvsp[0].blk)))); } #line 2582 "src/parser.c" /* yacc.c:1646 */ break; case 28: #line 413 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2590 "src/parser.c" /* yacc.c:1646 */ break; case 29: #line 417 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_both((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2598 "src/parser.c" /* yacc.c:1646 */ break; case 30: #line 421 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '+'); } #line 2606 "src/parser.c" /* yacc.c:1646 */ break; case 31: #line 425 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '+'); } #line 2614 "src/parser.c" /* yacc.c:1646 */ break; case 32: #line 429 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop())); } #line 2622 "src/parser.c" /* yacc.c:1646 */ break; case 33: #line 433 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '-'); } #line 2630 "src/parser.c" /* yacc.c:1646 */ break; case 34: #line 437 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '-'); } #line 2638 "src/parser.c" /* yacc.c:1646 */ break; case 35: #line 441 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '*'); } #line 2646 "src/parser.c" /* yacc.c:1646 */ break; case 36: #line 445 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '*'); } #line 2654 "src/parser.c" /* yacc.c:1646 */ break; case 37: #line 449 "src/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 2664 "src/parser.c" /* yacc.c:1646 */ break; case 38: #line 455 "src/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 2674 "src/parser.c" /* yacc.c:1646 */ break; case 39: #line 461 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '/'); } #line 2682 "src/parser.c" /* yacc.c:1646 */ break; case 40: #line 465 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '%'); } #line 2690 "src/parser.c" /* yacc.c:1646 */ break; case 41: #line 469 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), EQ); } #line 2698 "src/parser.c" /* yacc.c:1646 */ break; case 42: #line 473 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), NEQ); } #line 2706 "src/parser.c" /* yacc.c:1646 */ break; case 43: #line 477 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '<'); } #line 2714 "src/parser.c" /* yacc.c:1646 */ break; case 44: #line 481 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '>'); } #line 2722 "src/parser.c" /* yacc.c:1646 */ break; case 45: #line 485 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), LESSEQ); } #line 2730 "src/parser.c" /* yacc.c:1646 */ break; case 46: #line 489 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), GREATEREQ); } #line 2738 "src/parser.c" /* yacc.c:1646 */ break; case 47: #line 493 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 2746 "src/parser.c" /* yacc.c:1646 */ break; case 48: #line 498 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-1].blk); } #line 2754 "src/parser.c" /* yacc.c:1646 */ break; case 49: #line 501 "src/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 2774 "src/parser.c" /* yacc.c:1646 */ break; case 50: #line 518 "src/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 2788 "src/parser.c" /* yacc.c:1646 */ break; case 51: #line 527 "src/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 2800 "src/parser.c" /* yacc.c:1646 */ break; case 52: #line 534 "src/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 2811 "src/parser.c" /* yacc.c:1646 */ break; case 53: #line 542 "src/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 2825 "src/parser.c" /* yacc.c:1646 */ break; case 54: #line 553 "src/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 2834 "src/parser.c" /* yacc.c:1646 */ break; case 55: #line 558 "src/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 2843 "src/parser.c" /* yacc.c:1646 */ break; case 56: #line 564 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 2851 "src/parser.c" /* yacc.c:1646 */ break; case 57: #line 567 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2859 "src/parser.c" /* yacc.c:1646 */ break; case 58: #line 572 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_param_regular(jv_string_value((yyvsp[0].literal))); jv_free((yyvsp[0].literal)); } #line 2868 "src/parser.c" /* yacc.c:1646 */ break; case 59: #line 577 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_param(jv_string_value((yyvsp[0].literal))); jv_free((yyvsp[0].literal)); } #line 2877 "src/parser.c" /* yacc.c:1646 */ break; case 60: #line 584 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("text"); } #line 2883 "src/parser.c" /* yacc.c:1646 */ break; case 61: #line 584 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-1].blk); jv_free((yyvsp[-2].literal)); } #line 2892 "src/parser.c" /* yacc.c:1646 */ break; case 62: #line 588 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = (yyvsp[-1].literal); } #line 2898 "src/parser.c" /* yacc.c:1646 */ break; case 63: #line 588 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-1].blk); jv_free((yyvsp[-2].literal)); } #line 2907 "src/parser.c" /* yacc.c:1646 */ break; case 64: #line 595 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_const(jv_string("")); } #line 2915 "src/parser.c" /* yacc.c:1646 */ break; case 65: #line 598 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-1].blk), gen_const((yyvsp[0].literal)), '+'); } #line 2923 "src/parser.c" /* yacc.c:1646 */ break; case 66: #line 601 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_binop((yyvsp[-3].blk), gen_format((yyvsp[-1].blk), jv_copy((yyvsp[-4].literal))), '+'); } #line 2931 "src/parser.c" /* yacc.c:1646 */ break; case 67: #line 607 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); } #line 2939 "src/parser.c" /* yacc.c:1646 */ break; case 68: #line 610 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-1].blk); } #line 2947 "src/parser.c" /* yacc.c:1646 */ break; case 69: #line 615 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 2955 "src/parser.c" /* yacc.c:1646 */ break; case 70: #line 618 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop())); } #line 2963 "src/parser.c" /* yacc.c:1646 */ break; case 71: #line 621 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 2971 "src/parser.c" /* yacc.c:1646 */ break; case 72: #line 627 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 2979 "src/parser.c" /* yacc.c:1646 */ break; case 73: #line 630 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_call("recurse", gen_noop()); } #line 2987 "src/parser.c" /* yacc.c:1646 */ break; case 74: #line 633 "src/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 3000 "src/parser.c" /* yacc.c:1646 */ break; case 75: #line 641 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "break requires a label to break to"); (yyval.blk) = gen_noop(); } #line 3009 "src/parser.c" /* yacc.c:1646 */ break; case 76: #line 645 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index_opt((yyvsp[-2].blk), gen_const((yyvsp[-1].literal))); } #line 3017 "src/parser.c" /* yacc.c:1646 */ break; case 77: #line 648 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index_opt(gen_noop(), gen_const((yyvsp[-1].literal))); } #line 3025 "src/parser.c" /* yacc.c:1646 */ break; case 78: #line 651 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index_opt((yyvsp[-3].blk), (yyvsp[-1].blk)); } #line 3033 "src/parser.c" /* yacc.c:1646 */ break; case 79: #line 654 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index_opt(gen_noop(), (yyvsp[-1].blk)); } #line 3041 "src/parser.c" /* yacc.c:1646 */ break; case 80: #line 657 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index((yyvsp[-1].blk), gen_const((yyvsp[0].literal))); } #line 3049 "src/parser.c" /* yacc.c:1646 */ break; case 81: #line 660 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index(gen_noop(), gen_const((yyvsp[0].literal))); } #line 3057 "src/parser.c" /* yacc.c:1646 */ break; case 82: #line 663 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3065 "src/parser.c" /* yacc.c:1646 */ break; case 83: #line 666 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index(gen_noop(), (yyvsp[0].blk)); } #line 3073 "src/parser.c" /* yacc.c:1646 */ break; case 84: #line 669 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields"); (yyval.blk) = gen_noop(); } #line 3082 "src/parser.c" /* yacc.c:1646 */ break; case 85: #line 673 "src/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 3092 "src/parser.c" /* yacc.c:1646 */ break; case 86: #line 679 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index_opt((yyvsp[-4].blk), (yyvsp[-2].blk)); } #line 3100 "src/parser.c" /* yacc.c:1646 */ break; case 87: #line 682 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_index((yyvsp[-3].blk), (yyvsp[-1].blk)); } #line 3108 "src/parser.c" /* yacc.c:1646 */ break; case 88: #line 685 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH_OPT)); } #line 3116 "src/parser.c" /* yacc.c:1646 */ break; case 89: #line 688 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = block_join((yyvsp[-2].blk), gen_op_simple(EACH)); } #line 3124 "src/parser.c" /* yacc.c:1646 */ break; case 90: #line 691 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-6].blk), (yyvsp[-4].blk), (yyvsp[-2].blk), INDEX_OPT); } #line 3132 "src/parser.c" /* yacc.c:1646 */ break; case 91: #line 694 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), gen_const(jv_null()), INDEX_OPT); } #line 3140 "src/parser.c" /* yacc.c:1646 */ break; case 92: #line 697 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-5].blk), gen_const(jv_null()), (yyvsp[-2].blk), INDEX_OPT); } #line 3148 "src/parser.c" /* yacc.c:1646 */ break; case 93: #line 700 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), INDEX); } #line 3156 "src/parser.c" /* yacc.c:1646 */ break; case 94: #line 703 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-4].blk), (yyvsp[-2].blk), gen_const(jv_null()), INDEX); } #line 3164 "src/parser.c" /* yacc.c:1646 */ break; case 95: #line 706 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_slice_index((yyvsp[-4].blk), gen_const(jv_null()), (yyvsp[-1].blk), INDEX); } #line 3172 "src/parser.c" /* yacc.c:1646 */ break; case 96: #line 709 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_const((yyvsp[0].literal)); } #line 3180 "src/parser.c" /* yacc.c:1646 */ break; case 97: #line 712 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3188 "src/parser.c" /* yacc.c:1646 */ break; case 98: #line 715 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_format(gen_noop(), (yyvsp[0].literal)); } #line 3196 "src/parser.c" /* yacc.c:1646 */ break; case 99: #line 718 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-1].blk); } #line 3204 "src/parser.c" /* yacc.c:1646 */ break; case 100: #line 721 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_collect((yyvsp[-1].blk)); } #line 3212 "src/parser.c" /* yacc.c:1646 */ break; case 101: #line 724 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_const(jv_array()); } #line 3220 "src/parser.c" /* yacc.c:1646 */ break; case 102: #line 727 "src/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 3232 "src/parser.c" /* yacc.c:1646 */ break; case 103: #line 734 "src/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 3241 "src/parser.c" /* yacc.c:1646 */ break; case 104: #line 738 "src/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 3250 "src/parser.c" /* yacc.c:1646 */ break; case 105: #line 742 "src/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 3267 "src/parser.c" /* yacc.c:1646 */ break; case 106: #line 754 "src/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 3277 "src/parser.c" /* yacc.c:1646 */ break; case 107: #line 759 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 3283 "src/parser.c" /* yacc.c:1646 */ break; case 108: #line 760 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 3289 "src/parser.c" /* yacc.c:1646 */ break; case 109: #line 761 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[-3].blk); } #line 3295 "src/parser.c" /* yacc.c:1646 */ break; case 110: #line 762 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_noop(); } #line 3301 "src/parser.c" /* yacc.c:1646 */ break; case 111: #line 765 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3309 "src/parser.c" /* yacc.c:1646 */ break; case 112: #line 768 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3317 "src/parser.c" /* yacc.c:1646 */ break; case 113: #line 773 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_lambda((yyvsp[0].blk)); } #line 3325 "src/parser.c" /* yacc.c:1646 */ break; case 114: #line 778 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-2].blk), gen_destructure_alt((yyvsp[0].blk))); } #line 3333 "src/parser.c" /* yacc.c:1646 */ break; case 115: #line 781 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_destructure_alt((yyvsp[0].blk)); } #line 3341 "src/parser.c" /* yacc.c:1646 */ break; case 116: #line 786 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3349 "src/parser.c" /* yacc.c:1646 */ break; case 117: #line 789 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3357 "src/parser.c" /* yacc.c:1646 */ break; case 118: #line 794 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal))); jv_free((yyvsp[0].literal)); } #line 3366 "src/parser.c" /* yacc.c:1646 */ break; case 119: #line 798 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); } #line 3374 "src/parser.c" /* yacc.c:1646 */ break; case 120: #line 801 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); } #line 3382 "src/parser.c" /* yacc.c:1646 */ break; case 121: #line 806 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_array_matcher(gen_noop(), (yyvsp[0].blk)); } #line 3390 "src/parser.c" /* yacc.c:1646 */ break; case 122: #line 809 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_array_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3398 "src/parser.c" /* yacc.c:1646 */ break; case 123: #line 814 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3406 "src/parser.c" /* yacc.c:1646 */ break; case 124: #line 817 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3414 "src/parser.c" /* yacc.c:1646 */ break; case 125: #line 822 "src/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 3422 "src/parser.c" /* yacc.c:1646 */ break; case 126: #line 825 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), BLOCK(gen_op_simple(DUP), gen_op_unbound(STOREV, jv_string_value((yyvsp[-2].literal))), (yyvsp[0].blk))); } #line 3430 "src/parser.c" /* yacc.c:1646 */ break; case 127: #line 828 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); } #line 3438 "src/parser.c" /* yacc.c:1646 */ break; case 128: #line 831 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); } #line 3446 "src/parser.c" /* yacc.c:1646 */ break; case 129: #line 834 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_object_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3454 "src/parser.c" /* yacc.c:1646 */ break; case 130: #line 837 "src/parser.y" /* yacc.c:1646 */ { jv msg = check_object_key((yyvsp[-3].blk)); if (jv_is_valid(msg)) { FAIL((yyloc), jv_string_value(msg)); } jv_free(msg); (yyval.blk) = gen_object_matcher((yyvsp[-3].blk), (yyvsp[0].blk)); } #line 3467 "src/parser.c" /* yacc.c:1646 */ break; case 131: #line 845 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "May need parentheses around object key expression"); (yyval.blk) = (yyvsp[0].blk); } #line 3476 "src/parser.c" /* yacc.c:1646 */ break; case 132: #line 851 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("as"); } #line 3484 "src/parser.c" /* yacc.c:1646 */ break; case 133: #line 854 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("def"); } #line 3492 "src/parser.c" /* yacc.c:1646 */ break; case 134: #line 857 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("module"); } #line 3500 "src/parser.c" /* yacc.c:1646 */ break; case 135: #line 860 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("import"); } #line 3508 "src/parser.c" /* yacc.c:1646 */ break; case 136: #line 863 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("include"); } #line 3516 "src/parser.c" /* yacc.c:1646 */ break; case 137: #line 866 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("if"); } #line 3524 "src/parser.c" /* yacc.c:1646 */ break; case 138: #line 869 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("then"); } #line 3532 "src/parser.c" /* yacc.c:1646 */ break; case 139: #line 872 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("else"); } #line 3540 "src/parser.c" /* yacc.c:1646 */ break; case 140: #line 875 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("elif"); } #line 3548 "src/parser.c" /* yacc.c:1646 */ break; case 141: #line 878 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("reduce"); } #line 3556 "src/parser.c" /* yacc.c:1646 */ break; case 142: #line 881 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("foreach"); } #line 3564 "src/parser.c" /* yacc.c:1646 */ break; case 143: #line 884 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("end"); } #line 3572 "src/parser.c" /* yacc.c:1646 */ break; case 144: #line 887 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("and"); } #line 3580 "src/parser.c" /* yacc.c:1646 */ break; case 145: #line 890 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("or"); } #line 3588 "src/parser.c" /* yacc.c:1646 */ break; case 146: #line 893 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("try"); } #line 3596 "src/parser.c" /* yacc.c:1646 */ break; case 147: #line 896 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("catch"); } #line 3604 "src/parser.c" /* yacc.c:1646 */ break; case 148: #line 899 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("label"); } #line 3612 "src/parser.c" /* yacc.c:1646 */ break; case 149: #line 902 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("break"); } #line 3620 "src/parser.c" /* yacc.c:1646 */ break; case 150: #line 905 "src/parser.y" /* yacc.c:1646 */ { (yyval.literal) = jv_string("__loc__"); } #line 3628 "src/parser.c" /* yacc.c:1646 */ break; case 151: #line 910 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk)=gen_noop(); } #line 3636 "src/parser.c" /* yacc.c:1646 */ break; case 152: #line 913 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3642 "src/parser.c" /* yacc.c:1646 */ break; case 153: #line 914 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk)=block_join((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3648 "src/parser.c" /* yacc.c:1646 */ break; case 154: #line 915 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = (yyvsp[0].blk); } #line 3654 "src/parser.c" /* yacc.c:1646 */ break; case 155: #line 918 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); } #line 3662 "src/parser.c" /* yacc.c:1646 */ break; case 156: #line 921 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); } #line 3670 "src/parser.c" /* yacc.c:1646 */ break; case 157: #line 924 "src/parser.y" /* yacc.c:1646 */ { (yyval.blk) = gen_dictpair((yyvsp[-2].blk), (yyvsp[0].blk)); } #line 3678 "src/parser.c" /* yacc.c:1646 */ break; case 158: #line 927 "src/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 3687 "src/parser.c" /* yacc.c:1646 */ break; case 159: #line 931 "src/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 3696 "src/parser.c" /* yacc.c:1646 */ break; case 160: #line 935 "src/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 3705 "src/parser.c" /* yacc.c:1646 */ break; case 161: #line 939 "src/parser.y" /* yacc.c:1646 */ { jv msg = check_object_key((yyvsp[-3].blk)); if (jv_is_valid(msg)) { FAIL((yyloc), jv_string_value(msg)); } jv_free(msg); (yyval.blk) = gen_dictpair((yyvsp[-3].blk), (yyvsp[0].blk)); } #line 3718 "src/parser.c" /* yacc.c:1646 */ break; case 162: #line 947 "src/parser.y" /* yacc.c:1646 */ { FAIL((yyloc), "May need parentheses around object key expression"); (yyval.blk) = (yyvsp[0].blk); } #line 3727 "src/parser.c" /* yacc.c:1646 */ break; #line 3731 "src/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 951 "src/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; } jq-jq-1.6/src/parser.h0000600000175000017500000001150513366726451014153 0ustar czchenczchen/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 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 . */ /* 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_SRC_PARSER_H_INCLUDED # define YY_YY_SRC_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* "%code requires" blocks. */ #line 11 "src/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 "src/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, ALTERNATION = 295, QQSTRING_START = 296, QQSTRING_TEXT = 297, QQSTRING_INTERP_START = 298, QQSTRING_INTERP_END = 299, QQSTRING_END = 300, FUNCDEF = 301, NONOPT = 302 }; #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 ALTERNATION 295 #define QQSTRING_START 296 #define QQSTRING_TEXT 297 #define QQSTRING_INTERP_START 298 #define QQSTRING_INTERP_END 299 #define QQSTRING_END 300 #define FUNCDEF 301 #define NONOPT 302 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 31 "src/parser.y" /* yacc.c:1909 */ jv literal; block blk; #line 172 "src/parser.h" /* yacc.c:1909 */ }; typedef union YYSTYPE YYSTYPE; # 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_SRC_PARSER_H_INCLUDED */ jq-jq-1.6/src/util.h0000600000175000017500000000300413366726451013627 0ustar czchenczchen#ifndef UTIL_H #define UTIL_H #ifdef WIN32 /* For WriteFile() below */ #include #include #include #include #include #include #endif #include "jv.h" #ifndef HAVE_MKSTEMP int mkstemp(char *template); #endif jv expand_path(jv); jv get_home(void); jv jq_realpath(jv); /* * The Windows CRT and console are something else. In order for the * console to get UTF-8 written to it correctly we have to bypass stdio * completely. No amount of fflush()ing helps. If the first byte of a * buffer being written with fwrite() is non-ASCII UTF-8 then the * console misinterprets the byte sequence. But one must not * WriteFile() if stdout is a file!1!! * * We carry knowledge of whether the FILE * is a tty everywhere we * output to it just so we can write with WriteFile() if stdout is a * console on WIN32. */ static void priv_fwrite(const char *s, size_t len, FILE *fout, int is_tty) { #ifdef WIN32 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 } const void *_jq_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #ifndef MIN #define MIN(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) #endif #ifndef MAX #define MAX(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; }) #endif #endif /* UTIL_H */ jq-jq-1.6/src/libm.h0000600000175000017500000000732613366726451013610 0ustar czchenczchen#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_DDD(jn) #endif #ifdef HAVE_YN LIBM_DDD(yn) #endif #ifdef HAVE_CEIL LIBM_DD(ceil) #else LIBM_DD_NO(ceil) #endif #ifdef HAVE_COPYSIGN LIBM_DDD(copysign) #else LIBM_DDD_NO(copysign) #endif #if defined(HAVE_DREM) && !defined(WIN32) LIBM_DDD(drem) #else LIBM_DDD_NO(drem) #endif #ifdef HAVE_ERF LIBM_DD(erf) #else LIBM_DD_NO(erf) #endif #ifdef HAVE_ERFC LIBM_DD(erfc) #else LIBM_DD_NO(erfc) #endif #if defined(HAVE_EXP10) && !defined(WIN32) LIBM_DD(exp10) #else LIBM_DD_NO(exp10) #endif #ifdef HAVE_EXPM1 LIBM_DD(expm1) #else LIBM_DD_NO(expm1) #endif #ifdef HAVE_FABS LIBM_DD(fabs) #else LIBM_DD_NO(fabs) #endif #ifdef HAVE_FDIM LIBM_DDD(fdim) #else LIBM_DDD_NO(fdim) #endif #ifdef HAVE_FMA LIBM_DDDD(fma) #else LIBM_DDDD_NO(fma) #endif #ifdef HAVE_FMAX LIBM_DDD(fmax) #else LIBM_DDD_NO(fmax) #endif #ifdef HAVE_FMIN LIBM_DDD(fmin) #else LIBM_DDD_NO(fmin) #endif #ifdef HAVE_FMOD LIBM_DDD(fmod) #else LIBM_DDD_NO(fmod) #endif #ifdef HAVE_GAMMA LIBM_DD(gamma) #else LIBM_DD_NO(gamma) #endif #ifdef HAVE_LGAMMA LIBM_DD(lgamma) #else LIBM_DD_NO(lgamma) #endif #ifdef HAVE_LOG1P LIBM_DD(log1p) #else LIBM_DD_NO(log1p) #endif #ifdef HAVE_LOGB LIBM_DD(logb) #else LIBM_DD_NO(logb) #endif #ifdef HAVE_NEARBYINT LIBM_DD(nearbyint) #else LIBM_DD_NO(nearbyint) #endif #ifdef HAVE_NEXTAFTER LIBM_DDD(nextafter) #else LIBM_DDD_NO(nextafter) #endif #ifdef HAVE_NEXTTOWARD LIBM_DDD(nexttoward) #else LIBM_DDD_NO(nexttoward) #endif #if defined(HAVE_POW10) && !defined(WIN32) LIBM_DD(pow10) #else LIBM_DD_NO(pow10) #endif #ifdef HAVE_RINT LIBM_DD(rint) #else LIBM_DD_NO(rint) #endif #ifdef HAVE_ROUND LIBM_DD(round) #else LIBM_DD_NO(round) #endif #ifdef HAVE_SCALB LIBM_DDD(scalb) #else LIBM_DDD_NO(scalb) #endif #ifdef HAVE_SCALBLN LIBM_DDD(scalbln) #else LIBM_DDD_NO(scalbln) #endif #if defined(HAVE_SIGNIFICAND) && !defined(WIN32) LIBM_DD(significand) #else LIBM_DD_NO(significand) #endif #ifdef HAVE_TRUNC LIBM_DD(trunc) #else LIBM_DD_NO(trunc) #endif #ifdef HAVE_LDEXP LIBM_DDD(ldexp) #else LIBM_DDD_NO(ldexp) #endif jq-jq-1.6/src/jv_file.c0000600000175000017500000000450513366726451014272 0ustar czchenczchen#include #include #include #include #include #include #include #include "jv.h" #include "jv_unicode.h" jv jv_load_file(const char* filename, int raw) { struct stat sb; int fd = open(filename, O_RDONLY); if (fd == -1) { return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", filename, strerror(errno))); } if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode)) { close(fd); return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s", filename, "It's a directory")); } FILE* file = fdopen(fd, "r"); struct jv_parser* parser = NULL; jv data; if (!file) { close(fd); 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); } // To avoid mangling UTF-8 multi-byte sequences that cross the end of our read // buffer, we need to be able to read the remainder of a sequence and add that // before appending. const int max_utf8_len = 4; char buf[4096+max_utf8_len]; while (!feof(file) && !ferror(file)) { size_t n = fread(buf, 1, sizeof(buf)-max_utf8_len, file); int len = 0; if (n == 0) continue; if (jvp_utf8_backtrack(buf+(n-1), buf, &len) && len > 0 && !feof(file) && !ferror(file)) { n += fread(buf+n, 1, len, file); } if (raw) { data = jv_string_append_buf(data, buf, 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; } jq-jq-1.6/src/builtin.c0000600000175000017500000016014513366726451014325 0ustar czchenczchen#define _BSD_SOURCE #define _GNU_SOURCE #ifndef __sun__ # define _XOPEN_SOURCE # define _XOPEN_SOURCE_EXTENDED 1 #else # define _XPG6 # define __EXTENSIONS__ #endif #include #include #include #ifdef HAVE_ALLOCA_H # include #elif !defined alloca # ifdef __GNUC__ # define alloca __builtin_alloca # elif defined _MSC_VER # include # define alloca _alloca # elif !defined HAVE_ALLOCA # ifdef __cplusplus extern "C" # endif void *alloca (size_t); # endif #endif #include #include #include #include #ifdef HAVE_LIBONIG #include #endif #include #include #include "builtin.h" #include "compile.h" #include "jq_parser.h" #include "bytecode.h" #include "linker.h" #include "locfile.h" #include "jv_unicode.h" #include "jv_alloc.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 inline jv ret_error(jv bad, jv msg) { jv_free(bad); return jv_invalid_with_msg(msg); } static inline jv ret_error2(jv bad1, jv bad2, jv msg) { jv_free(bad1); jv_free(bad2); return jv_invalid_with_msg(msg); } 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) { \ jv_free(input); \ if (jv_get_kind(a) != JV_KIND_NUMBER) { \ jv_free(b); \ return type_error(a, "number required"); \ } \ if (jv_get_kind(b) != JV_KIND_NUMBER) { \ jv_free(a); \ return type_error(b, "number required"); \ } \ 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) #define LIBM_DDDD(name) \ static jv f_ ## name(jq_state *jq, jv input, jv a, jv b, jv c) { \ jv_free(input); \ if (jv_get_kind(a) != JV_KIND_NUMBER) { \ jv_free(b); \ jv_free(c); \ return type_error(a, "number required"); \ } \ if (jv_get_kind(b) != JV_KIND_NUMBER) { \ jv_free(a); \ jv_free(c); \ return type_error(b, "number required"); \ } \ if (jv_get_kind(c) != JV_KIND_NUMBER) { \ jv_free(a); \ jv_free(b); \ return type_error(c, "number required"); \ } \ jv ret = jv_number(name(jv_number_value(a), jv_number_value(b), jv_number_value(c))); \ jv_free(a); \ jv_free(b); \ jv_free(c); \ return ret; \ } #define LIBM_DDDD_NO(name) #include "libm.h" #undef LIBM_DDDD_NO #undef LIBM_DDD_NO #undef LIBM_DD_NO #undef LIBM_DDDD #undef LIBM_DDD #undef LIBM_DD #ifdef HAVE_FREXP static jv f_frexp(jq_state *jq, jv input) { if (jv_get_kind(input) != JV_KIND_NUMBER) { return type_error(input, "number required"); } int exp; double d = frexp(jv_number_value(input), &exp); jv ret = JV_ARRAY(jv_number(d), jv_number(exp)); jv_free(input); return ret; } #endif #ifdef HAVE_MODF static jv f_modf(jq_state *jq, jv input) { if (jv_get_kind(input) != JV_KIND_NUMBER) { return type_error(input, "number required"); } double i; jv ret = JV_ARRAY(jv_number(modf(jv_number_value(input), &i))); jv_free(input); return jv_array_append(ret, jv_number(i)); } #endif #ifdef HAVE_LGAMMA_R static jv f_lgamma_r(jq_state *jq, jv input) { if (jv_get_kind(input) != JV_KIND_NUMBER) { return type_error(input, "number required"); } int sign; jv ret = JV_ARRAY(jv_number(lgamma_r(jv_number_value(input), &sign))); jv_free(input); return jv_array_append(ret, jv_number(sign)); } #endif 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 ret_error2(a, b, 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 ret_error2(a, b, 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); } } static jv f_utf8bytelength(jq_state *jq, jv input) { if (jv_get_kind(input) != JV_KIND_STRING) return type_error(input, "only strings have UTF-8 byte length"); return jv_number(jv_string_length_bytes(input)); } #define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" static const unsigned char BASE64_ENCODE_TABLE[64 + 1] = CHARS_ALPHANUM "+/"; static const unsigned char BASE64_INVALID_ENTRY = 0xFF; static const unsigned char BASE64_DECODE_TABLE[255] = { 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, 62, // + 0xFF, 0xFF, 0xFF, 63, // / 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9 0xFF, 0xFF, 0xFF, 99, // = 0xFF, 0xFF, 0xFF, 0, 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, // A-Z 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // a-z 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, 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, 0xFF, 0xFF, 0xFF, 0xFF }; static jv escape_string(jv input, const char* escapings) { assert(jv_get_kind(input) == JV_KIND_STRING); const char* lookup[128] = {0}; const char* p = escapings; lookup[0] = "\\0"; while (*p) { lookup[(int)*p] = p+1; p++; p += strlen(p); p++; } jv ret = jv_string(""); const char* i = jv_string_value(input); const char* end = i + jv_string_length_bytes(jv_copy(input)); const char* cstart; int c = 0; while ((i = jvp_utf8_next((cstart = i), end, &c))) { if (c < 128 && lookup[c]) { ret = jv_string_append_str(ret, lookup[c]); } else { ret = jv_string_append_buf(ret, cstart, i - cstart); } } jv_free(input); return ret; } static jv f_format(jq_state *jq, jv input, jv fmt) { if (jv_get_kind(fmt) != JV_KIND_STRING) { jv_free(input); return type_error(fmt, "is not a valid format"); } const char* fmt_s = jv_string_value(fmt); if (!strcmp(fmt_s, "json")) { jv_free(fmt); return jv_dump_string(input, 0); } else if (!strcmp(fmt_s, "text")) { jv_free(fmt); return f_tostring(jq, input); } else if (!strcmp(fmt_s, "csv") || !strcmp(fmt_s, "tsv")) { const char *quotes, *sep, *escapings; const char *msg; if (!strcmp(fmt_s, "csv")) { msg = "cannot be csv-formatted, only array"; quotes = "\""; sep = ","; escapings = "\"\"\"\0"; } else { msg = "cannot be tsv-formatted, only array"; assert(!strcmp(fmt_s, "tsv")); quotes = ""; sep = "\t"; escapings = "\t\\t\0\r\\r\0\n\\n\0\\\\\\\0"; } jv_free(fmt); if (jv_get_kind(input) != JV_KIND_ARRAY) return type_error(input, msg); jv line = jv_string(""); jv_array_foreach(input, i, x) { if (i) line = jv_string_append_str(line, sep); switch (jv_get_kind(x)) { case JV_KIND_NULL: /* null rendered as empty string */ jv_free(x); break; case JV_KIND_TRUE: case JV_KIND_FALSE: line = jv_string_concat(line, jv_dump_string(x, 0)); break; case JV_KIND_NUMBER: if (jv_number_value(x) != jv_number_value(x)) { /* NaN, render as empty string */ jv_free(x); } else { line = jv_string_concat(line, jv_dump_string(x, 0)); } break; case JV_KIND_STRING: { line = jv_string_append_str(line, quotes); line = jv_string_concat(line, escape_string(x, escapings)); line = jv_string_append_str(line, quotes); break; } default: jv_free(input); jv_free(line); return type_error(x, "is not valid in a csv row"); } } jv_free(input); return line; } else if (!strcmp(fmt_s, "html")) { jv_free(fmt); return escape_string(f_tostring(jq, input), "&&\0<<\0>>\0''\0\""\0"); } else if (!strcmp(fmt_s, "uri")) { jv_free(fmt); input = f_tostring(jq, input); int unreserved[128] = {0}; const char* p = CHARS_ALPHANUM "-_.!~*'()"; while (*p) unreserved[(int)*p++] = 1; jv line = jv_string(""); const char* s = jv_string_value(input); for (int i=0; i= 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] = BASE64_ENCODE_TABLE[(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 if (!strcmp(fmt_s, "base64d")) { jv_free(fmt); input = f_tostring(jq, input); const unsigned char* data = (const unsigned char*)jv_string_value(input); int len = jv_string_length_bytes(jv_copy(input)); size_t decoded_len = (3 * len) / 4; // 3 usable bytes for every 4 bytes of input char *result = jv_mem_calloc(decoded_len, sizeof(char)); memset(result, 0, decoded_len * sizeof(char)); uint32_t ri = 0; int input_bytes_read=0; uint32_t code = 0; for (int i=0; i> 16) & 0xFF; result[ri++] = (code >> 8) & 0xFF; result[ri++] = code & 0xFF; input_bytes_read = 0; code = 0; } } if (input_bytes_read == 3) { result[ri++] = (code >> 10) & 0xFF; result[ri++] = (code >> 2) & 0xFF; } else if (input_bytes_read == 2) { result[ri++] = (code >> 4) & 0xFF; } else if (input_bytes_read == 1) { free(result); return type_error(input, "trailing base64 byte found"); } jv line = jv_string_sized(result, ri); jv_free(input); free(result); 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_LIBONIG static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups, int *groups, regex_t *reg, void *arg) { jv captures = *(jv*)arg; for (int i = 0; i < ngroups; ++i) { jv cap = jv_array_get(jv_copy(captures),groups[i]-1); if (jv_get_kind(cap) == JV_KIND_OBJECT) { cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name)); captures = jv_array_set(captures,groups[i]-1,cap); } else { jv_free(cap); } } *(jv *)arg = captures; return 0; } static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) { int test = jv_equal(testmode, jv_true()); jv result; int onigret; int global = 0; regex_t *reg; OnigErrorInfo einfo; OnigRegion* region; if (jv_get_kind(input) != JV_KIND_STRING) { jv_free(regex); jv_free(modifiers); return type_error(input, "cannot be matched, as it is not a string"); } if (jv_get_kind(regex) != JV_KIND_STRING) { jv_free(input); jv_free(modifiers); return type_error(regex, "is not a string"); } OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP; if (jv_get_kind(modifiers) == JV_KIND_STRING) { jv modarray = jv_string_explode(jv_copy(modifiers)); jv_array_foreach(modarray, i, mod) { switch ((int)jv_number_value(mod)) { case 'g': global = 1; break; case 'i': options |= ONIG_OPTION_IGNORECASE; break; case 'x': options |= ONIG_OPTION_EXTEND; break; case 'm': options |= ONIG_OPTION_MULTILINE; break; case 's': options |= ONIG_OPTION_SINGLELINE; break; case 'p': options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE; break; case 'l': options |= ONIG_OPTION_FIND_LONGEST; break; case 'n': options |= ONIG_OPTION_FIND_NOT_EMPTY; break; default: jv_free(input); jv_free(regex); jv_free(modarray); return jv_invalid_with_msg(jv_string_concat(modifiers, jv_string(" is not a valid modifier string"))); } } jv_free(modarray); } else if (jv_get_kind(modifiers) != JV_KIND_NULL) { // If it isn't a string or null, then it is the wrong type... jv_free(input); jv_free(regex); return type_error(modifiers, "is not a string"); } jv_free(modifiers); onigret = onig_new(®, (const UChar*)jv_string_value(regex), (const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))), options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo); if (onigret != ONIG_NORMAL) { UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(ebuf, onigret, &einfo); jv_free(input); jv_free(regex); return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "), jv_string((char*)ebuf))); } result = test ? jv_false() : jv_array(); const char *input_string = jv_string_value(input); const UChar* start = (const UChar*)jv_string_value(input); const unsigned long length = jv_string_length_bytes(jv_copy(input)); const UChar* end = start + length; region = onig_region_new(); do { onigret = onig_search(reg, (const UChar*)jv_string_value(input), end, /* string boundaries */ start, end, /* search boundaries */ region, ONIG_OPTION_NONE); if (onigret >= 0) { if (test) { result = jv_true(); break; } // Zero-width match if (region->end[0] == region->beg[0]) { unsigned long idx; const char *fr = (const char*)input_string; for (idx = 0; fr != input_string+region->beg[0]; idx++) { fr += jvp_utf8_decode_length(*fr); } jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); match = jv_object_set(match, jv_string("length"), jv_number(0)); match = jv_object_set(match, jv_string("string"), jv_string("")); match = jv_object_set(match, jv_string("captures"), jv_array()); result = jv_array_append(result, match); start += 1; continue; } unsigned long idx; unsigned long len; const char *fr = (const char*)input_string; for (idx = len = 0; fr < input_string+region->end[0]; len++) { if (fr == input_string+region->beg[0]) idx = len, len=0; fr += jvp_utf8_decode_length(*fr); } jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); unsigned long blen = region->end[0]-region->beg[0]; match = jv_object_set(match, jv_string("length"), jv_number(len)); match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen)); jv captures = jv_array(); for (int i = 1; i < region->num_regs; ++i) { // Empty capture. if (region->beg[i] == region->end[i]) { // Didn't match. jv cap; if (region->beg[i] == -1) { cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1)); cap = jv_object_set(cap, jv_string("string"), jv_null()); } else { fr = input_string; for (idx = 0; fr != input_string+region->beg[i]; idx++) { fr += jvp_utf8_decode_length(*fr); } cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); cap = jv_object_set(cap, jv_string("string"), jv_string("")); } cap = jv_object_set(cap, jv_string("length"), jv_number(0)); cap = jv_object_set(cap, jv_string("name"), jv_null()); captures = jv_array_append(captures, cap); continue; } fr = input_string; for (idx = len = 0; fr != input_string+region->end[i]; len++) { if (fr == input_string+region->beg[i]) idx = len, len=0; fr += jvp_utf8_decode_length(*fr); } blen = region->end[i]-region->beg[i]; jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx)); cap = jv_object_set(cap, jv_string("length"), jv_number(len)); cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen)); cap = jv_object_set(cap, jv_string("name"), jv_null()); captures = jv_array_append(captures,cap); } onig_foreach_name(reg,f_match_name_iter,&captures); match = jv_object_set(match, jv_string("captures"), captures); result = jv_array_append(result, match); start = (const UChar*)(input_string+region->end[0]); onig_region_free(region,0); } else if (onigret == ONIG_MISMATCH) { break; } else { /* Error */ UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(ebuf, onigret, einfo); jv_free(result); result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "), jv_string((char*)ebuf))); break; } } while (global && start != end); onig_region_free(region,1); region = NULL; if (region) onig_region_free(region,1); onig_free(reg); jv_free(input); jv_free(regex); return result; } #else /* !HAVE_LIBONIG */ 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_LIBONIG */ 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; itm_gmtoff; #elif HAVE_TM___TM_GMT_OFF return t + tm->__tm_gmtoff; #else return (time_t)-2; /* Not supported */ #endif } /* Compute and set tm_wday */ static void set_tm_wday(struct tm *tm) { /* * https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Gauss.27s_algorithm * https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html * * Tested with dates from 1900-01-01 through 2100-01-01. This * algorithm produces the wrong day-of-the-week number for dates in * the range 1900-01-01..1900-02-28, and for 2100-01-01..2100-02-28. * Since this is only needed on OS X and *BSD, we might just document * this. */ int century = (1900 + tm->tm_year) / 100; int year = (1900 + tm->tm_year) % 100; if (tm->tm_mon < 2) year--; /* * The month value in the wday computation below is shifted so that * March is 1, April is 2, .., January is 11, and February is 12. */ int mon = tm->tm_mon - 1; if (mon < 1) mon += 12; int wday = (tm->tm_mday + (int)floor((2.6 * mon - 0.2)) + year + (int)floor(year / 4.0) + (int)floor(century / 4.0) - 2 * century) % 7; if (wday < 0) wday += 7; #if 0 /* See commentary above */ assert(wday == tm->tm_wday || tm->tm_wday == 8); #endif tm->tm_wday = wday; } /* * Compute and set tm_yday. * */ static void set_tm_yday(struct tm *tm) { static const int d[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; int mon = tm->tm_mon; int year = 1900 + tm->tm_year; int leap_day = 0; if (tm->tm_mon > 1 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) leap_day = 1; /* Bound check index into d[] */ if (mon < 0) mon = -mon; if (mon > 11) mon %= 12; int yday = d[mon] + leap_day + tm->tm_mday - 1; assert(yday == tm->tm_yday || tm->tm_yday == 367); tm->tm_yday = yday; } #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 ret_error2(a, b, jv_string("strptime/1 requires string inputs and arguments")); } struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_wday = 8; // sentinel tm.tm_yday = 367; // sentinel 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))) { return ret_error2(a, b, jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt)); } jv_free(b); /* * This is OS X or some *BSD whose strptime() is just not that * helpful! * * We don't know that the format string did involve parsing a * year, or a month (if tm->tm_mon == 0). But with our invalid * day-of-week and day-of-year sentinel checks above, the worst * this can do is produce garbage. */ #ifdef __APPLE__ /* * Apple has made it worse, and different versions of the OS have different * behaviors. Some versions just don't touch the fields, but others do, and * sometimes provide wrong answers, at that! We can't tell at compile-time * which behavior the target system will have, so instead we always use our * functions to set these on OS X, and document that %u and %j are * unsupported on OS X. */ set_tm_wday(&tm); set_tm_yday(&tm); #else if (tm.tm_wday == 8 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11) set_tm_wday(&tm); if (tm.tm_yday == 367 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11) set_tm_yday(&tm); #endif 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)) { \ jv_free(j); \ 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 ret_error(a, jv_string("mktime requires array inputs")); if (jv_array_length(jv_copy(a)) < 6) return ret_error(a, 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 ret_error(a, 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 ret_error(a, 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_LOCALTIME_R static jv f_localtime(jq_state *jq, jv a) { if (jv_get_kind(a) != JV_KIND_NUMBER) return ret_error(a, jv_string("localtime() 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 = localtime_r(&secs, &tm); if (tmp == NULL) return jv_invalid_with_msg(jv_string("error 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_localtime(jq_state *jq, jv a) { if (jv_get_kind(a) != JV_KIND_NUMBER) return ret_error(a, jv_string("localtime 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 = localtime(&secs); if (tmp == NULL) return jv_invalid_with_msg(jv_string("error 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_localtime(jq_state *jq, jv a) { jv_free(a); return jv_invalid_with_msg(jv_string("localtime 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 ret_error2(a, b, jv_string("strftime/1 requires parsed datetime inputs")); } else if (jv_get_kind(b) != JV_KIND_STRING) { return ret_error2(a, b, jv_string("strftime/1 requires a string format")); } struct tm tm; if (!jv2tm(a, &tm)) return ret_error(b, 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 b) { jv_free(a); jv_free(b); return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform")); } #endif #ifdef HAVE_STRFTIME static jv f_strflocaltime(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) == JV_KIND_NUMBER) { a = f_localtime(jq, a); } else if (jv_get_kind(a) != JV_KIND_ARRAY) { return ret_error2(a, b, jv_string("strflocaltime/1 requires parsed datetime inputs")); } else if (jv_get_kind(b) != JV_KIND_STRING) { return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format")); } struct tm tm; if (!jv2tm(a, &tm)) return jv_invalid_with_msg(jv_string("strflocaltime/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("strflocaltime/1: unknown system failure")); return jv_string(buf); } #else static jv f_strflocaltime(jq_state *jq, jv a, jv b) { jv_free(a); jv_free(b); return jv_invalid_with_msg(jv_string("strflocaltime/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 a) { jv_free(a); 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, jv a) { jv_free(a); 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) #define LIBM_DDDD(name) \ {(cfunction_ptr)f_ ## name, #name, 4}, #define LIBM_DDDD_NO(name) static const struct cfunction function_list[] = { #include "libm.h" #ifdef HAVE_FREXP {(cfunction_ptr)f_frexp,"frexp", 1}, #endif #ifdef HAVE_MODF {(cfunction_ptr)f_modf,"modf", 1}, #endif #ifdef HAVE_LGAMMA_R {(cfunction_ptr)f_lgamma_r,"lgamma_r", 1}, #endif {(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_utf8bytelength, "utf8bytelength", 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_halt, "halt", 1}, {(cfunction_ptr)f_halt_error, "halt_error", 2}, {(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_strflocaltime, "strflocaltime", 2}, {(cfunction_ptr)f_mktime, "mktime", 1}, {(cfunction_ptr)f_gmtime, "gmtime", 1}, {(cfunction_ptr)f_localtime, "localtime", 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_DDDD_NO #undef LIBM_DDD_NO #undef LIBM_DD_NO #undef LIBM_DDDD #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", code, strlen(code)); block funcs; int nerrors = jq_parse_library(src, &funcs); if (nerrors == 0) { *bb = block_bind(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) { block builtins = gen_noop(); int nerrors = slurp_lib(jq, bb); if (nerrors) { block_free(*bb); return nerrors; } nerrors = builtins_bind_one(jq, &builtins, jq_builtins); assert(!nerrors); builtins = bind_bytecoded_builtins(builtins); builtins = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), builtins); builtins = gen_builtin_list(builtins); *bb = block_bind(builtins, *bb, OP_IS_CALL_PSEUDO); *bb = block_drop_unreferenced(*bb); return nerrors; } jq-jq-1.6/src/bytecode.h0000600000175000017500000000315713366726451014461 0ustar czchenczchen#ifndef BYTECODE_H #define BYTECODE_H #include #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 jq-jq-1.6/src/locfile.h0000600000175000017500000000110213366726451014264 0ustar czchenczchen#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 jq-jq-1.6/src/linker.c0000600000175000017500000003255513366726451014146 0ustar czchenczchen#include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #endif #include "jq_parser.h" #include "locfile.h" #include "jv.h" #include "jq.h" #include "parser.h" #include "util.h" #include "compile.h" #include "jv_alloc.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); if (is_data) bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, NULL); } } 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 = jv_mem_realloc(lib_state->names, lib_state->ct * sizeof(const char *)); lib_state->defs = jv_mem_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; } jq-jq-1.6/src/lexer.l0000600000175000017500000001024513366726451014002 0ustar czchenczchen%{ #include #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 ALTERNATION; } "."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { 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; } { "\\(" { 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); } jq-jq-1.6/src/jv_print.c0000600000175000017500000002706713366726451014517 0ustar czchenczchen#include #include #include #include #ifdef WIN32 #include #include #include #endif #include "jv.h" #include "jv_dtoa.h" #include "jv_unicode.h" #include "jv_alloc.h" #ifndef MAX_PRINT_DEPTH #define MAX_PRINT_DEPTH (256) #endif #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 char color_bufs[sizeof(color_kinds)/sizeof(color_kinds[0])][16]; static const char *color_bufps[8]; static const char* def_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 const char **colors = def_colors; int jq_set_colors(const char *c) { const char *e; size_t i; if (c == NULL) return 1; colors = def_colors; memset(color_bufs, 0, sizeof(color_bufs)); for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]); i++) color_bufps[i] = def_colors[i]; for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]) && *c != '\0'; i++, c = e) { if ((e = strchr(c, ':')) == NULL) e = c + strlen(c); if ((size_t)(e - c) > sizeof(color_bufs[i]) - 4 /* ESC [ m NUL */) return 0; color_bufs[i][0] = ESC[0]; color_bufs[i][1] = '['; (void) strncpy(&color_bufs[i][2], c, e - c); if (strspn(&color_bufs[i][2], "0123456789;") < strlen(&color_bufs[i][2])) return 0; color_bufs[i][2 + (e - c)] = 'm'; color_bufps[i] = color_bufs[i]; if (e[0] == ':') e++; } colors = color_bufps; return 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) { wchar_t *ws; size_t wl; if (len == -1) len = strlen(s); wl = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0); ws = jv_mem_calloc((wl + 1), sizeof(*ws)); if (!ws) return; wl = MultiByteToWideChar(CP_UTF8, 0, s, len, ws, wl + 1); ws[wl] = 0; WriteConsoleW((HANDLE)_get_osfhandle(fileno(fout)), ws, wl, NULL, NULL); free(ws); } 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 MAX_PRINT_DEPTH) { put_str("", F, S, flags & JV_PRINT_ISATTY); } else 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("", F, S, flags & JV_PRINT_ISATTY); } else { put_str("", 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; } jq-jq-1.6/src/jv_aux.c0000600000175000017500000004663513366726451014162 0ustar czchenczchen#include #include #include #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 static 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 neg_keys = jv_array(); jv nonneg_keys = jv_array(); jv new_array = jv_array(); jv starts = jv_array(), ends = jv_array(); jv_array_foreach(keys, i, key) { if (jv_get_kind(key) == JV_KIND_NUMBER) { if (jv_number_value(key) < 0) { neg_keys = jv_array_append(neg_keys, key); } else { nonneg_keys = jv_array_append(nonneg_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 neg_idx = 0; int nonneg_idx = 0; int len = jv_array_length(jv_copy(t)); jv_array_foreach(t, i, elem) { int del = 0; while (neg_idx < jv_array_length(jv_copy(neg_keys))) { int delidx = len + (int)jv_number_value(jv_array_get(jv_copy(neg_keys), neg_idx)); if (i == delidx) { del = 1; } if (i < delidx) { break; } neg_idx++; } while (nonneg_idx < jv_array_length(jv_copy(nonneg_keys))) { int delidx = (int)jv_number_value(jv_array_get(jv_copy(nonneg_keys), nonneg_idx)); if (i == delidx) { del = 1; } if (i < delidx) { break; } nonneg_idx++; } for (int sidx=0; !del && sidx 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_calloc(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= 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; int index; }; 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 index if r == 0 makes the sort stable return r ? r : (a->index - b->index); } 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_calloc(sizeof(struct sort_entry), n); for (int i=0; i 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; } jq-jq-1.6/src/lexer.c0000600000175000017500000020667413366726451014006 0ustar czchenczchen#line 2 "src/lexer.c" #line 4 "src/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 6 #define YY_FLEX_SUBMINOR_VERSION 0 #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 #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . 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 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 #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #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) #define YY_LINENO_REWIND_TO(ptr) /* 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_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,yy_size_t 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(yyscanner) (/*CONSTCOND*/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 ); #if defined(__GNUC__) && __GNUC__ >= 3 __attribute__((__noreturn__)) #endif 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 50 #define YY_END_OF_BUFFER 51 /* 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[157] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 49, 48, 48, 49, 40, 1, 35, 35, 36, 37, 35, 35, 35, 35, 35, 39, 35, 35, 35, 35, 49, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 35, 44, 44, 42, 45, 48, 2, 1, 29, 27, 25, 26, 33, 39, 47, 47, 18, 28, 0, 31, 3, 32, 0, 38, 46, 0, 46, 46, 4, 46, 46, 46, 46, 46, 46, 9, 46, 46, 46, 46, 14, 46, 46, 46, 24, 44, 43, 41, 43, 47, 0, 39, 30, 39, 34, 0, 46, 13, 46, 46, 8, 46, 46, 15, 46, 46, 46, 46, 46, 46, 46, 19, 0, 43, 46, 46, 46, 46, 12, 11, 46, 46, 46, 46, 46, 46, 10, 43, 46, 22, 20, 46, 46, 46, 21, 46, 46, 43, 46, 46, 5, 46, 7, 16, 23, 17, 6, 0 } ; static yyconst YY_CHAR 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 YY_CHAR 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_uint16_t yy_base[170] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 320, 321, 57, 59, 297, 321, 0, 321, 296, 321, 321, 295, 294, 293, 47, 47, 50, 292, 291, 290, 294, 0, 291, 48, 51, 53, 52, 37, 59, 57, 66, 56, 63, 68, 70, 72, 287, 0, 0, 321, 80, 90, 321, 0, 321, 321, 321, 321, 95, 99, 0, 106, 286, 321, 110, 321, 321, 321, 290, 0, 285, 281, 86, 77, 277, 97, 101, 111, 113, 115, 117, 274, 119, 120, 118, 121, 270, 122, 123, 124, 321, 0, 257, 321, 255, 0, 254, 249, 321, 245, 321, 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, 321, 230, 239, 245, 250, 255, 264, 273, 278, 283, 285, 290, 294, 298 } ; static yyconst flex_int16_t yy_def[170] = { 0, 156, 1, 1, 1, 1, 1, 1, 1, 1, 1, 157, 157, 156, 156, 156, 156, 156, 156, 158, 156, 156, 156, 156, 156, 156, 156, 159, 156, 156, 156, 156, 156, 156, 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 156, 162, 162, 156, 163, 156, 156, 158, 156, 156, 156, 156, 156, 156, 164, 164, 156, 156, 156, 156, 156, 156, 156, 160, 161, 156, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 156, 162, 156, 156, 165, 164, 156, 164, 156, 156, 156, 166, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 163, 167, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 168, 161, 161, 161, 161, 161, 161, 161, 161, 161, 169, 161, 161, 161, 161, 161, 161, 161, 161, 161, 0, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156 } ; static yyconst flex_uint16_t yy_nxt[375] = { 0, 14, 15, 16, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 20, 26, 27, 28, 29, 20, 20, 30, 31, 32, 33, 34, 35, 35, 22, 14, 23, 36, 37, 38, 39, 40, 41, 42, 35, 43, 35, 44, 45, 35, 46, 35, 47, 35, 48, 35, 35, 22, 49, 23, 51, 51, 74, 52, 52, 54, 54, 54, 54, 61, 65, 62, 62, 74, 62, 66, 74, 74, 74, 80, 64, 74, 74, 67, 74, 75, 53, 53, 74, 64, 79, 74, 67, 74, 87, 74, 96, 74, 54, 54, 76, 74, 74, 74, 77, 78, 81, 83, 82, 84, 74, 74, 90, 88, 85, 86, 91, 62, 106, 62, 89, 62, 74, 62, 92, 99, 74, 99, 67, 99, 100, 99, 67, 105, 102, 97, 74, 67, 74, 107, 74, 67, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 109, 108, 112, 116, 110, 74, 115, 74, 117, 118, 125, 119, 111, 126, 74, 113, 114, 127, 74, 74, 74, 124, 128, 74, 129, 74, 120, 156, 74, 74, 74, 74, 74, 74, 74, 74, 132, 74, 74, 130, 131, 74, 74, 137, 140, 74, 74, 74, 139, 135, 133, 138, 145, 134, 147, 74, 143, 144, 151, 141, 148, 74, 150, 142, 152, 74, 97, 149, 74, 74, 74, 121, 74, 153, 154, 74, 74, 121, 74, 74, 74, 74, 155, 50, 50, 50, 50, 50, 50, 50, 50, 50, 56, 121, 56, 56, 56, 56, 56, 56, 56, 63, 63, 74, 63, 74, 63, 72, 74, 72, 74, 72, 73, 73, 73, 102, 73, 94, 94, 100, 94, 94, 94, 94, 102, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 98, 121, 98, 121, 98, 122, 74, 122, 122, 123, 74, 123, 136, 74, 136, 136, 146, 104, 146, 146, 95, 74, 95, 95, 103, 101, 93, 74, 71, 70, 69, 68, 60, 59, 58, 57, 55, 156, 13, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156 } ; static yyconst flex_int16_t yy_chk[375] = { 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, 40, 11, 12, 15, 15, 16, 16, 27, 28, 27, 29, 36, 29, 28, 37, 39, 38, 40, 27, 44, 42, 29, 41, 36, 11, 12, 45, 27, 39, 43, 29, 46, 44, 47, 53, 48, 54, 54, 37, 155, 76, 154, 37, 38, 41, 42, 41, 43, 153, 75, 47, 45, 43, 43, 48, 61, 76, 61, 46, 62, 78, 62, 48, 64, 79, 64, 61, 67, 64, 67, 62, 75, 67, 53, 80, 61, 81, 78, 82, 62, 83, 87, 85, 86, 88, 90, 91, 92, 105, 107, 108, 110, 80, 79, 82, 87, 81, 111, 86, 113, 88, 90, 107, 91, 81, 108, 114, 83, 85, 110, 115, 116, 117, 105, 111, 118, 113, 119, 92, 121, 124, 126, 125, 129, 130, 131, 132, 133, 116, 134, 141, 114, 115, 144, 137, 124, 129, 140, 142, 145, 126, 119, 117, 125, 134, 118, 137, 148, 132, 133, 144, 130, 140, 147, 142, 131, 145, 150, 121, 141, 152, 151, 149, 146, 143, 147, 148, 139, 138, 136, 135, 128, 127, 123, 150, 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 122, 158, 158, 158, 158, 158, 158, 158, 159, 159, 120, 159, 112, 159, 160, 109, 160, 106, 160, 161, 161, 161, 102, 161, 162, 162, 100, 162, 162, 162, 162, 99, 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, 164, 97, 164, 95, 164, 165, 89, 165, 165, 166, 84, 166, 167, 77, 167, 167, 168, 74, 168, 168, 169, 73, 169, 169, 71, 65, 49, 35, 33, 32, 31, 30, 26, 25, 24, 21, 17, 13, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156 } ; /* 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 "src/lexer.l" #line 2 "src/lexer.l" #include #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 "src/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 605 "src/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 #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; yy_size_t 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 ); yy_size_t 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 ); int jq_yyget_column (yyscan_t yyscanner ); void jq_yyset_column (int _column_no ,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 YY_NO_UNPUT #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 /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; 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 ); } { #line 38 "src/lexer.l" #line 900 "src/lexer.c" while ( /*CONSTCOND*/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 { 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 >= 157 ) 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] != 321 ); 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 "src/lexer.l" { /* comments */ } YY_BREAK case 2: YY_RULE_SETUP #line 42 "src/lexer.l" { return NEQ; } YY_BREAK case 3: YY_RULE_SETUP #line 43 "src/lexer.l" { return EQ; } YY_BREAK case 4: YY_RULE_SETUP #line 44 "src/lexer.l" { return AS; } YY_BREAK case 5: YY_RULE_SETUP #line 45 "src/lexer.l" { return IMPORT; } YY_BREAK case 6: YY_RULE_SETUP #line 46 "src/lexer.l" { return INCLUDE; } YY_BREAK case 7: YY_RULE_SETUP #line 47 "src/lexer.l" { return MODULE; } YY_BREAK case 8: YY_RULE_SETUP #line 48 "src/lexer.l" { return DEF; } YY_BREAK case 9: YY_RULE_SETUP #line 49 "src/lexer.l" { return IF; } YY_BREAK case 10: YY_RULE_SETUP #line 50 "src/lexer.l" { return THEN; } YY_BREAK case 11: YY_RULE_SETUP #line 51 "src/lexer.l" { return ELSE; } YY_BREAK case 12: YY_RULE_SETUP #line 52 "src/lexer.l" { return ELSE_IF; } YY_BREAK case 13: YY_RULE_SETUP #line 53 "src/lexer.l" { return AND; } YY_BREAK case 14: YY_RULE_SETUP #line 54 "src/lexer.l" { return OR; } YY_BREAK case 15: YY_RULE_SETUP #line 55 "src/lexer.l" { return END; } YY_BREAK case 16: YY_RULE_SETUP #line 56 "src/lexer.l" { return REDUCE; } YY_BREAK case 17: YY_RULE_SETUP #line 57 "src/lexer.l" { return FOREACH; } YY_BREAK case 18: YY_RULE_SETUP #line 58 "src/lexer.l" { return DEFINEDOR; } YY_BREAK case 19: YY_RULE_SETUP #line 59 "src/lexer.l" { return TRY; } YY_BREAK case 20: YY_RULE_SETUP #line 60 "src/lexer.l" { return CATCH; } YY_BREAK case 21: YY_RULE_SETUP #line 61 "src/lexer.l" { return LABEL; } YY_BREAK case 22: YY_RULE_SETUP #line 62 "src/lexer.l" { return BREAK; } YY_BREAK case 23: YY_RULE_SETUP #line 63 "src/lexer.l" { return LOC; } YY_BREAK case 24: YY_RULE_SETUP #line 64 "src/lexer.l" { return SETPIPE; } YY_BREAK case 25: YY_RULE_SETUP #line 65 "src/lexer.l" { return SETPLUS; } YY_BREAK case 26: YY_RULE_SETUP #line 66 "src/lexer.l" { return SETMINUS; } YY_BREAK case 27: YY_RULE_SETUP #line 67 "src/lexer.l" { return SETMULT; } YY_BREAK case 28: YY_RULE_SETUP #line 68 "src/lexer.l" { return SETDIV; } YY_BREAK case 29: YY_RULE_SETUP #line 69 "src/lexer.l" { return SETMOD; } YY_BREAK case 30: YY_RULE_SETUP #line 70 "src/lexer.l" { return SETDEFINEDOR; } YY_BREAK case 31: YY_RULE_SETUP #line 71 "src/lexer.l" { return LESSEQ; } YY_BREAK case 32: YY_RULE_SETUP #line 72 "src/lexer.l" { return GREATEREQ; } YY_BREAK case 33: YY_RULE_SETUP #line 73 "src/lexer.l" { return REC; } YY_BREAK case 34: YY_RULE_SETUP #line 74 "src/lexer.l" { return ALTERNATION; } YY_BREAK case 35: YY_RULE_SETUP #line 75 "src/lexer.l" { return yytext[0];} YY_BREAK case 36: YY_RULE_SETUP #line 77 "src/lexer.l" { return enter(yytext[0], YY_START, yyscanner); } YY_BREAK case 37: YY_RULE_SETUP #line 81 "src/lexer.l" { return try_exit(yytext[0], YY_START, yyscanner); } YY_BREAK case 38: YY_RULE_SETUP #line 85 "src/lexer.l" { yylval->literal = jv_string_sized(yytext + 1, yyleng - 1); return FORMAT; } YY_BREAK case 39: YY_RULE_SETUP #line 89 "src/lexer.l" { yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL; } YY_BREAK case 40: YY_RULE_SETUP #line 93 "src/lexer.l" { yy_push_state(IN_QQSTRING, yyscanner); return QQSTRING_START; } YY_BREAK case 41: YY_RULE_SETUP #line 99 "src/lexer.l" { return enter(QQSTRING_INTERP_START, YY_START, yyscanner); } YY_BREAK case 42: YY_RULE_SETUP #line 102 "src/lexer.l" { yy_pop_state(yyscanner); return QQSTRING_END; } YY_BREAK case 43: /* rule 43 can match eol */ YY_RULE_SETUP #line 106 "src/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 44: /* rule 44 can match eol */ YY_RULE_SETUP #line 113 "src/lexer.l" { yylval->literal = jv_string_sized(yytext, yyleng); return QQSTRING_TEXT; } YY_BREAK case 45: YY_RULE_SETUP #line 117 "src/lexer.l" { return INVALID_CHARACTER; } YY_BREAK case 46: YY_RULE_SETUP #line 123 "src/lexer.l" { yylval->literal = jv_string(yytext); return IDENT;} YY_BREAK case 47: YY_RULE_SETUP #line 124 "src/lexer.l" { yylval->literal = jv_string(yytext+1); return FIELD;} YY_BREAK case 48: /* rule 48 can match eol */ YY_RULE_SETUP #line 126 "src/lexer.l" {} YY_BREAK case 49: YY_RULE_SETUP #line 128 "src/lexer.l" { return INVALID_CHARACTER; } YY_BREAK case 50: YY_RULE_SETUP #line 130 "src/lexer.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 1239 "src/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 user's declarations */ } /* 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; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; yy_size_t 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 = (yy_size_t) (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 { yy_size_t 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_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t 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, 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 ((int) (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. */ int 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) { yy_state_type yy_current_state; 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 ) { 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 >= 157 ) 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) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; 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 >= 157 ) 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 == 156); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #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 */ yy_size_t 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 = (yy_size_t)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 ); } /* 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) { yy_size_t 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; /* After all that talk, this was set to 1 anyways... */ 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. */ yy_size_t 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, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; yy_size_t 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; (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. */ yy_size_t 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 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" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column 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" ); 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) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; 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) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif #define YYTABLES_NAME "yytables" #line 130 "src/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); } jq-jq-1.6/src/jv_dtoa.c0000600000175000017500000025527413366726451014315 0ustar czchenczchen /**************************************************************** * * 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 #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, test_scale; 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; test_scale = y; #ifdef Avoid_Underflow test_scale = (!bc.scale || y > 2*P*Exp_msk1); #endif if (test_scale) { 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; } jq-jq-1.6/src/jv_unicode.h0000600000175000017500000000062613366726451015006 0ustar czchenczchen#ifndef JV_UNICODE_H #define JV_UNICODE_H const char* jvp_utf8_backtrack(const char* start, const char* min, int *missing_bytes); 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 jq-jq-1.6/src/jq.h0000600000175000017500000000450313366726451013271 0ustar czchenczchen#ifndef JQ_H #define JQ_H #include #include "jv.h" enum { JQ_DEBUG_TRACE = 1, JQ_DEBUG_TRACE_DETAIL = 2, JQ_DEBUG_TRACE_ALL = JQ_DEBUG_TRACE | JQ_DEBUG_TRACE_DETAIL, }; 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 **); void jq_halt(jq_state *, jv, jv); int jq_halted(jq_state *); jv jq_get_exit_code(jq_state *); jv jq_get_error_message(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*); int jq_set_colors(const char *); #endif /* !JQ_H */ jq-jq-1.6/src/builtin.jq0000600000175000017500000003045313366726451014513 0ustar czchenczchendef halt_error: halt_error(5); 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])); 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 (.; label $out | (setpath($p; getpath($p) | update) | ., break $out), delpaths([$p])); def map_values(f): .[] |= f; # recurse def recurse(f): def r: ., (f | 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 // .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 "" else .+$x end) + ($i | if type=="boolean" or type=="number" then tostring else .//"" 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 length > 0 and 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; .-1; $item, if . <= 0 then break $out else empty end) end; def isempty(g): 0 == ((label $go | g | (1, break $go)) // 0); def first(g): label $out | g | ., break $out; 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][$n:]) 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: {string:true,number:true,boolean:true,null:true} as $leaf_types | . as $dot | if $leaf_types[$dot|type] or length==0 then [[],$dot] else # We really need a _streaming_ form of `keys`. # We can use `range` for arrays, but not for objects. keys_unsorted as $keys | $keys[-1] as $last| ((# for each key $keys[] | . as $key | $dot[$key] | . as $dot | # recurse on each key/value tostream|.[0]|=[$key]+.), # then add the closing marker [[$last]]) 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_unsorted[] as $key ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f elif type == "array" then map( walk(f) ) | f else f end; # SQL-ish operators here: def INDEX(stream; idx_expr): reduce stream as $row ({}; .[$row|idx_expr| if type != "string" then tojson else . end] |= $row); def INDEX(idx_expr): INDEX(.[]; idx_expr); def JOIN($idx; idx_expr): [.[] | [., $idx[idx_expr]]]; def JOIN($idx; stream; idx_expr): stream | [., $idx[idx_expr]]; def JOIN($idx; stream; idx_expr; join_expr): stream | [., $idx[idx_expr]] | join_expr; def IN(s): reduce (first(select(. == s)) | true) as $v (false; if . or $v then true else false end); def IN(src; s): reduce (src|IN(s)) as $v (false; if . or $v then true else false end); jq-jq-1.6/src/jq_parser.h0000600000175000017500000000032013366726451014636 0ustar czchenczchen#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-jq-1.6/src/locfile.c0000600000175000017500000000460513366726451014272 0ustar czchenczchen#include #include #include #include #include #include #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; inlines++; } l->linemap = jv_mem_calloc(sizeof(int), (l->nlines + 1)); l->linemap[0] = 0; int line = 1; for (int i=0; ilinemap[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", 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; } jq-jq-1.6/src/parser.y0000600000175000017500000005314313366726451014200 0ustar czchenczchen%{ #include #include #include #include #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($$); } %destructor { block_free($$); } %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 IDENT %token FIELD %token LITERAL %token 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 ALTERNATION "?//" %token QQSTRING_START %token 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 Exp Term %type MkDict MkDictPair ExpD %type ElseBody %type String QQString %type FuncDef FuncDefs %type Module Import Imports ImportWhat ImportFrom %type Param Params Arg Args %type Patterns RepPatterns Pattern ArrayPats ObjPats ObjPat %type 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; } /* Returns string message if the block is a constant that is not valid as an * object key. */ static jv check_object_key(block k) { if (block_is_const(k) && block_const_kind(k) != JV_KIND_STRING) { char errbuf[15]; return jv_string_fmt("Cannot use %s (%s) as object key", jv_kind_name(block_const_kind(k)), jv_dump_string_trunc(jv_copy(block_const(k)), errbuf, sizeof(errbuf))); } return jv_invalid(); } 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" Patterns '|' Exp { $$ = gen_destructure($1, $3, $5); } | "reduce" Term "as" Patterns '(' Exp ';' Exp ')' { $$ = gen_reduce($2, $4, $6, $8); } | "foreach" Term "as" Patterns '(' Exp ';' Exp ';' Exp ')' { $$ = gen_foreach($2, $4, $6, $8, $10); } | "foreach" Term "as" Patterns '(' 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 { $$ = jv_string("text"); } QQString QQSTRING_END { $$ = $3; jv_free($2); } | FORMAT QQSTRING_START { $$ = $1; } QQString QQSTRING_END { $$ = $4; jv_free($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($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); } RepPatterns: RepPatterns "?//" Pattern { $$ = BLOCK($1, gen_destructure_alt($3)); } | Pattern { $$ = gen_destructure_alt($1); } Patterns: RepPatterns "?//" Pattern { $$ = BLOCK($1, $3); } | Pattern { $$ = $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($2), BLOCK(gen_op_simple(DUP), gen_op_unbound(STOREV, jv_string_value($2)), $4)); } | 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 { jv msg = check_object_key($2); if (jv_is_valid(msg)) { FAIL(@$, jv_string_value(msg)); } jv_free(msg); $$ = gen_object_matcher($2, $5); } | error ':' Pattern { FAIL(@$, "May need parentheses around object key expression"); $$ = $3; } 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 { jv msg = check_object_key($2); if (jv_is_valid(msg)) { FAIL(@$, jv_string_value(msg)); } jv_free(msg); $$ = gen_dictpair($2, $5); } | error ':' ExpD { FAIL(@$, "May need parentheses around object key expression"); $$ = $3; } %% 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; } jq-jq-1.6/src/util.c0000600000175000017500000003225113366726451013630 0ustar czchenczchen #ifdef HAVE_MEMMEM #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ALLOCA_H # include #elif !defined alloca # ifdef __GNUC__ # define alloca __builtin_alloca # elif defined _MSC_VER # include # define alloca _alloca # elif !defined HAVE_ALLOCA # ifdef __cplusplus extern "C" # endif void *alloca (size_t); # endif #endif #ifndef WIN32 #include #endif #ifdef WIN32 #include #include #include #include #include #endif #include "util.h" #include "jq.h" #include "jv_alloc.h" #ifdef WIN32 FILE *fopen(const char *fname, const char *mode) { size_t sz = sizeof(wchar_t) * MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0); wchar_t *wfname = alloca(sz + 2); // +2 is not needed, but just in case MultiByteToWideChar(CP_UTF8, 0, fname, -1, wfname, sz); sz = sizeof(wchar_t) * MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); wchar_t *wmode = alloca(sz + 2); // +2 is not needed, but just in case MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, sz); return _wfopen(wfname, wmode); } #endif #ifndef HAVE_MKSTEMP int mkstemp(char *template) { size_t len = strlen(template); int tries=5; int fd; // mktemp() truncates template when it fails char *s = alloca(len + 1); assert(s != NULL); strcpy(s, template); do { // Restore template strcpy(template, s); (void) mktemp(template); fd = open(template, O_CREAT | O_EXCL | O_RDWR, 0600); } while (fd == -1 && tries-- > 0); return fd; } #endif jv expand_path(jv path) { assert(jv_get_kind(path) == JV_KIND_STRING); const char *pstr = jv_string_value(path); jv ret = path; if (jv_string_length_bytes(jv_copy(path)) > 1 && pstr[0] == '~' && pstr[1] == '/') { jv home = get_home(); if (jv_is_valid(home)) { ret = jv_string_fmt("%s/%s",jv_string_value(home),pstr+2); jv_free(home); } else { jv emsg = jv_invalid_get_msg(home); ret = jv_invalid_with_msg(jv_string_fmt("Could not expand %s. (%s)", pstr, jv_string_value(emsg))); jv_free(emsg); } jv_free(path); } return ret; } jv get_home() { jv ret; char *home = getenv("HOME"); if (!home) { #ifndef WIN32 struct passwd* pwd = getpwuid(getuid()); if (pwd) ret = jv_string(pwd->pw_dir); else ret = jv_invalid_with_msg(jv_string("Could not find home directory.")); #else home = getenv("USERPROFILE"); if (!home) { char *hd = getenv("HOMEDRIVE"); if (!hd) hd = ""; home = getenv("HOMEPATH"); if (!home) { ret = jv_invalid_with_msg(jv_string("Could not find home directory.")); } else { ret = jv_string_fmt("%s%s",hd,home); } } else { ret = jv_string(home); } #endif } else { ret = jv_string(home); } return ret; } jv jq_realpath(jv path) { int path_max; char *buf = NULL; #ifdef _PC_PATH_MAX path_max = pathconf(jv_string_value(path),_PC_PATH_MAX); #else path_max = PATH_MAX; #endif if (path_max > 0) { buf = jv_mem_alloc(path_max); } #ifdef WIN32 char *tmp = _fullpath(buf, jv_string_value(path), path_max); #else char *tmp = realpath(jv_string_value(path), buf); #endif if (tmp == NULL) { free(buf); return path; } jv_free(path); path = jv_string(tmp); free(tmp); return path; } const void *_jq_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { #ifdef HAVE_MEMMEM return (const void*)memmem(haystack, haystacklen, needle, needlelen); #else const char *h = haystack; const char *n = needle; size_t hi, hi2, ni; if (haystacklen < needlelen || haystacklen == 0) return NULL; for (hi = 0; hi < (haystacklen - needlelen + 1); hi++) { for (ni = 0, hi2 = hi; ni < needlelen; ni++, hi2++) { if (h[hi2] != n[ni]) goto not_this; } return &h[hi]; not_this: continue; } return NULL; #endif /* !HAVE_MEMMEM */ } struct jq_util_input_state { jq_util_msg_cb err_cb; void *err_cb_data; jv_parser *parser; FILE* current_input; char **files; int nfiles; int curr_file; int failures; jv slurped; char buf[4096]; size_t buf_valid_len; jv current_filename; size_t current_line; }; static void fprinter(void *data, const char *fname) { fprintf((FILE *)data, "jq: error: Could not open file %s: %s\n", fname, strerror(errno)); } // If parser == NULL -> RAW jq_util_input_state *jq_util_input_init(jq_util_msg_cb err_cb, void *err_cb_data) { if (err_cb == NULL) { err_cb = fprinter; err_cb_data = stderr; } jq_util_input_state *new_state = jv_mem_alloc(sizeof(*new_state)); memset(new_state, 0, sizeof(*new_state)); new_state->err_cb = err_cb; new_state->err_cb_data = err_cb_data; new_state->parser = NULL; new_state->current_input = NULL; new_state->files = NULL; new_state->nfiles = 0; new_state->curr_file = 0; new_state->slurped = jv_invalid(); new_state->buf[0] = 0; new_state->buf_valid_len = 0; new_state->current_filename = jv_invalid(); new_state->current_line = 0; return new_state; } void jq_util_input_set_parser(jq_util_input_state *state, jv_parser *parser, int slurp) { assert(!jv_is_valid(state->slurped)); state->parser = parser; if (parser == NULL && slurp) state->slurped = jv_string(""); else if (slurp) state->slurped = jv_array(); else state->slurped = jv_invalid(); } void jq_util_input_free(jq_util_input_state **state) { jq_util_input_state *old_state = *state; *state = NULL; if (old_state == NULL) return; if (old_state->parser != NULL) jv_parser_free(old_state->parser); for (int i = 0; i < old_state->nfiles; i++) free(old_state->files[i]); free(old_state->files); jv_free(old_state->slurped); jv_free(old_state->current_filename); jv_mem_free(old_state); } void jq_util_input_add_input(jq_util_input_state *state, const char *fname) { state->files = jv_mem_realloc(state->files, (state->nfiles + 1) * sizeof(state->files[0])); state->files[state->nfiles++] = jv_mem_strdup(fname); } int jq_util_input_errors(jq_util_input_state *state) { return state->failures; } static const char *next_file(jq_util_input_state *state) { if (state->curr_file < state->nfiles) return state->files[state->curr_file++]; return NULL; } static int jq_util_input_read_more(jq_util_input_state *state) { if (!state->current_input || feof(state->current_input) || ferror(state->current_input)) { if (state->current_input && ferror(state->current_input)) { // System-level input error on the stream. It will be closed (below). // TODO: report it. Can't use 'state->err_cb()' as it is hard-coded for // 'open' related problems. fprintf(stderr,"Input error: %s\n", strerror(errno)); } if (state->current_input) { if (state->current_input == stdin) { clearerr(stdin); // perhaps we can read again; anyways, we don't fclose(stdin) } else { fclose(state->current_input); } state->current_input = NULL; jv_free(state->current_filename); state->current_filename = jv_invalid(); state->current_line = 0 ; } const char *f = next_file(state); if (f != NULL) { if (!strcmp(f, "-")) { state->current_input = stdin; state->current_filename = jv_string(""); } else { state->current_input = fopen(f, "r"); state->current_filename = jv_string(f); if (!state->current_input) { state->err_cb(state->err_cb_data, f); state->failures++; } } state->current_line = 0; } } state->buf[0] = 0; state->buf_valid_len = 0; if (state->current_input) { char *res; memset(state->buf, 0, sizeof(state->buf)); while (!(res = fgets(state->buf, sizeof(state->buf), state->current_input)) && ferror(state->current_input) && errno == EINTR) clearerr(state->current_input); if (res == NULL) { state->buf[0] = 0; if (ferror(state->current_input)) state->failures++; } else { const char *p = memchr(state->buf, '\n', sizeof(state->buf)); if (p != NULL) state->current_line++; if (p == NULL && state->parser != NULL) { /* * There should be no NULs in JSON texts (but JSON text * sequences are another story). */ state->buf_valid_len = strlen(state->buf); } else if (p == NULL && feof(state->current_input)) { size_t i; /* * XXX We can't know how many bytes we've read! * * We can't use getline() because there need not be any newlines * in the input. The only entirely correct choices are: use * fgetc() or fread(). Using fread() will complicate buffer * management here. * * For now we guess how much fgets() read. */ for (p = state->buf, i = 0; i < sizeof(state->buf); i++) { if (state->buf[i] != '\0') p = &state->buf[i]; } state->buf_valid_len = p - state->buf + 1; } else if (p == NULL) { state->buf_valid_len = sizeof(state->buf) - 1; } else { state->buf_valid_len = (p - state->buf) + 1; } } } return state->curr_file == state->nfiles && (!state->current_input || feof(state->current_input) || ferror(state->current_input)); } jv jq_util_input_next_input_cb(jq_state *jq, void *data) { return jq_util_input_next_input((jq_util_input_state *)data); } // Return the current_filename:current_line jv jq_util_input_get_position(jq_state *jq) { jq_input_cb cb = NULL; void *cb_data = NULL; jq_get_input_cb(jq, &cb, &cb_data); assert(cb == jq_util_input_next_input_cb); if (cb != jq_util_input_next_input_cb) return jv_invalid_with_msg(jv_string("Invalid jq_util_input API usage")); jq_util_input_state *s = (jq_util_input_state *)cb_data; // We can't assert that current_filename is a string because if // the error was a JSON parser error then we may not have set // current_filename yet. if (jv_get_kind(s->current_filename) != JV_KIND_STRING) return jv_string(""); jv v = jv_string_fmt("%s:%lu", jv_string_value(s->current_filename), (unsigned long)s->current_line); return v; } jv jq_util_input_get_current_filename(jq_state* jq) { jq_input_cb cb=NULL; void *cb_data=NULL; jq_get_input_cb(jq, &cb, &cb_data); if (cb != jq_util_input_next_input_cb) return jv_invalid_with_msg(jv_string("Unknown input filename")); jq_util_input_state *s = (jq_util_input_state *)cb_data; jv v = jv_copy(s->current_filename); return v; } jv jq_util_input_get_current_line(jq_state* jq) { jq_input_cb cb=NULL; void *cb_data=NULL; jq_get_input_cb(jq, &cb, &cb_data); if (cb != jq_util_input_next_input_cb) return jv_invalid_with_msg(jv_string("Unknown input line number")); jq_util_input_state *s = (jq_util_input_state *)cb_data; jv v = jv_number(s->current_line); return v; } // Blocks to read one more input from stdin and/or given files // When slurping, it returns just one value jv jq_util_input_next_input(jq_util_input_state *state) { int is_last = 0; int has_more = 0; jv value = jv_invalid(); // need more input do { if (state->parser == NULL) { // Raw input is_last = jq_util_input_read_more(state); if (state->buf_valid_len == 0) continue; if (jv_is_valid(state->slurped)) { // Slurped raw input state->slurped = jv_string_concat(state->slurped, jv_string_sized(state->buf, state->buf_valid_len)); } else { if (!jv_is_valid(value)) value = jv_string(""); if (state->buf[state->buf_valid_len-1] == '\n') { // whole line state->buf[state->buf_valid_len-1] = 0; return jv_string_concat(value, jv_string_sized(state->buf, state->buf_valid_len-1)); } value = jv_string_concat(value, jv_string_sized(state->buf, state->buf_valid_len)); state->buf[0] = '\0'; state->buf_valid_len = 0; } } else { if (jv_parser_remaining(state->parser) == 0) { is_last = jq_util_input_read_more(state); if (is_last && state->buf_valid_len == 0) value = jv_invalid(); jv_parser_set_buf(state->parser, state->buf, state->buf_valid_len, !is_last); } value = jv_parser_next(state->parser); if (jv_is_valid(state->slurped)) { // When slurping an input that doesn't have a trailing newline, // we might have more than one value on the same line, so let's check // to see if we have more data to parse. has_more = jv_parser_remaining(state->parser); if (jv_is_valid(value)) { state->slurped = jv_array_append(state->slurped, value); value = jv_invalid(); } else if (jv_invalid_has_msg(jv_copy(value))) return value; // Not slurped parsed input } else if (jv_is_valid(value) || jv_invalid_has_msg(jv_copy(value))) { return value; } } } while (!is_last || has_more); if (jv_is_valid(state->slurped)) { value = state->slurped; state->slurped = jv_invalid(); } return value; } jq-jq-1.6/src/execute.c0000600000175000017500000010417613366726451014323 0ustar czchenczchen#include #include #include #include #include #include #include #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; int halted; jv exit_code; jv error_message; 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; istk, 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; iclosure = make_closure(jq, argdef + i * 2); entries++; } for (int i=0; inlocals; 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; icurr_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); } } /* For f_getpath() */ jv _jq_path_append(jq_state *jq, jv v, jv p, jv value_at_path) { if (jq->subexp_nest != 0 || jv_get_kind(jq->path) != JV_KIND_ARRAY || !jv_is_valid(value_at_path)) { jv_free(v); jv_free(p); return value_at_path; } if (!jv_identical(v, jv_copy(jq->value_at_path))) { jv_free(p); return value_at_path; } if (jv_get_kind(p) == JV_KIND_ARRAY) jq->path = jv_array_concat(jq->path, p); else jq->path = jv_array_append(jq->path, p); jv_free(jq->value_at_path); return jv_copy(jq->value_at_path = 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(); jq->halted = 0; jv_free(jq->exit_code); jv_free(jq->error_message); 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) { if (jq->halted) { if (jq->debug_trace_enabled) printf("\t\n"); return jv_invalid(); } 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]; param = jq->stk_top; for (int i=0; istk, 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); } if (jq->debug_trace_enabled & JQ_DEBUG_TRACE_DETAIL) { while ((param = *stack_block_next(&jq->stk, param))) { printf(" || "); jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), JV_PRINT_REFCOUNT); } } } else { printf("\t"); } 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 PUSHK_UNDER: { jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++); assert(jv_is_valid(v)); jv v2 = stack_pop(jq); stack_push(jq, v); stack_push(jq, v2); 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 STOREVN: stack_save(jq, pc - 1, stack_get_pos(jq)); 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 ON_BACKTRACK(STOREVN): { uint16_t level = *pc++; uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); jv_free(*var); *var = jv_null(); goto do_backtrack; 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 DESTRUCTURE_ALT: case FORK: { stack_save(jq, pc - 1, stack_get_pos(jq)); pc++; // skip offset this time break; } case ON_BACKTRACK(FORK_OPT): case ON_BACKTRACK(DESTRUCTURE_ALT): { 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 // DESTRUCTURE_ALT doesn't want the error message on the stack, // as we would just want to throw it away anyway. if (opcode != ON_BACKTRACK(DESTRUCTURE_ALT)) { jv_free(stack_pop(jq)); // free the input stack_push(jq, jv_invalid_get_msg(jq->error)); // push the error's message } else { jv_free(jq->error); } 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 * (i.e., number of call arguments) * (what we're calling) * (frame reference + code pointer) * * * * 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->halted = 0; jq->exit_code = jv_invalid(); jq->error_message = jv_invalid(); 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)); jq->debug_trace_enabled = flags & JQ_DEBUG_TRACE_ALL; 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 * * (2 units) * (2 units each) * * * A closure is: * * (a relative frame count chased via the current frame's env) * (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; insubfunctions; i++) { bc->subfunctions[i] = optimize(bc->subfunctions[i]); } return optimize_code(bc); } static jv args2obj(jv args) { if (jv_get_kind(args) == JV_KIND_OBJECT) return args; assert(jv_get_kind(args) == JV_KIND_ARRAY); jv r = jv_object(); jv kk = jv_string("name"); jv vk = jv_string("value"); jv_array_foreach(args, i, v) r = jv_object_set(r, jv_object_get(jv_copy(v), kk), jv_object_get(v, vk)); jv_free(args); jv_free(kk); jv_free(vk); return r; } 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 || jv_get_kind(args) == JV_KIND_OBJECT); struct locfile* locations; locations = locfile_init(jq, "", 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) { nerrors = builtins_bind(jq, &program); if (nerrors == 0) { nerrors = block_compile(program, &jq->bc, locations, args = args2obj(args)); } } else jv_free(args); 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); locfile_free(locations); return jq->bc != NULL; } int jq_compile(jq_state *jq, const char* str) { return jq_compile_args(jq, str, jv_object()); } 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; } void jq_halt(jq_state *jq, jv exit_code, jv error_message) { assert(!jq->halted); jq->halted = 1; jq->exit_code = exit_code; jq->error_message = error_message; } int jq_halted(jq_state *jq) { return jq->halted; } jv jq_get_exit_code(jq_state *jq) { return jv_copy(jq->exit_code); } jv jq_get_error_message(jq_state *jq) { return jv_copy(jq->error_message); } jq-jq-1.6/src/jv.c0000600000175000017500000010150013366726451013264 0ustar czchenczchen#include #include #include #include #include #include #include #include #include #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 ""; 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 ""; } 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; ilength; 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 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(); } if (a.offset + start >= 1 << (sizeof(a.offset) * CHAR_BIT)) { jv r = jv_array_sized(end - start); for (int i = start; i < end; i++) r = jv_array_append(r, jv_array_get(jv_copy(a), i)); jv_free(a); return r; } else { 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; ielements[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; irefcnt}}; 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; istring) != 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; istring) == 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; istring) != 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; istring) != 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; istring) == 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; } int jv_object_has(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); int res = slot ? 1 : 0; jv_free(object); jv_free(key); return res; } 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 = memcmp(&a.u.number, &b.u.number, sizeof(a.u.number)) == 0; 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; } jq-jq-1.6/configure.ac0000600000175000017500000002253013366726451014205 0ustar czchenczchenm4_define([jq_version], m4_esyscmd_s([(git rev-parse --verify -q jq-1.0 > /dev/null && (git describe --tags --dirty --match 'jq-*'|sed 's/^jq-//')) || echo `git rev-parse --abbrev-ref HEAD`-`git describe --always --dirty`]))) AC_INIT([jq], [jq_version], [https://github.com/stedolan/jq/issues], [jq], [https://stedolan.github.io/jq]) m4_include([m4/ax_compare_version.m4]) 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 subdir-objects parallel-tests foreign -Wall]) AM_SILENT_RULES([yes]) AM_PROG_AR AM_MAINTAINER_MODE([enable]) AC_PROG_CC AC_PROG_CC_STDC AC_PROG_CPP_WERROR AC_PROG_YACC AC_OBJEXT AC_EXEEXT LT_INIT([shared static win32-dll]) AM_PROG_CC_C_O dnl couldn't use AM_PROG_LEX as it doesn't support header files like the dnl AC_PROG_YACC macros... dnl check bison version if test "$USE_MAINTAINER_MODE" = yes; then if test "$YACC" != "bison -y"; then AC_MSG_CHECKING([bison version]) AC_MSG_RESULT([not bison]) else AX_PROG_BISON_VERSION([3], [], [AC_MSG_ERROR([You need bison version 3.0 or greater, or use --disable-maintainer-mode.])]) fi AC_CHECK_PROGS(LEX, flex lex) fi dnl Check for valgrind AC_CHECK_PROGS(valgrind_cmd, valgrind) if test "x$valgrind_cmd" = "x" ; then AC_MSG_WARN([valgrind is required to test jq.]) fi AC_CHECK_FUNCS(memmem) AC_CHECK_FUNCS(mkstemp) AC_CHECK_HEADER("shlwapi.h",[have_win32=1;]) AM_CONDITIONAL([WIN32], [test "x$have_win32" = x1]) dnl Running tests with Valgrind is slow. It is faster to iterate on dnl code without Valgrind until tests pass, then enable Valgrind and dnl fix leaks. AC_ARG_ENABLE([valgrind], AC_HELP_STRING([--disable-valgrind], [do not run tests under Valgrind])) dnl Running tests with Valgrind is slow; address sanitizer (ASAN) is dnl faster. AC_ARG_ENABLE([asan], AC_HELP_STRING([--enable-asan], [enable address sanitizer])) dnl Undefined Behavior Sanitizer AC_ARG_ENABLE([ubsan], AC_HELP_STRING([--enable-ubsan], [enable undefined behavior sanitizer])) dnl Code coverage AC_ARG_ENABLE([gcov], AC_HELP_STRING([--enable-gcov], [enable gcov code coverage tool])) dnl Don't attempt to build docs if there's no Ruby lying around AC_ARG_ENABLE([docs], AC_HELP_STRING([--disable-docs], [don't build docs])) dnl Don't attempt to build the error injection object (if there is no LD_PRELOAD support) AC_ARG_ENABLE([error-injection], AC_HELP_STRING([--enable-error-injection], [build and test with error injection])) dnl Enable building all static AC_ARG_ENABLE([all-static], AC_HELP_STRING([--enable-all-static], [link jq with static libraries only])) AS_IF([test "x$enable_docs" != "xno"],[ AC_CHECK_PROGS(bundle_cmd, bundle) AC_CACHE_CHECK([for Ruby dependencies], [jq_cv_ruby_deps], [jq_cv_ruby_deps=yes; AS_IF([test "x$bundle_cmd" = "x" || \ ! bmsg="`cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null`"],[ AC_MSG_WARN([$bmsg]) cat <], [0]) AC_FIND_FUNC([_isatty], [c], [#include ], [0]) AC_FIND_FUNC([strptime], [c], [#include ], [0, 0, 0]) AC_FIND_FUNC([strftime], [c], [#include ], [0, 0, 0, 0]) AC_FIND_FUNC([timegm], [c], [#include ], [0]) AC_FIND_FUNC([gmtime_r], [c], [#include ], [0, 0]) AC_FIND_FUNC([gmtime], [c], [#include ], [0]) AC_FIND_FUNC([localtime_r], [c], [#include ], [0, 0]) AC_FIND_FUNC([localtime], [c], [#include ], [0]) AC_FIND_FUNC([gettimeofday], [c], [#include ], [0, 0]) AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define to 1 if the system has the tm_gmt_off field in struct tm])], [], [[#include ]]) AC_CHECK_MEMBER([struct tm.__tm_gmtoff], [AC_DEFINE([HAVE_TM___TM_GMT_OFF],1,[Define to 1 if the system has the __tm_gmt_off field in struct tm])], [], [[#include ]]) AC_ARG_ENABLE([pthread-tls], [AC_HELP_STRING([--enable-pthread-tls], [Enable use of pthread thread local storage])], [], [enable_pthread_tls=no]) if test $enable_pthread_tls = yes; then AC_FIND_FUNC([pthread_key_create], [pthread], [#include ], [NULL, NULL]) AC_FIND_FUNC([pthread_once], [pthread], [#include ], [NULL, NULL]) AC_FIND_FUNC([atexit], [pthread], [#include ], [NULL]) fi dnl libm math.h functions AC_CHECK_MATH_FUNC(acos) AC_CHECK_MATH_FUNC(acosh) AC_CHECK_MATH_FUNC(asin) AC_CHECK_MATH_FUNC(asinh) AC_CHECK_MATH_FUNC(atan2) AC_CHECK_MATH_FUNC(atan) AC_CHECK_MATH_FUNC(atanh) AC_CHECK_MATH_FUNC(cbrt) AC_CHECK_MATH_FUNC(ceil) AC_CHECK_MATH_FUNC(copysign) AC_CHECK_MATH_FUNC(cos) AC_CHECK_MATH_FUNC(cosh) AC_CHECK_MATH_FUNC(drem) AC_CHECK_MATH_FUNC(erf) AC_CHECK_MATH_FUNC(erfc) AC_CHECK_MATH_FUNC(exp10) AC_CHECK_MATH_FUNC(exp2) AC_CHECK_MATH_FUNC(exp) AC_CHECK_MATH_FUNC(expm1) AC_CHECK_MATH_FUNC(fabs) AC_CHECK_MATH_FUNC(fdim) AC_CHECK_MATH_FUNC(floor) AC_CHECK_MATH_FUNC(fma) AC_CHECK_MATH_FUNC(fmax) AC_CHECK_MATH_FUNC(fmin) AC_CHECK_MATH_FUNC(fmod) AC_CHECK_MATH_FUNC(frexp) AC_CHECK_MATH_FUNC(gamma) AC_CHECK_MATH_FUNC(hypot) AC_CHECK_MATH_FUNC(j0) AC_CHECK_MATH_FUNC(j1) AC_CHECK_MATH_FUNC(jn) AC_CHECK_MATH_FUNC(ldexp) AC_CHECK_MATH_FUNC(lgamma) AC_CHECK_MATH_FUNC(log10) AC_CHECK_MATH_FUNC(log1p) AC_CHECK_MATH_FUNC(log2) AC_CHECK_MATH_FUNC(log) AC_CHECK_MATH_FUNC(logb) AC_CHECK_MATH_FUNC(modf) AC_CHECK_MATH_FUNC(lgamma_r) AC_CHECK_MATH_FUNC(nearbyint) AC_CHECK_MATH_FUNC(nextafter) AC_CHECK_MATH_FUNC(nexttoward) AC_CHECK_MATH_FUNC(pow10) # Not available with glibc version >= 2.27 AC_CHECK_MATH_FUNC(pow) AC_CHECK_MATH_FUNC(remainder) AC_CHECK_MATH_FUNC(rint) AC_CHECK_MATH_FUNC(round) AC_CHECK_MATH_FUNC(scalb) AC_CHECK_MATH_FUNC(scalbln) AC_CHECK_MATH_FUNC(significand) AC_CHECK_MATH_FUNC(sin) AC_CHECK_MATH_FUNC(sinh) AC_CHECK_MATH_FUNC(sqrt) AC_CHECK_MATH_FUNC(tan) AC_CHECK_MATH_FUNC(tanh) AC_CHECK_MATH_FUNC(tgamma) AC_CHECK_MATH_FUNC(trunc) AC_CHECK_MATH_FUNC(y0) AC_CHECK_MATH_FUNC(y1) AC_CHECK_MATH_FUNC(yn) dnl Thread local storage have___thread=no AC_MSG_CHECKING(for thread-local storage) AC_LINK_IFELSE([AC_LANG_SOURCE([ static __thread int x ; int main () { x = 123; return x; } ])], have___thread=yes) if test $have___thread = yes; then AC_DEFINE([HAVE___THREAD],1,[Define to 1 if the system supports __thread]) fi AC_MSG_RESULT($have___thread) AC_C_BIGENDIAN( AC_DEFINE([IEEE_MC68k], 1, [machine is bigendian]), AC_DEFINE([IEEE_8087], 1, [machine is littleendian]), AC_MSG_ERROR(unknown endianess), AC_MSG_ERROR(universial endianess not supported) ) dnl Oniguruma AC_ARG_WITH([oniguruma], [AS_HELP_STRING([--with-oniguruma=prefix], [try this for a non-standard install prefix of the oniguruma library])], , [with_oniguruma=yes]) onig_CFLAGS= onig_LDFLAGS= build_oniguruma=no AS_IF([test "x$with_oniguruma" != xno], [ save_CFLAGS="$CFLAGS" save_LDFLAGS="$LDFLAGS" AS_IF([test "x$with_oniguruma" != xyes], [ AS_IF([test "x$with_oniguruma" = xbuiltin], [ build_oniguruma=yes ], [ onig_CFLAGS="-I${with_oniguruma}/include" onig_LDFLAGS="-L${with_oniguruma}/lib" ]) ]) AS_IF([test "x$build_oniguruma" = xno], [ # check for ONIGURUMA library, either in /usr or where requested CFLAGS="$CFLAGS $onig_CFLAGS" LDFLAGS="$LDFLAGS $onig_LDFLAGS" AC_CHECK_HEADER("oniguruma.h", AC_CHECK_LIB([onig],[onig_version])) # handle check results AS_IF([test "x$ac_cv_lib_onig_onig_version" != "xyes"], [ build_oniguruma=yes AC_MSG_NOTICE([Oniguruma was not found. Will use the packaged oniguruma.]) ]) ]) AS_IF([test "x$build_oniguruma" = xyes -a -f "${srcdir}/modules/oniguruma/configure.ac" ], [ onig_CFLAGS="-I${srcdir}/modules/oniguruma/src" onig_LDFLAGS="-L${srcdir}/modules/oniguruma/src -Wl,-rpath,${libdir}" AC_CONFIG_SUBDIRS([modules/oniguruma]) AC_DEFINE([HAVE_LIBONIG],1,[Define to 1 if the system includes libonig]) ]) CFLAGS="$save_CFLAGS" LDFLAGS="$save_LDFLAGS" ]) AC_SUBST(onig_CFLAGS) AC_SUBST(onig_LDFLAGS) AM_CONDITIONAL([BUILD_ONIGURUMA], [test "x$build_oniguruma" = xyes]) AC_SUBST([BUNDLER], ["$bundle_cmd"]) AC_CONFIG_MACRO_DIR([config/m4]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT jq-jq-1.6/README.md0000600000175000017500000000574513366726451013207 0ustar czchenczchenjq == jq is a lightweight and flexible command-line JSON processor. [![Coverage Status](https://coveralls.io/repos/stedolan/jq/badge.svg?branch=master&service=github)](https://coveralls.io/github/stedolan/jq?branch=master), Unix: [![Build Status](https://travis-ci.org/stedolan/jq.svg?branch=master)](https://travis-ci.org/stedolan/jq), Windows: [![Windows build status](https://ci.appveyor.com/api/projects/status/mi816811c9e9mx29?svg=true)](https://ci.appveyor.com/project/stedolan/jq) If you want to learn to use jq, read the documentation at [https://stedolan.github.io/jq](https://stedolan.github.io/jq). This documentation is generated from the docs/ folder of this repository. You can also try it online at [jqplay.org](https://jqplay.org). If you want to hack on jq, feel free, but be warned that its internals are not well-documented at the moment. Bring a hard hat and a shovel. Also, read the wiki: https://github.com/stedolan/jq/wiki, where you will find cookbooks, discussion of advanced topics, internals, release engineering, and more. Source tarball and built executable releases can be found on the homepage and on the github release page, https://github.com/stedolan/jq/releases If you're building directly from the latest git, you'll need flex, bison (3.0 or newer), libtool, make, and autoconf installed. To get regexp support you'll also need to install Oniguruma or clone it as a git submodule as per the instructions below. (note that jq's tests require regexp support to pass). To build, run: git submodule update --init # if building from git to get oniguruma autoreconf -fi # if building from git ./configure --with-oniguruma=builtin make -j8 make check To build without bison or flex, add `--disable-maintainer-mode` to the ./configure invocation: ./configure --with-oniguruma=builtin --disable-maintainer-mode (Developers must not use `--disable-maintainer-mode`, not when making changes to the jq parser and/or lexer.) To build a statically linked version of jq, run: make LDFLAGS=-all-static After make finishes, you'll be able to use `./jq`. You can also install it using: sudo make install If you're not using the latest git version but instead building a released tarball (available on the website), then you won't need to run `autoreconf` (and shouldn't), and you won't need flex or bison. To cross-compile for OS X and Windows, see docs/Rakefile's build task and scripts/crosscompile. You'll need a cross-compilation environment, such as Mingw for cross-compiling for Windows. Cross-compilation requires a clean workspace, then: # git clean ... autoreconf -i ./configure make distclean scripts/crosscompile Use the --host= and --target= ./configure options to select a cross-compilation environment. See also the wiki. Send questions to https://stackoverflow.com/questions/tagged/jq or to the #jq channel (http://irc.lc/freenode/%23jq/) on Freenode (https://webchat.freenode.net/). jq-jq-1.6/COPYING0000600000175000017500000000557413366726451012763 0ustar czchenczchenjq is copyright (C) 2012 Stephen Dolan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jq's documentation (everything found under the docs/ subdirectory in the source tree) is licensed under the Creative Commons CC BY 3.0 license, which can be found at: https://creativecommons.org/licenses/by/3.0/ The documentation website includes a copy of Twitter's Boostrap and relies on Bonsai, Liquid templates and various other projects, look them up for detailed licensing conditions. jq incorporates David M. Gay's dtoa.c and g_fmt.c, which bear the following notices: dtoa.c: 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. g_fmt.c: 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. jq-jq-1.6/.travis.yml0000600000175000017500000001534313366726451014034 0ustar czchenczchensudo: false language: c stages: - name: test - name: build # Don't run build stage for pull requests to save time and resources. if: type != pull_request jobs: include: # Build with gcc and run tests on Ubuntu. - &test-ubuntu stage: test os: linux compiler: gcc addons: apt: packages: - valgrind - bison - automake before_install: - uname -s - rvm install ruby-1.9.3-p551 - rvm use 1.9.3 - rm src/{lexer,parser}.{c,h} - sed -i.bak '/^AM_INIT_AUTOMAKE(\[-Wno-portability 1\.14\])$/s/14/11/' modules/oniguruma/configure.ac install: - bundle install --gemfile=docs/Gemfile - wget http://ftp.debian.org/debian/pool/main/b/bison/bison_3.0.2.dfsg-2_amd64.deb - ar p bison_3.0.2.dfsg-2_amd64.deb data.tar.xz | tar xJ - if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi before_script: # If this is OS X we'll get bison from brew, else we'll get bison # from the .deb unpacked above in the install section. - PATH=/usr/local/opt/bison/bin:$PWD/usr/bin:$PATH - echo SHELL=$SHELL - echo PATH=$PATH - which bison - bison --version - autoreconf -if - ./configure --with-oniguruma=builtin YACC="$(which bison) -y" $COVERAGE script: # When using the bison from Debian we need to tell that bison where # to find its data. Yay non-relocatable code. Not. - echo PATH=$PATH - which bison - make BISON_PKGDATADIR=$PWD/usr/share/bison src/parser.c || make src/parser.c # Make dist! # # Make it first to fail the build early, before we test with # valgrind. - make dist # Build and test the dist (without valgrind) - | ( tar xvf jq-`scripts/version`.tar.gz && cd jq-`scripts/version` && pwd && ./configure --disable-valgrind --with-oniguruma=builtin YACC="$(which bison) -y" $COVERAGE && make BISON_PKGDATADIR=$PWD/usr/share/bison src/parser.c || make src/parser.c && make -j4 && make check -j4 || true ) # Build and test the HEAD - make -j4 - make check -j4 after_failure: - cat test-suite.log - cat tests/*.log # Build with clang and run tests on Ubuntu. - <<: *test-ubuntu compiler: clang # Build with gcc and run tests with gcov on Ubuntu. - <<: *test-ubuntu env: COVERAGE="--disable-valgrind --enable-gcov" after_script: - rm -rf src/.libs # don't care about coverage for libjq - coveralls --gcov-options '\-lp' -e src/lexer.c -e src/parser.c -e src/jv_dtoa.c # Build with gcc and run tests on macOS. - &test-osx <<: *test-ubuntu os: osx before_install: - uname -s - brew update - brew install flex bison - rvm install ruby-1.9.3-p551 - rvm use 1.9.3 - gem install bundler - rm src/{lexer,parser}.{c,h} - sed -i.bak '/^AM_INIT_AUTOMAKE(\[-Wno-portability 1\.14\])$/s/14/11/' modules/oniguruma/configure.ac install: - bundle install --gemfile=docs/Gemfile - if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi # Build with clang and run tests on macOS. - <<: *test-osx compiler: clang # Build with gcc and run tests on Alpine Linux v3.7 (inside chroot). # Note: Alpine uses musl libc. - &test-alpine stage: test os: linux language: minimal compiler: gcc sudo: true before_install: - "wget 'https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.7.0/alpine-chroot-install' \ && echo '090d323d887ef3a2fd4e752428553f22a52b87bb alpine-chroot-install' | sha1sum -c || travis_terminate 1" - alpine() { /alpine/enter-chroot -u "$USER" "$@"; } install: - sudo sh alpine-chroot-install -b v3.7 -a "$ARCH" -p 'build-base automake autoconf bison libtool oniguruma-dev' before_script: - autoreconf -if script: - alpine ./configure --disable-docs - alpine make - alpine make check # Build release binary statically linked with musl libc on Alpine Linux # (inside chroot). If building a tagged commit, then deploy release # tarball to GitHub Releases. - &build-alpine <<: *test-alpine stage: build env: ARCH=x86_64 script: - alpine ./configure --disable-docs --enable-all-static CFLAGS='-Os -static -no-pie' CXXFLAGS='-Os -static -no-pie' - alpine make - alpine strip jq - jq -V - ls -lah jq - file jq # Ensure that the built executable is really statically linked. - file jq | grep -Fw 'statically linked' before_deploy: - PKGNAME="jq-$TRAVIS_TAG-$ARCH-linux" - mkdir $PKGNAME && mv jq $PKGNAME/ - tar -czf $PKGNAME.tar.gz $PKGNAME/ - sha256sum $PKGNAME.tar.gz > $PKGNAME.tar.gz.sha256 deploy: provider: releases api_key: secure: # TODO: put encrypted GitHub token here! file: jq-$TRAVIS_TAG-*.tar.gz* file_glob: true skip_cleanup: true on: tags: true # Build binaries for other architectures using QEMU user-mode emulation. - <<: *build-alpine env: ARCH=x86 - <<: *build-alpine env: ARCH=aarch64 - <<: *build-alpine env: ARCH=armhf - <<: *build-alpine env: ARCH=ppc64le notifications: email: false jq-jq-1.6/README0000777000175000017500000000000013366726451014061 2README.mdustar czchenczchenjq-jq-1.6/.gitmodules0000600000175000017500000000014713366726451014074 0ustar czchenczchen[submodule "modules/oniguruma"] path = modules/oniguruma url = https://github.com/kkos/oniguruma.git jq-jq-1.6/config/0000700000175000017500000000000013366726451013160 5ustar czchenczchenjq-jq-1.6/config/m4/0000700000175000017500000000000013366726451013500 5ustar czchenczchenjq-jq-1.6/config/m4/misc.m40000600000175000017500000000020213366726451014671 0ustar czchenczchen dnl Shamelessly stolen from Heimdal AC_DEFUN([upcase],[`echo $1 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`])dnl jq-jq-1.6/config/m4/check-math-func.m40000600000175000017500000000027713366726451016707 0ustar czchenczchendnl AC_CHECK_MATH_FUNC(func) AC_DEFUN([AC_CHECK_MATH_FUNC], [ AC_LANG(C) AC_CHECK_LIB([m],[$1],[ eval "ac_tr_func=HAVE_[]upcase($1)" AC_DEFINE_UNQUOTED($ac_tr_func) ],[ ]) ]) jq-jq-1.6/config/m4/find-func.m40000600000175000017500000000033613366726451015617 0ustar czchenczchendnl Shamelessly stolen from Heimdal dnl dnl AC_FIND_FUNC(func, libraries, includes, arguments) AC_DEFUN([AC_FIND_FUNC], [ AC_FIND_FUNC_NO_LIBS([$1], [$2], [$3], [$4]) if test -n "$LIB_$1"; then LIBS="$LIB_$1 $LIBS" fi ]) jq-jq-1.6/config/m4/find-func-no-libs.m40000600000175000017500000000043613366726451017161 0ustar czchenczchendnl Shamelessly stolen from Heimdal dnl dnl Look for function in any of the specified libraries dnl dnl AC_FIND_FUNC_NO_LIBS(func, libraries, includes, arguments, extra libs, extra args) AC_DEFUN([AC_FIND_FUNC_NO_LIBS], [ AC_FIND_FUNC_NO_LIBS2([$1], ["" $2], [$3], [$4], [$5], [$6])]) jq-jq-1.6/config/m4/find-func-no-libs2.m40000600000175000017500000000256413366726451017247 0ustar czchenczchendnl Shamelessly stolen from Heimdal dnl dnl Look for function in any of the specified libraries dnl dnl AC_FIND_FUNC_NO_LIBS2(func, libraries, includes, arguments, extra libs, extra args) AC_DEFUN([AC_FIND_FUNC_NO_LIBS2], [ AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(ac_cv_funclib_$1, [ if eval "test \"\$ac_cv_func_$1\" != yes" ; then ac_save_LIBS="$LIBS" for ac_lib in $2; do case "$ac_lib" in "") ;; yes) ac_lib="" ;; no) continue ;; -l*) ;; *) ac_lib="-l$ac_lib" ;; esac LIBS="$6 $ac_lib $5 $ac_save_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[$3]],[[$1($4)]])],[eval "if test -n \"$ac_lib\";then ac_cv_funclib_$1=$ac_lib; else ac_cv_funclib_$1=yes; fi";break]) done eval "ac_cv_funclib_$1=\${ac_cv_funclib_$1-no}" LIBS="$ac_save_LIBS" fi ]) eval "ac_res=\$ac_cv_funclib_$1" if false; then AC_CHECK_FUNCS($1) dnl AC_CHECK_LIBS($2, foo) fi # $1 eval "ac_tr_func=HAVE_[]upcase($1)" eval "ac_tr_lib=HAVE_LIB[]upcase($ac_res | sed -e 's/-l//')" eval "LIB_$1=$ac_res" case "$ac_res" in yes) eval "ac_cv_func_$1=yes" eval "LIB_$1=" AC_DEFINE_UNQUOTED($ac_tr_func) AC_MSG_RESULT([yes]) ;; no) eval "ac_cv_func_$1=no" eval "LIB_$1=" AC_MSG_RESULT([no]) ;; *) eval "ac_cv_func_$1=yes" eval "ac_cv_lib_`echo "$ac_res" | sed 's/-l//'`=yes" AC_DEFINE_UNQUOTED($ac_tr_func) AC_DEFINE_UNQUOTED($ac_tr_lib) AC_MSG_RESULT([yes, in $ac_res]) ;; esac AC_SUBST(LIB_$1) ]) jq-jq-1.6/Makefile.am0000600000175000017500000001462013366726451013754 0ustar czchenczchen ### C source files to be built and distributed. 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 AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \ -Wno-unused-parameter -Wno-unused-function ACLOCAL_AMFLAGS = -I config/m4 ### Generating the lexer and parser # 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 = src/lexer.h src/lexer.c src/parser.h src/parser.c \ src/builtin.inc 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 = src/builtin.inc src/version.h .y.c: $(AM_V_YACC) echo "NOT building parser.c!" .l.c: $(AM_V_LEX) echo "NOT building lexer.c!" endif # Tell YACC (bison) autoconf macros that you want a header file created. # If the --warnings=all fails, you probably have an old version of bison # OSX ships an old bison, so update with homebrew or macports AM_YFLAGS = --warnings=all -d ### libjq lib_LTLIBRARIES = libjq.la libjq_la_SOURCES = ${LIBJQ_SRC} libjq_la_LIBADD = -lm libjq_la_LDFLAGS = $(onig_LDFLAGS) -export-symbols-regex '^j[qv]_' -version-info 1:4:0 if WIN32 libjq_la_LIBADD += -lshlwapi endif include_HEADERS = src/jv.h src/jq.h if ENABLE_UBSAN AM_CFLAGS += -fsanitize=undefined endif ### Running tests under Valgrind if ENABLE_ASAN AM_CFLAGS += -fsanitize=address NO_VALGRIND = 1 else if ENABLE_VALGRIND NO_VALGRIND = else NO_VALGRIND = 1 endif endif ### Code coverage with gcov if ENABLE_GCOV AM_CFLAGS += --coverage --no-inline endif ### Error injection for testing if ENABLE_ERROR_INJECTION lib_LTLIBRARIES += libinject_errors.la libinject_errors_la_SOURCES = src/inject_errors.c libinject_errors_la_LIBADD = -ldl libinject_errors_la_LDFLAGS = -module endif ### Building the jq binary # Remake the version.h header file if, and only if, the git ID has changed .PHONY: .FORCE .FORCE: generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed 's/.*/\#define JQ_VERSION \"&\"/'`" .remake-version-h: .FORCE @ $(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" > $@ src/main.c: src/version.h src/builtin.inc: src/builtin.jq $(AM_V_GEN) sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $^ > $@ src/builtin.o: src/builtin.inc bin_PROGRAMS = jq jq_SOURCES = src/main.c src/version.h jq_LDFLAGS = -static-libtool-libs jq_LDADD = libjq.la -lm if WIN32 jq_LDADD += -lshlwapi endif if ENABLE_ALL_STATIC jq_LDFLAGS += -all-static endif ### Tests (make check) TESTS = tests/optionaltest tests/mantest tests/jqtest tests/onigtest tests/shtest tests/utf8test tests/base64test TESTS_ENVIRONMENT = NO_VALGRIND=$(NO_VALGRIND) ### Building the manpage man_MANS = jq.1 if ENABLE_DOCS jq.1: $(srcdir)/docs/content/3.manual/manual.yml $(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; } jq.1.prebuilt: jq.1 $(AM_V_GEN) cp $^ $@ || { rm -f $@; false; } else jq.1: $(srcdir)/jq.1.prebuilt $(AM_V_GEN) cp $^ $@ endif ### Build oniguruma if BUILD_ONIGURUMA libjq_la_LIBADD += modules/oniguruma/src/.libs/libonig.la SUBDIRS = modules/oniguruma endif AM_CFLAGS += $(onig_CFLAGS) ### Packaging docs/site.yml: configure.ac sed 's/^jq_version: .*/jq_version: "$(VERSION)"/' $@ > $@.new mv $@.new $@ install-binaries: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-exec DOC_FILES = docs/content docs/public docs/templates docs/site.yml \ docs/Gemfile docs/Gemfile.lock docs/Rakefile docs/README.md \ jq.1.prebuilt EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \ jq.1.prebuilt jq.spec src/lexer.c src/lexer.h src/parser.c \ src/parser.h src/version.h src/builtin.jq \ 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 \ tests/modules/syntaxerror/syntaxerror.jq \ tests/modules/test_bind_order.jq \ tests/modules/test_bind_order0.jq \ tests/modules/test_bind_order1.jq \ tests/modules/test_bind_order2.jq tests/onig.supp \ tests/onig.test tests/setup tests/torture/input0.json \ tests/optional.test tests/optionaltest \ tests/utf8-truncate.jq tests/utf8test \ tests/base64.test tests/base64test \ tests/jq-f-test.sh tests/shtest # README.md is expected in Github projects, good stuff in it, so we'll # distribute it and install it with the package in the doc directory. docdir = ${datadir}/doc/${PACKAGE} dist_doc_DATA = README.md COPYING AUTHORS README RELEASE ?= 1 rpm: dist jq.spec @echo "Packaging jq as an RPM ..." mkdir -p rpm/SOURCES rpm/BUILD rpm/BUILDROOT rpm/RPMS rpm/SPECS cp jq-$(VERSION).tar.gz rpm/SOURCES/ rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $(VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$(VERSION).tar.gz find rpm/RPMS/ -name "*.rpm" -exec mv {} ./ \; rm -rf rpm dist-clean-local: rm -f ${BUILT_SOURCES} # Not sure why this doesn't get cleaned up automatically, guess # automake used to man pages which are hand coded? # 'make clean' doesn't delete the manpage if it can't be rebuilt clean-local-docs: if ENABLE_DOCS rm -f jq.1 endif clean-local-gcov: rm -f src/*.gcno src/*.gcda src/*.gcov clean-local: clean-local-docs clean-local-gcov rm -f src/version.h .remake-version-h .PHONY: clean-local-docs clean-local-gcov jq-jq-1.6/docs/0000700000175000017500000000000013366726451012643 5ustar czchenczchenjq-jq-1.6/docs/templates/0000700000175000017500000000000013366726451014641 5ustar czchenczchenjq-jq-1.6/docs/templates/manual.liquid0000600000175000017500000000743613366726451017343 0ustar czchenczchen {% include "shared/head" %} {% include "shared/navbar" %}

{{headline}}

{{ history | markdownify }} {{ body | markdownify }} {% for section in sections %}

{{section.title}}

{{section.body | markdownify}} {% for entry in section.entries %}

{{entry.title | markdownify | no_paragraph}} {% if entry.subtitle %}{{entry.subtitle}}{% endif %}

{{entry.body | markdownify}} {% if entry.examples %}
{% capture exampleID %}{{ "" | unique }}{% endcapture %} {% if entry.examples[1] %}Examples{%else%}Example{%endif%}
{% for example in entry.examples %} {% unless example.output[0] %} {% endunless %} {% for output in example.output %} {% if forloop.index == 1 %} {% else %} {% endif %} {% endfor %}
jq '{{example.program | escape}}'
Input{{example.input | escape}}
Output none
Output{{output | escape}}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{% include "shared/footer" %} jq-jq-1.6/docs/templates/default.liquid0000600000175000017500000000170713366726451017505 0ustar czchenczchen {% include "shared/head" %} {% include "shared/navbar" %}

{{headline}}

{% for item in body %} {% if item.text %} {{ item.text | replace: '$JQ_VERSION', jq_version | markdownify }} {% endif %} {% if item.command %} {% capture resultID %}{{ "result" | unique}}{% endcapture %}
{{item.command}}
Show result
{{item.result}}
{% endif %} {% endfor %}
{% include "shared/footer" %} jq-jq-1.6/docs/templates/shared/0000700000175000017500000000000013366726451016107 5ustar czchenczchenjq-jq-1.6/docs/templates/shared/_head.liquid0000600000175000017500000000201013366726451020353 0ustar czchenczchen {{headline}} jq-jq-1.6/docs/templates/shared/_footer.liquid0000600000175000017500000000160513366726451020761 0ustar czchenczchen jq-jq-1.6/docs/templates/shared/_navbar.liquid0000600000175000017500000000234613366726451020737 0ustar czchenczchen jq-jq-1.6/docs/templates/index.liquid0000600000175000017500000000465613366726451017176 0ustar czchenczchen {% include "shared/head" %} {% include "shared/navbar" %}
{{body1 | markdownify}}
{{body2 | markdownify}}
{{body3 | markdownify}}
{{tail | markdownify}}

News

    {% for item in news %}
  • {{item.date}} {{item.body | markdownify}}
  • {% endfor %}
{% include "shared/footer" %} jq-jq-1.6/docs/Gemfile.lock0000600000175000017500000000231613366726451015071 0ustar czchenczchenGEM remote: https://rubygems.org/ specs: activesupport (4.2.3) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) addressable (2.3.8) bonsai (1.4.9) activesupport (>= 3.0.3) builder (>= 3.0.0) i18n (>= 0.5.0) launchy (>= 0.3.7) liquid (>= 2.2.2) maruku (>= 0.6.0) rack sass sinatra (>= 1.0) tilt (>= 1.3) watch (>= 0.1.0) yui-compressor builder (3.2.2) hpricot (0.8.6) i18n (0.7.0) json (1.8.3) launchy (2.4.3) addressable (~> 2.3) liquid (3.0.6) maruku (0.7.2) minitest (5.8.0) mustache (0.99.8) rack (1.6.4) rack-protection (1.5.3) rack rake (10.4.2) rdiscount (2.1.8) ronn (0.7.3) hpricot (>= 0.8.2) mustache (>= 0.7.0) rdiscount (>= 1.5.8) sass (3.4.16) sinatra (1.4.6) rack (~> 1.4) rack-protection (~> 1.4) tilt (>= 1.3, < 3) thread_safe (0.3.5) tilt (2.0.1) tzinfo (1.2.2) thread_safe (~> 0.1) watch (0.1.0) yui-compressor (0.12.0) PLATFORMS ruby DEPENDENCIES bonsai maruku mustache (< 1.0) rake ronn jq-jq-1.6/docs/Rakefile0000600000175000017500000000133313366726451014312 0ustar czchenczchencurrent_dir = File.dirname(__FILE__) rakefile_manual = File.expand_path(File.join(current_dir, "Rakefile.manual")) rakefile_website = File.expand_path(File.join(current_dir, "Rakefile.website")) desc "Build the manpage from the bonsai source of the manual" task :manpage do system %(#{$0} -f #{rakefile_manual} manpage) end desc "Collect jq unit test cases from the bonsai source of the manual" task :mantests do system %(#{$0} -f #{rakefile_manual} mantests) end desc "Build the website from the bonsai sources" task :build do system %(#{$0} -f #{rakefile_website} build) end desc "Serve a live view of the website on http://localhost:5000/jq/" task :serve do system %(#{$0} -f #{rakefile_website} serve) end jq-jq-1.6/docs/content/0000700000175000017500000000000013366726451014315 5ustar czchenczchenjq-jq-1.6/docs/content/2.download/0000700000175000017500000000000013366726451016264 5ustar czchenczchenjq-jq-1.6/docs/content/2.download/default.yml0000600000175000017500000001777713366726451020460 0ustar czchenczchenheadline: Download jq body: - text: | jq is written in C and has no runtime dependencies, so it should be possible to build it for nearly any platform. Prebuilt binaries are available for Linux, OS X and Windows. The binaries should just run, but on OS X and Linux you may need to make them executable first using `chmod +x jq`. jq is licensed under the MIT license. For all of the gory details, read the file `COPYING` in the source distribution. ### Linux * jq 1.5 is in the official [Debian](https://packages.debian.org/jq) and [Ubuntu](http://packages.ubuntu.com/jq) repositories. Install using `sudo apt-get install jq`. * jq 1.5 is in the official [Fedora](http://pkgs.fedoraproject.org/cgit/jq.git/) repository. Install using `sudo dnf install jq`. * jq 1.4 is in the official [openSUSE](https://software.opensuse.org/package/jq) repository. Install using `sudo zypper install jq`. * jq 1.5 is in the official [Arch](https://www.archlinux.org/packages/?sort=&q=jq&maintainer=&flagged=) repository. Install using `sudo pacman -Sy jq`. * jq 1.6 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux32). * jq 1.5 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux32). * jq 1.4 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-linux-x86_64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-linux-x86). * jq 1.3 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-linux-x86_64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-linux-x86). ### OS X * Use [Homebrew](http://brew.sh/) to install jq 1.5 with `brew install jq`. * jq 1.6 binary for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64). * jq 1.5 binary for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-osx-amd64). * jq 1.4 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-osx-x86_64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-osx-x86). * jq 1.3 binaries for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-osx-x86_64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-osx-x86). ### FreeBSD * `pkg install jq` as root installs a pre-built [binary package](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/pkgng-intro.html). * `make -C /usr/ports/textproc/jq install clean` as root installs the [jq](https://www.freshports.org/textproc/jq/) [port](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-using.html) from source. ### Solaris * `pkgutil -i jq` in [OpenCSW](https://www.opencsw.org/p/jq) for Solaris 10+, Sparc and x86. * jq 1.4 binaries for Solaris 11 [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-solaris11-64) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-solaris11-32). ### Windows * Use [Chocolatey NuGet](https://chocolatey.org/) to install jq 1.5 with `chocolatey install jq`. * jq 1.6 executables for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win64.exe) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win32.exe). * jq 1.5 executables for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-win64.exe) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-win32.exe). * jq 1.4 executables for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-win64.exe) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.4/jq-win32.exe). * jq 1.3 executables for [64-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-win64.exe) or [32-bit](https://github.com/stedolan/jq/releases/download/jq-1.3/jq-win32.exe). ### Checksums and signatures SHA-256 checksums are provided for all release and pre-release binaries. They can be found under [sig/v1.x/sha256sum.txt](https://github.com/stedolan/jq/tree/master/sig). The checksums for jq 1.6 are in [sig/v1.6/sha256sum.txt](https://raw.githubusercontent.com/stedolan/jq/master/sig/v1.6/sha256sum.txt). The checksums for jq 1.5 are in [sig/v1.5/sha256sum.txt](https://raw.githubusercontent.com/stedolan/jq/master/sig/v1.5/sha256sum.txt). Additionally, all binaries are signed by the [jq Package Signing Key](https://raw.githubusercontent.com/stedolan/jq/master/sig/jq-release.key). The signatures can be found under [sig/v1.x/\*.asc](https://github.com/stedolan/jq/tree/master/sig). The signatures for jq 1.6 are in [sig/v1.5/\*.asc](https://github.com/stedolan/jq/tree/master/sig/v1.6). The signatures for jq 1.5 are in [sig/v1.5/\*.asc](https://github.com/stedolan/jq/tree/master/sig/v1.5). You can use [GnuPG](https://gnupg.org/) to verify a signature by downloading the signature and running `gpg --verify signature.asc`. ### From source on Linux, OS X, Cygwin, and other POSIX-like operating systems * [Source tarball for jq 1.6](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz) * [Source tarball for jq 1.5](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-1.5.tar.gz) You can build it using the usual `./configure && make && sudo make install` rigmarole. If you're interested in using the lastest development version, try: git clone https://github.com/stedolan/jq.git cd jq autoreconf -i ./configure --disable-maintainer-mode make sudo make install To build it from a git clone, you'll need to install a few packages first: * [GCC](https://gcc.gnu.org) * [Make](https://www.gnu.org/software/make/) * [Autotools](https://www.gnu.org/software/automake/) For Linux systems, these will all be in your system's package manager, and if you do development on the machine they're most likely already installed. On OS X, these are all included in Apple's command line tools, which can be installed from [Xcode](https://developer.apple.com/xcode/). However, you may find that you need a newer version of Bison than the one provided by Apple. This can be found in [Homebrew](http://brew.sh) or [MacPorts](https://macports.org/). The `--disable-maintainer-mode` flag says to use the pre-generated lexer and parser that come with the code. To compile the lexer and parser also from source, leave out this flag. You will need to install [Flex](http://flex.sourceforge.net/) and [Bison](https://www.gnu.org/software/bison/). #### Building the documentation jq's documentation is compiled into static HTML using [Bonsai](http://www.tinytree.info). To view the documentation locally, run `rake serve` (or `bundle exec rake serve`) from the docs/ subdirectory. To build the docs just `rake build` from the docs subdirectory. You'll need a few Ruby dependencies, which can be installed by following the instructions in `docs/README.md`. The man page is built by `make jq.1`, or just `make`, also from the YAML docs, and you'll still need the Ruby dependencies to build the manpage. jq-jq-1.6/docs/content/1.tutorial/0000700000175000017500000000000013366726451016317 5ustar czchenczchenjq-jq-1.6/docs/content/1.tutorial/default.yml0000600000175000017500000003304113366726451020471 0ustar czchenczchenheadline: Tutorial body: - text: | GitHub has a JSON API, so let's play with that. This URL gets us the last 5 commits from the jq repo. - command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5'" result: | [ { "sha": "d25341478381063d1c76e81b3a52e0592a7c997f", "commit": { "author": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "committer": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "tree": { "sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40", "url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40" }, "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "comment_count": 0 }, "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f", "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments", "author": { "login": "stedolan", ... - text: | GitHub returns nicely formatted JSON. For servers that don't, it can be helpful to pipe the response through jq to pretty-print it. The simplest jq program is the expression `.`, which takes the input and produces it unchanged as output. - command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.'" result: | [ { "sha": "d25341478381063d1c76e81b3a52e0592a7c997f", "commit": { "author": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "committer": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "tree": { "sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40", "url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40" }, "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "comment_count": 0 }, "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f", "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments", "author": { "login": "stedolan", ... - text: | We can use jq to extract just the first commit. - command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]'" result: | { "sha": "d25341478381063d1c76e81b3a52e0592a7c997f", "commit": { "author": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "committer": { "name": "Stephen Dolan", "email": "mu@netsoc.tcd.ie", "date": "2013-06-22T16:30:59Z" }, "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "tree": { "sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40", "url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40" }, "url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "comment_count": 0 }, "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f", "html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f", "comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments", "author": { "login": "stedolan", "id": 79765, "avatar_url": "https://avatars.githubusercontent.com/u/79765?v=3", "gravatar_id": "", "url": "https://api.github.com/users/stedolan", "html_url": "https://github.com/stedolan", "followers_url": "https://api.github.com/users/stedolan/followers", "following_url": "https://api.github.com/users/stedolan/following{/other_user}", "gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}", "starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/stedolan/subscriptions", "organizations_url": "https://api.github.com/users/stedolan/orgs", "repos_url": "https://api.github.com/users/stedolan/repos", "events_url": "https://api.github.com/users/stedolan/events{/privacy}", "received_events_url": "https://api.github.com/users/stedolan/received_events", "type": "User", "site_admin": false }, "committer": { "login": "stedolan", "id": 79765, "avatar_url": "https://avatars.githubusercontent.com/u/79765?v=3", "gravatar_id": "", "url": "https://api.github.com/users/stedolan", "html_url": "https://github.com/stedolan", "followers_url": "https://api.github.com/users/stedolan/followers", "following_url": "https://api.github.com/users/stedolan/following{/other_user}", "gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}", "starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/stedolan/subscriptions", "organizations_url": "https://api.github.com/users/stedolan/orgs", "repos_url": "https://api.github.com/users/stedolan/repos", "events_url": "https://api.github.com/users/stedolan/events{/privacy}", "received_events_url": "https://api.github.com/users/stedolan/received_events", "type": "User", "site_admin": false }, "parents": [ { "sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7", "url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7", "html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7" }, { "sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a", "url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a", "html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a" } ] } - text: | For the rest of the examples, I'll leave out the `curl` command - it's not going to change. There's a lot of info we don't care about there, so we'll restrict it down to the most interesting fields. - command: "jq '.[0] | {message: .commit.message, name: .commit.committer.name}'" result: | { "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "name": "Stephen Dolan" } - text: | The `|` operator in jq feeds the output of one filter (`.[0]` which gets the first element of the array in the response) into the input of another (`{...}` which builds an object out of those fields). You can access nested attributes, such as `.commit.message`. Now let's get the rest of the commits. - command: "jq '.[] | {message: .commit.message, name: .commit.committer.name}'" result: | { "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "name": "Stephen Dolan" } { "message": "Reject all overlong UTF8 sequences.", "name": "Stephen Dolan" } { "message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences.", "name": "Stephen Dolan" } { "message": "Fix example in manual for `floor`. See #155.", "name": "Stephen Dolan" } { "message": "Document floor", "name": "Nicolas Williams" } - text: | `.[]` returns each element of the array returned in the response, one at a time, which are all fed into `{message: .commit.message, name: .commit.committer.name}`. Data in jq is represented as streams of JSON values - every jq expression runs for each value in its input stream, and can produce any number of values to its output stream. Streams are serialised by just separating JSON values with whitespace. This is a `cat`-friendly format - you can just join two JSON streams together and get a valid JSON stream. If you want to get the output as a single array, you can tell jq to "collect" all of the answers by wrapping the filter in square brackets: - command: "jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'" result: | [ { "message": "Merge pull request #163 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "name": "Stephen Dolan" }, { "message": "Reject all overlong UTF8 sequences.", "name": "Stephen Dolan" }, { "message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences.", "name": "Stephen Dolan" }, { "message": "Fix example in manual for `floor`. See #155.", "name": "Stephen Dolan" }, { "message": "Document floor", "name": "Nicolas Williams" } ] - text: | - - - Next, let's try getting the URLs of the parent commits out of the API results as well. In each commit, the GitHub API includes information about "parent" commits. There can be one or many. "parents": [ { "sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7", "url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7", "html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7" }, { "sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a", "url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a", "html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a" } ] We want to pull out all of the "html_url" fields inside that array of parent commits and make a simple list of strings to go along with the "message" and "author" fields we already have. - command: "jq '[.[] | {message: .commit.message, name: .commit.committer.name, parents: [.parents[].html_url]}]'" result: | [ { "message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161", "name": "Stephen Dolan", "parents": [ "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7", "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a" ] }, { "message": "Reject all overlong UTF8 sequences.", "name": "Stephen Dolan", "parents": [ "https://github.com/stedolan/jq/commit/ff48bd6ec538b01d1057be8e93b94eef6914e9ef" ] }, { "message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences.", "name": "Stephen Dolan", "parents": [ "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7" ] }, { "message": "Fix example in manual for `floor`. See #155.", "name": "Stephen Dolan", "parents": [ "https://github.com/stedolan/jq/commit/3dcdc582ea993afea3f5503a78a77675967ecdfa" ] }, { "message": "Document floor", "name": "Nicolas Williams", "parents": [ "https://github.com/stedolan/jq/commit/7c4171d414f647ab08bcd20c76a4d8ed68d9c602" ] } ] - text: | Here we're making an object as before, but this time the `parents` field is being set to `[.parents[].html_url]`, which collects all of the parent commit URLs defined in the parents object. - text: | - - - Here endeth the tutorial! There's lots more to play with. Go read [the manual](../manual/) if you're interested, and [download jq](../download/) if you haven't already. jq-jq-1.6/docs/content/index/0000700000175000017500000000000013366726451015424 5ustar czchenczchenjq-jq-1.6/docs/content/index/index.yml0000600000175000017500000000411113366726451017255 0ustar czchenczchenheadline: jq blurb: | jq is a lightweight and flexible command-line JSON processor. body1: | jq is like `sed` for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that `sed`, `awk`, `grep` and friends let you play with text. body2: | jq is written in portable C, and it has zero runtime dependencies. You can download a single binary, `scp` it to a far away machine of the same type, and expect it to work. body3: | jq can mangle the data format that you have into the one that you want with very little effort, and the program to do so is often shorter and simpler than you'd expect. tail: | Go read the [tutorial](/jq/tutorial/) for more, or the [manual](/jq/manual/) for *way* more. Ask questions on [stackoverflow](https://stackoverflow.com/) using the [jq tag](https://stackoverflow.com/questions/tagged/jq), or on the [#jq](http://irc.lc/freenode/%23jq/) channel on [Freenode](https://webchat.freenode.net/). news: - date: 1 November 2018 body: | jq 1.6 released. See installation options on the [download](/jq/download/) page, and the [release notes](https://github.com/stedolan/jq/releases/tag/jq-1.6) for details. - date: 15 August 2015 body: | jq 1.5 released, including new datetime, math, and regexp functions, try/catch syntax, array and object destructuring, a streaming parser, and a module system. See installation options on the [download](/jq/download/) page, and the [release notes](https://github.com/stedolan/jq/releases/tag/jq-1.5) for details. - date: 26 July 2015 body: | jq 1.5rc2 is available. Get it on the [releases](https://github.com/stedolan/jq/releases) page. - date: 01 January 2015 body: | jq 1.5rc1 is available. Get it on the [releases](https://github.com/stedolan/jq/releases) page. - date: 09 June 2014 body: | jq 1.4 (finally) released! Get it on the [download](/jq/download/) page. - date: 19 May 2013 body: | jq 1.3 released. jq-jq-1.6/docs/content/3.manual/0000700000175000017500000000000013366726451015733 5ustar czchenczchenjq-jq-1.6/docs/content/3.manual/manual.yml0000600000175000017500000036272213366726451017751 0ustar czchenczchen--- headline: jq Manual (development version) history: | *For released versions, see [jq 1.6](/jq/manual/v1.6), [jq 1.5](/jq/manual/v1.5), [jq 1.4](/jq/manual/v1.4) or [jq 1.3](/jq/manual/v1.3).* body: | A jq program is a "filter": it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks. Filters can be combined in various ways - you can pipe the output of one filter into another filter, or collect the output of a filter into an array. Some filters produce multiple results, for instance there's one that produces all the elements of its input array. Piping that filter into a second runs the second filter for each element of the array. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq. It's important to remember that every filter has an input and an output. Even literals like "hello" or 42 are filters - they take an input but always produce the same literal as output. Operations that combine two filters, like addition, generally feed the same input to both and combine the results. So, you can implement an averaging filter as `add / length` - feeding the input array both to the `add` filter and the `length` filter and then performing the division. But that's getting ahead of ourselves. :) Let's start with something simpler: manpage_intro: | jq(1) -- Command-line JSON processor ==================================== ## SYNOPSIS `jq` [...] [...] `jq` can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents. For instance, running the command `jq 'map(.price) | add'` will take an array of JSON objects as input and return the sum of their "price" fields. `jq` can accept text input as well, but by default, `jq` reads a stream of JSON entities (including numbers and other literals) from `stdin`. Whitespace is only needed to separate entities such as 1 and 2, and true and false. One or more may be specified, in which case `jq` will read input from those instead. The are described in the [INVOKING JQ] section; they mostly concern input and output formatting. The is written in the jq language and specifies how to transform the input file or document. ## FILTERS manpage_epilogue: | ## BUGS Presumably. Report them or discuss them at: https://github.com/stedolan/jq/issues ## AUTHOR Stephen Dolan `` sections: - title: Invoking jq body: | jq filters run on a stream of JSON data. The input to jq is parsed as a sequence of whitespace-separated JSON values which are passed through the provided filter one at a time. The output(s) of the filter are written to standard out, again as a sequence of whitespace-separated JSON data. Note: it is important to mind the shell's quoting rules. As a general rule it's best to always quote (with single-quote characters) the jq program, as too many characters with special meaning to jq are also shell meta-characters. For example, `jq "foo"` will fail on most Unix shells because that will be the same as `jq foo`, which will generally fail because `foo is not defined`. When using the Windows command shell (cmd.exe) it's best to use double quotes around your jq program when given on the command-line (instead of the `-f program-file` option), but then double-quotes in the jq program need backslash escaping. You can affect how jq reads and writes its input and output using some command-line options: * `--version`: Output the jq version and exit with zero. * `--seq`: Use the `application/json-seq` MIME type scheme for separating JSON texts in jq's input and output. This means that an ASCII RS (record separator) character is printed before each value on output and an ASCII LF (line feed) is printed after every output. Input JSON texts that fail to parse are ignored (but warned about), discarding all subsequent input until the next RS. This mode also parses the output of jq without the `--seq` option. * `--stream`: Parse the input in streaming fashion, outputing arrays of path and leaf values (scalars and empty arrays or empty objects). For example, `"a"` becomes `[[],"a"]`, and `[[],"a",["b"]]` becomes `[[0],[]]`, `[[1],"a"]`, and `[[1,0],"b"]`. This is useful for processing very large inputs. Use this in conjunction with filtering and the `reduce` and `foreach` syntax to reduce large inputs incrementally. * `--slurp`/`-s`: Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once. * `--raw-input`/`-R`: Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with `--slurp`, then the entire input is passed to the filter as a single long string. * `--null-input`/`-n`: Don't read any input at all! Instead, the filter is run once using `null` as the input. This is useful when using jq as a simple calculator or to construct JSON data from scratch. * `--compact-output` / `-c`: By default, jq pretty-prints JSON output. Using this option will result in more compact output by instead putting each JSON object on a single line. * `--tab`: Use a tab for each indentation level instead of two spaces. * `--indent n`: Use the given number of spaces (no more than 8) for indentation. * `--color-output` / `-C` and `--monochrome-output` / `-M`: By default, jq outputs colored JSON if writing to a terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. Colors can be configured with the `JQ_COLORS` environment variable (see below). * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even if the input specified them as escape sequences (like "\u03bc"). Using this option, you can force jq to produce pure ASCII output with every non-ASCII character replaced with the equivalent escape sequence. * `--unbuffered` Flush the output after each JSON object is printed (useful if you're piping a slow data source into jq and piping jq's output elsewhere). * `--sort-keys` / `-S`: Output the fields of each object with the keys in sorted order. * `--raw-output` / `-r`: With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems. * `--join-output` / `-j`: Like `-r` but jq won't print a newline after each output. * `-f filename` / `--from-file filename`: Read filter from the file rather than from a command line, like awk's -f option. You can also use '#' to make comments. * `-Ldirectory` / `-L directory`: Prepend `directory` to the search list for modules. If this option is used then no builtin search list is used. See the section on modules below. * `-e` / `--exit-status`: Sets the exit status of jq to 0 if the last output values was neither `false` nor `null`, 1 if the last output value was either `false` or `null`, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran. Another way to set the exit status is with the `halt_error` builtin function. * `--arg name value`: This option passes a value to the jq program as a predefined variable. If you run jq with `--arg foo bar`, then `$foo` is available in the program and has the value `"bar"`. Note that `value` will be treated as a string, so `--arg foo 123` will bind `$foo` to `"123"`. Named arguments are also available to the jq program as `$ARGS.named`. * `--argjson name JSON-text`: This option passes a JSON-encoded value to the jq program as a predefined variable. If you run jq with `--argjson foo 123`, then `$foo` is available in the program and has the value `123`. * `--slurpfile variable-name filename`: This option reads all the JSON texts in the named file and binds an array of the parsed JSON values to the given global variable. If you run jq with `--slurpfile foo bar`, then `$foo` is available in the program and has an array whose elements correspond to the texts in the file named `bar`. * `--rawfile variable-name filename`: This option reads in the named file and binds its contents to the given global variable. If you run jq with `--rawfile foo bar`, then `$foo` is available in the program and has a string whose contents are to the texs in the file named `bar`. * `--argfile variable-name filename`: Do not use. Use `--slurpfile` instead. (This option is like `--slurpfile`, but when the file has just one text, then that is used, else an array of texts is used as in `--slurpfile`.) * `--args`: Remaining arguments are positional string arguments. These are available to the jq program as `$ARGS.positional[]`. * `--jsonargs`: Remaining arguments are positional JSON text arguments. These are available to the jq program as `$ARGS.positional[]`. * `--run-tests [filename]`: Runs the tests in the given file or standard input. This must be the last option given and does not honor all preceding options. The input consists of comment lines, empty lines, and program lines followed by one input line, as many lines of output as are expected (one per output), and a terminating empty line. Compilation failure tests start with a line containing only "%%FAIL", then a line containing the program to compile, then a line containing an error message to compare to the actual. Be warned that this option can change backwards-incompatibly. - title: Basic filters entries: - title: "Identity: `.`" body: | The absolute simplest filter is `.` . This is a filter that takes its input and produces it unchanged as output. That is, this is the identity operator. Since jq by default pretty-prints all output, this trivial program can be a useful way of formatting JSON output from, say, `curl`. examples: - program: '.' input: '"Hello, world!"' output: ['"Hello, world!"'] - title: "Object Identifier-Index: `.foo`, `.foo.bar`" body: | The simplest *useful* filter is `.foo`. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there's none present. A filter of the form `.foo.bar` is equivalent to `.foo|.bar`. This syntax only works for simple, identifier-like keys, that is, keys that are all made of alphanumeric characters and underscore, and which do not start with a digit. If the key contains special characters, you need to surround it with double quotes like this: `."foo$"`, or else `.["foo$"]`. For example `.["foo::bar"]` and `.["foo.bar"]` work while `.foo::bar` does not, and `.foo.bar` means `.["foo"].["bar"]`. examples: - program: '.foo' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]' input: '{"foo": 42}' output: [42] - title: "Optional Object Identifier-Index: `.foo?`" body: | Just like `.foo`, but does not output even an error when `.` is not an array or an object. examples: - program: '.foo?' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo?' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]?' input: '{"foo": 42}' output: [42] - program: '[.foo?]' input: '[1,2]' output: ['[]'] - title: "Generic Object Index: `.[]`" body: | You can also look up fields of an object using syntax like `.["foo"]` (.foo above is a shorthand version of this, but only for identifier-like strings). - title: "Array Index: `.[2]`" body: | When the index value is an integer, `.[]` can index arrays. Arrays are zero-based, so `.[2]` returns the third element. Negative indices are allowed, with -1 referring to the last element, -2 referring to the next to last element, and so on. examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['{"name":"JSON", "good":true}'] - program: '.[2]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] - program: '.[-2]' input: '[1,2,3]' output: ['2'] - title: "Array/String Slice: `.[10:15]`" body: | The `.[10:15]` syntax can be used to return a subarray of an array or substring of a string. The array returned by `.[10:15]` will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive). Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array). examples: - program: '.[2:4]' input: '["a","b","c","d","e"]' output: ['["c", "d"]'] - program: '.[2:4]' input: '"abcdefghi"' output: ['"cd"'] - program: '.[:3]' input: '["a","b","c","d","e"]' output: ['["a", "b", "c"]'] - program: '.[-2:]' input: '["a","b","c","d","e"]' output: ['["d", "e"]'] - title: "Array/Object Value Iterator: `.[]`" body: | If you use the `.[index]` syntax, but omit the index entirely, it will return *all* of the elements of an array. Running `.[]` with the input `[1,2,3]` will produce the numbers as three separate results, rather than as a single array. You can also use this on an object, and it will return all the values of the object. examples: - program: '.[]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: - '{"name":"JSON", "good":true}' - '{"name":"XML", "good":false}' - program: '.[]' input: '[]' output: [] - program: '.[]' input: '{"a": 1, "b": 1}' output: ['1', '1'] - title: "`.[]?`" body: | Like `.[]`, but no errors will be output if . is not an array or object. - title: "Comma: `,`" body: | If two filters are separated by a comma, then the same input will be fed into both and the two filters' output value streams will be concatenated in order: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right. For instance, filter `.foo, .bar`, produces both the "foo" fields and "bar" fields as separate outputs. examples: - program: '.foo, .bar' input: '{"foo": 42, "bar": "something else", "baz": true}' output: ['42', '"something else"'] - program: ".user, .projects[]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['"stedolan"', '"jq"', '"wikiflow"'] - program: '.[4,2]' input: '["a","b","c","d","e"]' output: ['"e"', '"c"'] - title: "Pipe: `|`" body: | The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right. It's pretty much the same as the Unix shell's pipe, if you're used to that. If the one on the left produces multiple results, the one on the right will be run for each of those results. So, the expression `.[] | .foo` retrieves the "foo" field of each element of the input array. Note that `.a.b.c` is the same as `.a | .b | .c`. Note too that `.` is the input value at the particular stage in a "pipeline", specifically: where the `.` expression appears. Thus `.a | . | .b` is the same as `.a.b`, as the `.` in the middle refers to whatever value `.a` produced. examples: - program: '.[] | .name' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['"JSON"', '"XML"'] - title: "Parenthesis" body: | Parenthesis work as a grouping operator just as in any typical programming language. examples: - program: '(. + 2) * 5' input: '1' output: [15] - title: Types and Values body: | jq supports the same set of datatypes as JSON - numbers, strings, booleans, arrays, objects (which in JSON-speak are hashes with only string keys), and "null". Booleans, null, strings and numbers are written the same way as in javascript. Just like everything else in jq, these simple values take an input and produce an output - `42` is a valid jq expression that takes an input, ignores it, and returns 42 instead. entries: - title: "Array construction: `[]`" body: | As in JSON, `[]` is used to construct arrays, as in `[1,2,3]`. The elements of the arrays can be any jq expression, including a pipeline. All of the results produced by all of the expressions are collected into one big array. You can use it to construct an array out of a known quantity of values (as in `[.foo, .bar, .baz]`) or to "collect" all the results of a filter into an array (as in `[.items[].name]`) Once you understand the "," operator, you can look at jq's array syntax in a different light: the expression `[1,2,3]` is not using a built-in syntax for comma-separated arrays, but is instead applying the `[]` operator (collect results) to the expression 1,2,3 (which produces three different results). If you have a filter `X` that produces four results, then the expression `[X]` will produce a single result, an array of four elements. examples: - program: "[.user, .projects[]]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['["stedolan", "jq", "wikiflow"]'] - program: "[ .[] | . * 2]" input: '[1, 2, 3]' output: ['[2, 4, 6]'] - title: "Object Construction: `{}`" body: | Like JSON, `{}` is for constructing objects (aka dictionaries or hashes), as in: `{"a": 42, "b": 17}`. If the keys are "identifier-like", then the quotes can be left off, as in `{a:42, b:17}`. Keys generated by expressions need to be parenthesized, e.g., `{("a"+"b"):59}`. The value can be any expression (although you may need to wrap it in parentheses if it's a complicated one), which gets applied to the {} expression's input (remember, all filters have an input and an output). {foo: .bar} will produce the JSON object `{"foo": 42}` if given the JSON object `{"bar":42, "baz":43}` as its input. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write {user: .user, title: .title} Because that is so common, there's a shortcut syntax for it: `{user, title}`. If one of the expressions produces multiple results, multiple dictionaries will be produced. If the input's {"user":"stedolan","titles":["JQ Primer", "More JQ"]} then the expression {user, title: .titles[]} will produce two outputs: {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} Putting parentheses around the key means it will be evaluated as an expression. With the same input as above, {(.user): .titles} produces {"stedolan": ["JQ Primer", "More JQ"]} examples: - program: '{user, title: .titles[]}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: - '{"user":"stedolan", "title": "JQ Primer"}' - '{"user":"stedolan", "title": "More JQ"}' - program: '{(.user): .titles}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: ['{"stedolan": ["JQ Primer", "More JQ"]}'] - title: "Recursive Descent: `..`" body: | Recursively descends `.`, producing every value. This is the same as the zero-argument `recurse` builtin (see below). This is intended to resemble the XPath `//` operator. Note that `..a` does not work; use `..|.a` instead. In the example below we use `..|.a?` to find all the values of object keys "a" in any object found "below" `.`. This is particularly useful in conjunction with `path(EXP)` (also see below) and the `?` operator. examples: - program: '..|.a?' input: '[[{"a":1}]]' output: ['1'] - title: Builtin operators and functions body: | Some jq operator (for instance, `+`) do different things depending on the type of their arguments (arrays, numbers, etc.). However, jq never does implicit type conversions. If you try to add a string to an object you'll get an error message and no result. entries: - title: "Addition: `+`" body: | The operator `+` takes two filters, applies them both to the same input, and adds the results together. What "adding" means depends on the types involved: - **Numbers** are added by normal arithmetic. - **Arrays** are added by being concatenated into a larger array. - **Strings** are added by being joined into a larger string. - **Objects** are added by merging, that is, inserting all the key-value pairs from both objects into a single combined object. If both objects contain a value for the same key, the object on the right of the `+` wins. (For recursive merge use the `*` operator.) `null` can be added to any value, and returns the other value unchanged. examples: - program: '.a + 1' input: '{"a": 7}' output: ['8'] - program: '.a + .b' input: '{"a": [1,2], "b": [3,4]}' output: ['[1,2,3,4]'] - program: '.a + null' input: '{"a": 1}' output: ['1'] - program: '.a + 1' input: '{}' output: ['1'] - program: '{a: 1} + {b: 2} + {c: 3} + {a: 42}' input: 'null' output: ['{"a": 42, "b": 2, "c": 3}'] - title: "Subtraction: `-`" body: | As well as normal arithmetic subtraction on numbers, the `-` operator can be used on arrays to remove all occurrences of the second array's elements from the first array. examples: - program: '4 - .a' input: '{"a":3}' output: ['1'] - program: . - ["xml", "yaml"] input: '["xml", "yaml", "json"]' output: ['["json"]'] - title: "Multiplication, division, modulo: `*`, `/`, and `%`" body: | These infix operators behave as expected when given two numbers. Division by zero raises an error. `x % y` computes x modulo y. Multiplying a string by a number produces the concatenation of that string that many times. `"x" * 0` produces **null**. Dividing a string by another splits the first using the second as separators. Multiplying two objects will merge them recursively: this works like addition but if both objects contain a value for the same key, and the values are objects, the two are merged with the same strategy. examples: - program: '10 / . * 3' input: 5 output: [6] - program: '. / ", "' input: '"a, b,c,d, e"' output: ['["a","b,c,d","e"]'] - program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}' input: 'null' output: ['{"k": {"a": 0, "b": 2, "c": 3}}'] - program: '.[] | (1 / .)?' input: '[1,0,-1]' output: ['1', '-1'] - title: "`length`" body: | The builtin function `length` gets the length of various different types of value: - The length of a **string** is the number of Unicode codepoints it contains (which will be the same as its JSON-encoded length in bytes if it's pure ASCII). - The length of an **array** is the number of elements. - The length of an **object** is the number of key-value pairs. - The length of **null** is zero. examples: - program: '.[] | length' input: '[[1,2], "string", {"a":2}, null]' output: [2, 6, 1, 0] - title: "`utf8bytelength`" body: | The builtin function `utf8bytelength` outputs the number of bytes used to encode a string in UTF-8. examples: - program: 'utf8bytelength' input: '"\u03bc"' output: [2] - title: "`keys`, `keys_unsorted`" body: | The builtin function `keys`, when given an object, returns its keys in an array. The keys are sorted "alphabetically", by unicode codepoint order. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings. When `keys` is given an array, it returns the valid indices for that array: the integers from 0 to length-1. The `keys_unsorted` function is just like `keys`, but if the input is an object then the keys will not be sorted, instead the keys will roughly be in insertion order. examples: - program: 'keys' input: '{"abc": 1, "abcd": 2, "Foo": 3}' output: ['["Foo", "abc", "abcd"]'] - program: 'keys' input: '[42,3,35]' output: ['[0,1,2]'] - title: "`has(key)`" body: | The builtin function `has` returns whether the input object has the given key, or the input array has an element at the given index. `has($key)` has the same effect as checking whether `$key` is a member of the array returned by `keys`, although `has` will be faster. examples: - program: 'map(has("foo"))' input: '[{"foo": 42}, {}]' output: ['[true, false]'] - program: 'map(has(2))' input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] - title: "`in`" body: | The builtin function `in` returns whether or not the input key is in the given object, or the input index corresponds to an element in the given array. It is, essentially, an inversed version of `has`. examples: - program: '.[] | in({"foo": 42})' input: '["foo", "bar"]' output: ['true', 'false'] - program: 'map(in([0,1]))' input: '[2, 0]' output: ['[false, true]'] - title: "`map(x)`, `map_values(x)`" body: | For any filter `x`, `map(x)` will run that filter for each element of the input array, and return the outputs in a new array. `map(.+1)` will increment each element of an array of numbers. Similarly, `map_values(x)` will run that filter for each element, but it will return an object when an object is passed. `map(x)` is equivalent to `[.[] | x]`. In fact, this is how it's defined. Similarly, `map_values(x)` is defined as `.[] |= x`. examples: - program: 'map(.+1)' input: '[1,2,3]' output: ['[2,3,4]'] - program: 'map_values(.+1)' input: '{"a": 1, "b": 2, "c": 3}' output: ['{"a": 2, "b": 3, "c": 4}'] - title: "`path(path_expression)`" body: | Outputs array representations of the given path expression in `.`. The outputs are arrays of strings (object keys) and/or numbers (array indices). Path expressions are jq expressions like `.a`, but also `.[]`. There are two types of path expressions: ones that can match exactly, and ones that cannot. For example, `.a.b.c` is an exact match path expression, while `.a[].b` is not. `path(exact_path_expression)` will produce the array representation of the path expression even if it does not exist in `.`, if `.` is `null` or an array or an object. `path(pattern)` will produce array representations of the paths matching `pattern` if the paths exist in `.`. Note that the path expressions are not different from normal expressions. The expression `path(..|select(type=="boolean"))` outputs all the paths to boolean values in `.`, and only those paths. examples: - program: 'path(.a[0].b)' input: 'null' output: ['["a",0,"b"]'] - program: '[path(..)]' input: '{"a":[{"b":1}]}' output: ['[[],["a"],["a",0],["a",0,"b"]]'] - title: "`del(path_expression)`" body: | The builtin function `del` removes a key and its corresponding value from an object. examples: - program: 'del(.foo)' input: '{"foo": 42, "bar": 9001, "baz": 42}' output: ['{"bar": 9001, "baz": 42}'] - program: 'del(.[1, 2])' input: '["foo", "bar", "baz"]' output: ['["foo"]'] - title: "`getpath(PATHS)`" body: | The builtin function `getpath` outputs the values in `.` found at each path in `PATHS`. examples: - program: 'getpath(["a","b"])' input: 'null' output: ['null'] - program: '[getpath(["a","b"], ["a","c"])]' input: '{"a":{"b":0, "c":1}}' output: ['[0, 1]'] - title: "`setpath(PATHS; VALUE)`" body: | The builtin function `setpath` sets the `PATHS` in `.` to `VALUE`. examples: - program: 'setpath(["a","b"]; 1)' input: 'null' output: ['{"a": {"b": 1}}'] - program: 'setpath(["a","b"]; 1)' input: '{"a":{"b":0}}' output: ['{"a": {"b": 1}}'] - program: 'setpath([0,"a"]; 1)' input: 'null' output: ['[{"a":1}]'] - title: "`delpaths(PATHS)`" body: | The builtin function `delpaths` sets the `PATHS` in `.`. `PATHS` must be an array of paths, where each path is an array of strings and numbers. examples: - program: 'delpaths([["a","b"]])' input: '{"a":{"b":1},"x":{"y":2}}' output: ['{"a":{},"x":{"y":2}}'] - title: "`to_entries`, `from_entries`, `with_entries`" body: | These functions convert between an object and an array of key-value pairs. If `to_entries` is passed an object, then for each `k: v` entry in the input, the output array includes `{"key": k, "value": v}`. `from_entries` does the opposite conversion, and `with_entries(foo)` is a shorthand for `to_entries | map(foo) | from_entries`, useful for doing some operation to all keys and values of an object. `from_entries` accepts key, Key, name, Name, value and Value as keys. examples: - program: 'to_entries' input: '{"a": 1, "b": 2}' output: ['[{"key":"a", "value":1}, {"key":"b", "value":2}]'] - program: 'from_entries' input: '[{"key":"a", "value":1}, {"key":"b", "value":2}]' output: ['{"a": 1, "b": 2}'] - program: 'with_entries(.key |= "KEY_" + .)' input: '{"a": 1, "b": 2}' output: ['{"KEY_a": 1, "KEY_b": 2}'] - title: "`select(boolean_expression)`" body: | The function `select(foo)` produces its input unchanged if `foo` returns true for that input, and produces no output otherwise. It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))` will give you `[2,3]`. examples: - program: 'map(select(. >= 2))' input: '[1,5,3,0,7]' output: ['[5,3,7]'] - program: '.[] | select(.id == "second")' input: '[{"id": "first", "val": 1}, {"id": "second", "val": 2}]' output: ['{"id": "second", "val": 2}'] - title: "`arrays`, `objects`, `iterables`, `booleans`, `numbers`, `normals`, `finites`, `strings`, `nulls`, `values`, `scalars`" body: | These built-ins select only inputs that are arrays, objects, iterables (arrays or objects), booleans, numbers, normal numbers, finite numbers, strings, null, non-null values, and non-iterables, respectively. examples: - program: '.[]|numbers' input: '[[],{},1,"foo",null,true,false]' output: ['1'] - title: "`empty`" body: | `empty` returns no results. None at all. Not even `null`. It's useful on occasion. You'll know if you need it :) examples: - program: '1, empty, 2' input: 'null' output: [1, 2] - program: '[1,2,empty,3]' input: 'null' output: ['[1,2,3]'] - title: "`error(message)`" body: | Produces an error, just like `.a` applied to values other than null and objects would, but with the given message as the error's value. Errors can be caught with try/catch; see below. - title: "`halt`" body: | Stops the jq program with no further outputs. jq will exit with exit status `0`. - title: "`halt_error`, `halt_error(exit_code)`" body: | Stops the jq program with no further outputs. The input will be printed on `stderr` as raw output (i.e., strings will not have double quotes) with no decoration, not even a newline. The given `exit_code` (defaulting to `5`) will be jq's exit status. For example, `"Error: somthing went wrong\n"|halt_error(1)`. - title: "`$__loc__`" body: | Produces an object with a "file" key and a "line" key, with the filename and line number where `$__loc__` occurs, as values. examples: - program: 'try error("\($__loc__)") catch .' input: 'null' output: ['"{\"file\":\"\",\"line\":1}"'] - title: "`paths`, `paths(node_filter)`, `leaf_paths`" body: | `paths` outputs the paths to all the elements in its input (except it does not output the empty list, representing . itself). `paths(f)` outputs the paths to any values for which `f` is true. That is, `paths(numbers)` outputs the paths to all numeric values. `leaf_paths` is an alias of `paths(scalars)`; `leaf_paths` is *deprecated* and will be removed in the next major release. examples: - program: '[paths]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1],[1,0],[1,1],[1,1,"a"]]'] - program: '[paths(scalars)]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1,1,"a"]]'] - title: "`add`" body: | The filter `add` takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the `+` operator (described above). If the input is an empty array, `add` returns `null`. examples: - program: add input: '["a","b","c"]' output: ['"abc"'] - program: add input: '[1, 2, 3]' output: [6] - program: add input: '[]' output: ["null"] - title: "`any`, `any(condition)`, `any(generator; condition)`" body: | The filter `any` takes as input an array of boolean values, and produces `true` as output if any of the elements of the array are `true`. If the input is an empty array, `any` returns `false`. The `any(condition)` form applies the given condition to the elements of the input array. The `any(generator; condition)` form applies the given condition to all the outputs of the given generator. examples: - program: any input: '[true, false]' output: ["true"] - program: any input: '[false, false]' output: ["false"] - program: any input: '[]' output: ["false"] - title: "`all`, `all(condition)`, `all(generator; condition)`" body: | The filter `all` takes as input an array of boolean values, and produces `true` as output if all of the elements of the array are `true`. The `all(condition)` form applies the given condition to the elements of the input array. The `all(generator; condition)` form applies the given condition to all the outputs of the given generator. If the input is an empty array, `all` returns `true`. examples: - program: all input: '[true, false]' output: ["false"] - program: all input: '[true, true]' output: ["true"] - program: all input: '[]' output: ["true"] - title: "`flatten`, `flatten(depth)`" body: | The filter `flatten` takes as input an array of nested arrays, and produces a flat array in which all arrays inside the original array have been recursively replaced by their values. You can pass an argument to it to specify how many levels of nesting to flatten. `flatten(2)` is like `flatten`, but going only up to two levels deep. examples: - program: flatten input: '[1, [2], [[3]]]' output: ["[1, 2, 3]"] - program: flatten(1) input: '[1, [2], [[3]]]' output: ["[1, 2, [3]]"] - program: flatten input: '[[]]' output: ["[]"] - program: flatten input: '[{"foo": "bar"}, [{"foo": "baz"}]]' output: ['[{"foo": "bar"}, {"foo": "baz"}]'] - title: "`range(upto)`, `range(from;upto)` `range(from;upto;by)`" body: | The `range` function produces a range of numbers. `range(4;10)` produces 6 numbers, from 4 (inclusive) to 10 (exclusive). The numbers are produced as separate outputs. Use `[range(4;10)]` to get a range as an array. The one argument form generates numbers from 0 to the given number, with an increment of 1. The two argument form generates numbers from `from` to `upto` with an increment of 1. The three argument form generates numbers `from` to `upto` with an increment of `by`. examples: - program: 'range(2;4)' input: 'null' output: ['2', '3'] - program: '[range(2;4)]' input: 'null' output: ['[2,3]'] - program: '[range(4)]' input: 'null' output: ['[0,1,2,3]'] - program: '[range(0;10;3)]' input: 'null' output: ['[0,3,6,9]'] - program: '[range(0;10;-1)]' input: 'null' output: ['[]'] - program: '[range(0;-5;-1)]' input: 'null' output: ['[0,-1,-2,-3,-4]'] - title: "`floor`" body: | The `floor` function returns the floor of its numeric input. examples: - program: 'floor' input: '3.14159' output: ['3'] - title: "`sqrt`" body: | The `sqrt` function returns the square root of its numeric input. examples: - program: 'sqrt' input: '9' output: ['3'] - title: "`tonumber`" body: | The `tonumber` function parses its input as a number. It will convert correctly-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input. examples: - program: '.[] | tonumber' input: '[1, "1"]' output: [1, 1] - title: "`tostring`" body: | The `tostring` function prints its input as a string. Strings are left unchanged, and all other values are JSON-encoded. examples: - program: '.[] | tostring' input: '[1, "1", [1]]' output: ['"1"', '"1"', '"[1]"'] - title: "`type`" body: | The `type` function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object. examples: - program: 'map(type)' input: '[0, false, [], {}, null, "hello"]' output: ['["number", "boolean", "array", "object", "null", "string"]'] - title: "`infinite`, `nan`, `isinfinite`, `isnan`, `isfinite`, `isnormal`" body: | Some arithmetic operations can yield infinities and "not a number" (NaN) values. The `isinfinite` builtin returns `true` if its input is infinite. The `isnan` builtin returns `true` if its input is a NaN. The `infinite` builtin returns a positive infinite value. The `nan` builtin returns a NaN. The `isnormal` builtin returns true if its input is a normal number. Note that division by zero raises an error. Currently most arithmetic operations operating on infinities, NaNs, and sub-normals do not raise errors. examples: - program: '.[] | (infinite * .) < 0' input: '[-1, 1]' output: ['true', 'false'] - program: 'infinite, nan | type' input: 'null' output: ['"number"', '"number"'] - title: "`sort, sort_by(path_expression)`" body: | The `sort` functions sorts its input, which must be an array. Values are sorted in the following order: * `null` * `false` * `true` * numbers * strings, in alphabetical order (by unicode codepoint value) * arrays, in lexical order * objects The ordering for objects is a little complex: first they're compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key. `sort` may be used to sort by a particular field of an object, or by applying any jq filter. `sort_by(foo)` compares two elements by comparing the result of `foo` on each element. examples: - program: 'sort' input: '[8,3,null,6]' output: ['[null,3,6,8]'] - program: 'sort_by(.foo)' input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]' output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]'] - title: "`group_by(path_expression)`" body: | `group_by(.foo)` takes as input an array, groups the elements having the same `.foo` field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the `.foo` field. Any jq expression, not just a field access, may be used in place of `.foo`. The sorting order is the same as described in the `sort` function above. examples: - program: 'group_by(.foo)' input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]'] - title: "`min`, `max`, `min_by(path_exp)`, `max_by(path_exp)`" body: | Find the minimum or maximum element of the input array. The `min_by(path_exp)` and `max_by(path_exp)` functions allow you to specify a particular field or property to examine, e.g. `min_by(.foo)` finds the object with the smallest `foo` field. examples: - program: 'min' input: '[5,4,2,7]' output: ['2'] - program: 'max_by(.foo)' input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' output: ['{"foo":2, "bar":3}'] - title: "`unique`, `unique_by(path_exp)`" body: | The `unique` function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed. The `unique_by(path_exp)` function will keep only one element for each value obtained by applying the argument. Think of it as making an array by taking one element out of every group produced by `group`. examples: - program: 'unique' input: '[1,2,5,3,5,3,1,3]' output: ['[1,2,3,5]'] - program: 'unique_by(.foo)' input: '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' output: ['[{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]'] - program: 'unique_by(length)' input: '["chunky", "bacon", "kitten", "cicada", "asparagus"]' output: ['["bacon", "chunky", "asparagus"]'] - title: "`reverse`" body: | This function reverses an array. examples: - program: 'reverse' input: '[1,2,3,4]' output: ['[4,3,2,1]'] - title: "`contains(element)`" body: | The filter `contains(b)` will produce true if b is completely contained within the input. A string B is contained in a string A if B is a substring of A. An array B is contained in an array A if all elements in B are contained in any element in A. An object B is contained in object A if all of the values in B are contained in the value in A with the same key. All other types are assumed to be contained in each other if they are equal. examples: - program: 'contains("bar")' input: '"foobar"' output: ['true'] - program: 'contains(["baz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['true'] - program: 'contains(["bazzzzz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['false'] - program: 'contains({foo: 12, bar: [{barp: 12}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['true'] - program: 'contains({foo: 12, bar: [{barp: 15}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['false'] - title: "`indices(s)`" body: | Outputs an array containing the indices in `.` where `s` occurs. The input may be an array, in which case if `s` is an array then the indices output will be those where all elements in `.` match those of `s`. examples: - program: 'indices(", ")' input: '"a,b, cd, efg, hijk"' output: ['[3,7,12]'] - program: 'indices(1)' input: '[0,1,2,1,3,1,4]' output: ['[1,3,5]'] - program: 'indices([1,2])' input: '[0,1,2,3,1,4,2,5,1,2,6,7]' output: ['[1,8]'] - title: "`index(s)`, `rindex(s)`" body: | Outputs the index of the first (`index`) or last (`rindex`) occurrence of `s` in the input. examples: - program: 'index(", ")' input: '"a,b, cd, efg, hijk"' output: ['3'] - program: 'rindex(", ")' input: '"a,b, cd, efg, hijk"' output: ['12'] - title: "`inside`" body: | The filter `inside(b)` will produce true if the input is completely contained within b. It is, essentially, an inversed version of `contains`. examples: - program: 'inside("foobar")' input: '"bar"' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["baz", "bar"]' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["bazzzzz", "bar"]' output: ['false'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 12}]}' output: ['true'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 15}]}' output: ['false'] - title: "`startswith(str)`" body: | Outputs `true` if . starts with the given string argument. examples: - program: '[.[]|startswith("foo")]' input: '["fo", "foo", "barfoo", "foobar", "barfoob"]' output: ['[false, true, false, true, false]'] - title: "`endswith(str)`" body: | Outputs `true` if . ends with the given string argument. examples: - program: '[.[]|endswith("foo")]' input: '["foobar", "barfoo"]' output: ['[false, true]'] - title: "`combinations`, `combinations(n)`" body: | Outputs all combinations of the elements of the arrays in the input array. If given an argument `n`, it outputs all combinations of `n` repetitions of the input array. examples: - program: 'combinations' input: '[[1,2], [3, 4]]' output: ['[1, 3]', '[1, 4]', '[2, 3]', '[2, 4]'] - program: 'combinations(2)' input: '[0, 1]' output: ['[0, 0]', '[0, 1]', '[1, 0]', '[1, 1]'] - title: "`ltrimstr(str)`" body: | Outputs its input with the given prefix string removed, if it starts with it. examples: - program: '[.[]|ltrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "afoo"]' output: ['["fo","","barfoo","bar","afoo"]'] - title: "`rtrimstr(str)`" body: | Outputs its input with the given suffix string removed, if it ends with it. examples: - program: '[.[]|rtrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "foob"]' output: ['["fo","","bar","foobar","foob"]'] - title: "`explode`" body: | Converts an input string into an array of the string's codepoint numbers. examples: - program: 'explode' input: '"foobar"' output: ['[102,111,111,98,97,114]'] - title: "`implode`" body: | The inverse of explode. examples: - program: 'implode' input: '[65, 66, 67]' output: ['"ABC"'] - title: "`split(str)`" body: | Splits an input string on the separator argument. examples: - program: 'split(", ")' input: '"a, b,c,d, e, "' output: ['["a","b,c,d","e",""]'] - title: "`join(str)`" body: | Joins the array of elements given as input, using the argument as separator. It is the inverse of `split`: that is, running `split("foo") | join("foo")` over any input string returns said input string. Numbers and booleans in the input are converted to strings. Null values are treated as empty strings. Arrays and objects in the input are not supported. examples: - program: 'join(", ")' input: '["a","b,c,d","e"]' output: ['"a, b,c,d, e"'] - program: 'join(" ")' input: '["a",1,2.3,true,null,false]' output: ['"a 1 2.3 true false"'] - title: "`ascii_downcase`, `ascii_upcase`" body: | Emit a copy of the input string with its alphabetic characters (a-z and A-Z) converted to the specified case. example: - program: 'ascii_upcase' input: '"useful but not for é"' output: '"USEFUL BUT NOT FOR é"' - title: "`while(cond; update)`" body: | The `while(cond; update)` function allows you to repeatedly apply an update to `.` until `cond` is false. Note that `while(cond; update)` is internally defined as a recursive jq function. Recursive calls within `while` will not consume additional memory if `update` produces at most one output for each input. See advanced topics below. examples: - program: '[while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: "`until(cond; next)`" body: | The `until(cond; next)` function allows you to repeatedly apply the expression `next`, initially to `.` then to its own output, until `cond` is true. For example, this can be used to implement a factorial function (see below). Note that `until(cond; next)` is internally defined as a recursive jq function. Recursive calls within `until()` will not consume additional memory if `next` produces at most one output for each input. See advanced topics below. examples: - program: '[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]' input: '4' output: ['24'] - title: "`recurse(f)`, `recurse`, `recurse(f; condition)`, `recurse_down`" body: | The `recurse(f)` function allows you to search through a recursive structure, and extract interesting data from all levels. Suppose your input represents a filesystem: {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} Now suppose you want to extract all of the filenames present. You need to retrieve `.name`, `.children[].name`, `.children[].children[].name`, and so on. You can do this with: recurse(.children[]) | .name When called without an argument, `recurse` is equivalent to `recurse(.[]?)`. `recurse(f)` is identical to `recurse(f; . != null)` and can be used without concerns about recursion depth. `recurse(f; condition)` is a generator which begins by emitting . and then emits in turn .|f, .|f|f, .|f|f|f, ... so long as the computed value satisfies the condition. For example, to generate all the integers, at least in principle, one could write `recurse(.+1; true)`. For legacy reasons, `recurse_down` exists as an alias to calling `recurse` without arguments. This alias is considered *deprecated* and will be removed in the next major release. The recursive calls in `recurse` will not consume additional memory whenever `f` produces at most a single output for each input. examples: - program: 'recurse(.foo[])' input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}' output: - '{"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}' - '{"foo":[]}' - '{"foo":[{"foo":[]}]}' - '{"foo":[]}' - program: 'recurse' input: '{"a":0,"b":[1]}' output: - '{"a":0,"b":[1]}' - '0' - '[1]' - '1' - program: 'recurse(. * .; . < 20)' input: 2 output: - 2 - 4 - 16 - title: "`walk(f)`" body: | The `walk(f)` function applies f recursively to every component of the input entity. When an array is encountered, f is first applied to its elements and then to the array itself; when an object is encountered, f is first applied to all the values and then to the object. In practice, f will usually test the type of its input, as illustrated in the following examples. The first example highlights the usefulness of processing the elements of an array of arrays before processing the array itself. The second example shows how all the keys of all the objects within the input can be considered for alteration. examples: - program: 'walk(if type == "array" then sort else . end)' input: '[[4, 1, 7], [8, 5, 2], [3, 6, 9]]' output: - '[[1,4,7],[2,5,8],[3,6,9]]' - program: 'walk( if type == "object" then with_entries( .key |= sub( "^_+"; "") ) else . end )' input: '[ { "_a": { "__b": 2 } } ]' output: - '[{"a":{"b":2}}]' - title: "`$ENV`, `env`" body: | `$ENV` is an object representing the environment variables as set when the jq program started. `env` outputs an object representing jq's current environment. At the moment there is no builtin for setting environment variables. examples: - program: '$ENV.PAGER' input: 'null' output: ['"less"'] - program: 'env.PAGER' input: 'null' output: ['"less"'] - title: "`transpose`" body: | Transpose a possibly jagged matrix (an array of arrays). Rows are padded with nulls so the result is always rectangular. examples: - program: 'transpose' input: '[[1], [2,3]]' output: ['[[1,2],[null,3]]'] - title: "`bsearch(x)`" body: | bsearch(x) conducts a binary search for x in the input array. If the input is sorted and contains x, then bsearch(x) will return its index in the array; otherwise, if the array is sorted, it will return (-1 - ix) where ix is an insertion point such that the array would still be sorted after the insertion of x at ix. If the array is not sorted, bsearch(x) will return an integer that is probably of no interest. examples: - program: 'bsearch(0)' input: '[0,1]' output: ['0'] - program: 'bsearch(0)' input: '[1,2,3]' output: ['-1'] - program: 'bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end' input: '[1,2,3]' output: ['[1,2,3,4]'] - title: "String interpolation - `\\(foo)`" body: | Inside a string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string. examples: - program: '"The input was \(.), which is one less than \(.+1)"' input: '42' output: ['"The input was 42, which is one less than 43"'] - title: "Convert to/from JSON" body: | The `tojson` and `fromjson` builtins dump values as JSON texts or parse JSON texts into values, respectively. The tojson builtin differs from tostring in that tostring returns strings unmodified, while tojson encodes strings as JSON strings. examples: - program: '[.[]|tostring]' input: '[1, "foo", ["foo"]]' output: ['["1","foo","[\"foo\"]"]'] - program: '[.[]|tojson]' input: '[1, "foo", ["foo"]]' output: ['["1","\"foo\"","[\"foo\"]"]'] - program: '[.[]|tojson|fromjson]' input: '[1, "foo", ["foo"]]' output: ['[1,"foo",["foo"]]'] - title: "Format strings and escaping" body: | The `@foo` syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth. `@foo` can be used as a filter on its own, the possible escapings are: * `@text`: Calls `tostring`, see that function for details. * `@json`: Serializes the input as JSON. * `@html`: Applies HTML/XML escaping, by mapping the characters `<>&'"` to their entity equivalents `<`, `>`, `&`, `'`, `"`. * `@uri`: Applies percent-encoding, by mapping all reserved URI characters to a `%XX` sequence. * `@csv`: The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition. * `@tsv`: The input must be an array, and it is rendered as TSV (tab-separated values). Each input array will be printed as a single line. Fields are separated by a single tab (ascii `0x09`). Input characters line-feed (ascii `0x0a`), carriage-return (ascii `0x0d`), tab (ascii `0x09`) and backslash (ascii `0x5c`) will be output as escape sequences `\n`, `\r`, `\t`, `\\` respectively. * `@sh`: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings. * `@base64`: The input is converted to base64 as specified by RFC 4648. * `@base64d`: The inverse of `@base64`, input is decoded as specified by RFC 4648. Note\: If the decoded string is not UTF-8, the results are undefined. This syntax can be combined with string interpolation in a useful way. You can follow a `@foo` token with a string literal. The contents of the string literal will *not* be escaped. However, all interpolations made inside that string literal will be escaped. For instance, @uri "https://www.google.com/search?q=\(.search)" will produce the following output for the input `{"search":"what is jq?"}`: "https://www.google.com/search?q=what%20is%20jq%3F" Note that the slashes, question mark, etc. in the URL are not escaped, as they were part of the string literal. examples: - program: '@html' input: '"This works if x < y"' output: ['"This works if x < y"'] # - program: '@html "Anonymous said: \(.)"' # input: '""' # output: ["Anonymous said: <script>alert("lol hax");</script>"] - program: '@sh "echo \(.)"' input: "\"O'Hara's Ale\"" output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""] - program: '@base64' input: '"This is a message"' output: ['"VGhpcyBpcyBhIG1lc3NhZ2U="'] - program: '@base64d' input: '"VGhpcyBpcyBhIG1lc3NhZ2U="' output: ['"This is a message"'] - title: "Dates" body: | jq provides some basic date handling functionality, with some high-level and low-level builtins. In all cases these builtins deal exclusively with time in UTC. The `fromdateiso8601` builtin parses datetimes in the ISO 8601 format to a number of seconds since the Unix epoch (1970-01-01T00:00:00Z). The `todateiso8601` builtin does the inverse. The `fromdate` builtin parses datetime strings. Currently `fromdate` only supports ISO 8601 datetime strings, but in the future it will attempt to parse datetime strings in more formats. The `todate` builtin is an alias for `todateiso8601`. The `now` builtin outputs the current time, in seconds since the Unix epoch. Low-level jq interfaces to the C-library time functions are also provided: `strptime`, `strftime`, `strflocaltime`, `mktime`, `gmtime`, and `localtime`. Refer to your host operating system's documentation for the format strings used by `strptime` and `strftime`. Note: these are not necessarily stable interfaces in jq, particularly as to their localization functionality. The `gmtime` builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of Greenwhich Meridian time as an array of numbers representing (in this order): the year, the month (zero-based), the day of the month (one-based), the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year -- all one-based unless otherwise stated. The day of the week number may be wrong on some systems for dates before March 1st 1900, or after December 31 2099. The `localtime` builtin works like the `gmtime` builtin, but using the local timezone setting. The `mktime` builtin consumes "broken down time" representations of time output by `gmtime` and `strptime`. The `strptime(fmt)` builtin parses input strings matching the `fmt` argument. The output is in the "broken down time" representation consumed by `gmtime` and output by `mktime`. The `strftime(fmt)` builtin formats a time (GMT) with the given format. The `strflocaltime` does the same, but using the local timezone setting. The format strings for `strptime` and `strftime` are described in typical C library documentation. The format string for ISO 8601 datetime is `"%Y-%m-%dT%H:%M:%SZ"`. jq may not support some or all of this date functionality on some systems. In particular, the `%u` and `%j` specifiers for `strptime(fmt)` are not supported on macOS. examples: - program: 'fromdate' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")' input: '"2015-03-05T23:51:47Z"' output: ['[2015,2,5,23,51,47,4,63]'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")|mktime' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - title: "SQL-Style Operators" body: | jq provides a few SQL-style operators. * INDEX(stream; index_expression): This builtin produces an object whose keys are computed by the given index expression applied to each value from the given stream. * JOIN($idx; stream; idx_expr; join_expr): This builtin joins the values from the given stream to the given index. The index's keys are computed by applying the given index expression to each value from the given stream. An array of the value in the stream and the corresponding value from the index is fed to the given join expression to produce each result. * JOIN($idx; stream; idx_expr): Same as `JOIN($idx; stream; idx_expr; .)`. * JOIN($idx; idx_expr): This builtin joins the input `.` to the given index, applying the given index expression to `.` to compute the index key. The join operation is as described above. * IN(s): This builtin outputs `true` if `.` appears in the given stream, otherwise it outputs `false`. * IN(source; s): This builtin outputs `true` if any value in the source stream appears in the second stream, otherwise it outputs `false`. - title: "`builtins`" body: | Returns a list of all builtin functions in the format `name/arity`. Since functions with the same name but different arities are considered separate functions, `all/0`, `all/1`, and `all/2` would all be present in the list. - title: Conditionals and Comparisons entries: - title: "`==`, `!=`" body: | The expression 'a == b' will produce 'true' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and 'false' otherwise. In particular, strings are never considered equal to numbers. If you're coming from Javascript, jq's == is like Javascript's === - considering values equal only when they have the same type as well as the same value. != is "not equal", and 'a != b' returns the opposite value of 'a == b' examples: - program: '.[] == 1' input: '[1, 1.0, "1", "banana"]' output: ['true', 'true', 'false', 'false'] - title: if-then-else body: | `if A then B else C end` will act the same as `B` if `A` produces a value other than false or null, but act the same as `C` otherwise. Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you'll sometimes have to be more explicit about the condition you want: you can't test whether, e.g. a string is empty using `if .name then A else B end`, you'll need something more like `if (.name | length) > 0 then A else B end` instead. If the condition `A` produces multiple results, then `B` is evaluated once for each result that is not false or null, and `C` is evaluated once for each false or null. More cases can be added to an if using `elif A then B` syntax. examples: - program: |- if . == 0 then "zero" elif . == 1 then "one" else "many" end input: 2 output: ['"many"'] - title: "`>, >=, <=, <`" body: | The comparison operators `>`, `>=`, `<=`, `<` return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively). The ordering is the same as that described for `sort`, above. examples: - program: '. < 5' input: 2 output: ['true'] - title: and/or/not body: | jq supports the normal Boolean operators and/or/not. They have the same standard of truth as if expressions - false and null are considered "false values", and anything else is a "true value". If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input. `not` is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in `.foo and .bar | not`. These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default". If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below. examples: - program: '42 and "a string"' input: 'null' output: ['true'] - program: '(true, false) or false' input: 'null' output: ['true', 'false'] # - program: '(true, false) and (true, false)' # input: 'null' # output: ['true', 'false', 'false', 'false'] - program: '(true, true) and (true, false)' input: 'null' output: ['true', 'false', 'true', 'false'] - program: '[true, false | not]' input: 'null' output: ['[false, true]'] - title: "Alternative operator: `//`" body: | A filter of the form `a // b` produces the same results as `a`, if `a` produces results other than `false` and `null`. Otherwise, `a // b` produces the same results as `b`. This is useful for providing defaults: `.foo // 1` will evaluate to `1` if there's no `.foo` element in the input. It's similar to how `or` is sometimes used in Python (jq's `or` operator is reserved for strictly Boolean operations). examples: - program: '.foo // 42' input: '{"foo": 19}' output: [19] - program: '.foo // 42' input: '{}' output: [42] - title: try-catch body: | Errors can be caught by using `try EXP catch EXP`. The first expression is executed, and if it fails then the second is executed with the error message. The output of the handler, if any, is output as if it had been the output of the expression to try. The `try EXP` form uses `empty` as the exception handler. examples: - program: 'try .a catch ". is not an object"' input: 'true' output: ['". is not an object"'] - program: '[.[]|try .a]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - program: 'try error("some exception") catch .' input: 'true' output: ['"some exception"'] - title: Breaking out of control structures body: | A convenient use of try/catch is to break out of control structures like `reduce`, `foreach`, `while`, and so on. For example: # Repeat an expression until it raises "break" as an # error, then stop repeating without re-raising the error. # But if the error caught is not "break" then re-raise it. try repeat(exp) catch .=="break" then empty else error; jq has a syntax for named lexical labels to "break" or "go (back) to": label $out | ... break $out ... The `break $label_name` expression will cause the program to to act as though the nearest (to the left) `label $label_name` produced `empty`. The relationship between the `break` and corresponding `label` is lexical: the label has to be "visible" from the break. To break out of a `reduce`, for example: label $out | reduce .[] as $item (null; if .==false then break $out else ... end) The following jq program produces a syntax error: break $out because no label `$out` is visible. - title: "Error Suppression / Optional Operator: `?`" body: | The `?` operator, used as `EXP?`, is shorthand for `try EXP`. examples: - program: '[.[]|(.a)?]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - title: Regular expressions (PCRE) body: | jq uses the Oniguruma regular expression library, as do php, ruby, TextMate, Sublime Text, etc, so the description here will focus on jq specifics. The jq regex filters are defined so that they can be used using one of these patterns: STRING | FILTER( REGEX ) STRING | FILTER( REGEX; FLAGS ) STRING | FILTER( [REGEX] ) STRING | FILTER( [REGEX, FLAGS] ) where: * STRING, REGEX and FLAGS are jq strings and subject to jq string interpolation; * REGEX, after string interpolation, should be a valid PCRE regex; * FILTER is one of `test`, `match`, or `capture`, as described below. FLAGS is a string consisting of one of more of the supported flags: * `g` - Global search (find all matches, not just the first) * `i` - Case insensitive search * `m` - Multi line mode ('.' will match newlines) * `n` - Ignore empty matches * `p` - Both s and m modes are enabled * `s` - Single line mode ('^' -> '\A', '$' -> '\Z') * `l` - Find longest possible matches * `x` - Extended regex format (ignore whitespace and comments) To match whitespace in an x pattern use an escape such as \s, e.g. * test( "a\\sb", "x" ). Note that certain flags may also be specified within REGEX, e.g. * jq -n '("test", "TEst", "teST", "TEST") | test( "(?i)te(?-i)st" )' evaluates to: true, true, false, false. entries: - title: "`test(val)`, `test(regex; flags)`" body: | Like `match`, but does not return match objects, only `true` or `false` for whether or not the regex matches the input. examples: - program: 'test("foo")' input: '"foo"' output: ['true'] - program: '.[] | test("a b c # spaces are ignored"; "ix")' input: '["xabcd", "ABC"]' output: ['true', 'true'] - title: "`match(val)`, `match(regex; flags)`" body: | **match** outputs an object for each match it finds. Matches have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of the match * `string` - the string that it matched * `captures` - an array of objects representing capturing groups. Capturing group objects have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of this capturing group * `string` - the string that was captured * `name` - the name of the capturing group (or `null` if it was unnamed) Capturing groups that did not match anything return an offset of -1 examples: - program: 'match("(abc)+"; "g")' input: '"abc abc"' output: - '{"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}' - '{"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]}' - program: 'match("foo")' input: '"foo bar foo"' output: ['{"offset": 0, "length": 3, "string": "foo", "captures": []}'] - program: 'match(["foo", "ig"])' input: '"foo bar FOO"' output: - '{"offset": 0, "length": 3, "string": "foo", "captures": []}' - '{"offset": 8, "length": 3, "string": "FOO", "captures": []}' - program: 'match("foo (?bar)? foo"; "ig")' input: '"foo bar foo foo foo"' output: - '{"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}' - '{"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}' - program: '[ match("."; "g")] | length' input: '"abc"' output: [3] - title: "`capture(val)`, `capture(regex; flags)`" body: | Collects the named captures in a JSON object, with the name of each capture as the key, and the matched string as the corresponding value. examples: - program: 'capture("(?[a-z]+)-(?[0-9]+)")' input: '"xyzzy-14"' output: ['{ "a": "xyzzy", "n": "14" }'] - title: "`scan(regex)`, `scan(regex; flags)`" body: | Emit a stream of the non-overlapping substrings of the input that match the regex in accordance with the flags, if any have been specified. If there is no match, the stream is empty. To capture all the matches for each input string, use the idiom `[ expr ]`, e.g. `[ scan(regex) ]`. example: - program: 'scan("c")' input: '"abcdefabc"' output: ['"c"', '"c"'] - program: 'scan("b")' input: ("", "") output: ['[]', '[]'] - title: "`split(regex; flags)`" body: | For backwards compatibility, `split` splits on a string, not a regex. example: - program: 'split(", *"; null)' input: '"ab,cd, ef"' output: ['"ab","cd","ef"'] - title: "`splits(regex)`, `splits(regex; flags)`" body: | These provide the same results as their `split` counterparts, but as a stream instead of an array. example: - program: 'splits(", *")' input: '("ab,cd", "ef, gh")' output: ['"ab"', '"cd"', '"ef"', '"gh"'] - title: "`sub(regex; tostring)` `sub(regex; string; flags)`" body: | Emit the string obtained by replacing the first match of regex in the input string with `tostring`, after interpolation. `tostring` should be a jq string, and may contain references to named captures. The named captures are, in effect, presented as a JSON object (as constructed by `capture`) to `tostring`, so a reference to a captured variable named "x" would take the form: "\(.x)". example: - program: 'sub("^[^a-z]*(?[a-z]*).*")' input: '"123abc456"' output: '"ZabcZabc"' - title: "`gsub(regex; string)`, `gsub(regex; string; flags)`" body: | `gsub` is like `sub` but all the non-overlapping occurrences of the regex are replaced by the string, after interpolation. example: - program: 'gsub("(?.)[^a]*"; "+\(.x)-")' input: '"Abcabc"' output: '"+A-+a-"' - title: Advanced features body: | Variables are an absolute necessity in most programming languages, but they're relegated to an "advanced feature" in jq. In most languages, variables are the only means of passing around data. If you calculate a value, and you want to use it more than once, you'll need to store it in a variable. To pass a value to another part of the program, you'll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data. It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq's standard library (many jq functions such as `map` and `find` are in fact written in jq). jq has reduction operators, which are very powerful but a bit tricky. Again, these are mostly used internally, to define some useful bits of jq's standard library. It may not be obvious at first, but jq is all about generators (yes, as often found in other languages). Some utilities are provided to help deal with generators. Some minimal I/O support (besides reading JSON from standard input, and writing JSON to standard output) is available. Finally, there is a module/library system. entries: - title: "Variable / Symbolic Binding Operator: `... as $identifier | ...`" body: | In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next. Many expressions, for instance `a + b`, pass their input to two distinct subexpressions (here `a` and `b` are both passed the same input), so variables aren't usually necessary in order to use a value twice. For instance, calculating the average value of an array of numbers requires a few variables in most languages - at least one to hold the array, perhaps one for each element or for a loop counter. In jq, it's simply `add / length` - the `add` expression is given the array and produces its sum, and the `length` expression is given the array and produces its length. So, there's generally a cleaner way to solve most problems in jq than defining variables. Still, sometimes they do make things easier, so jq lets you define variables using `expression as $variable`. All variable names start with `$`. Here's a slightly uglier version of the array-averaging example: length as $array_length | add / $array_length We'll need a more complicated problem to find a situation where using variables actually makes our lives easier. Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names. Our input looks like: {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} We want to produce the posts with the author field containing a real name, as in: {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"} We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: .realnames as $names | .posts[] | {title, author: $names[.author]} The expression `exp as $x | ...` means: for each value of expression `exp`, run the rest of the pipeline with the entire original input, and with `$x` set to that value. Thus `as` functions as something of a foreach loop. Just as `{foo}` is a handy way of writing `{foo: .foo}`, so `{$foo}` is a handy way of writing `{foo:$foo}`. Multiple variables may be declared using a single `as` expression by providing a pattern that matches the structure of the input (this is known as "destructuring"): . as {realnames: $names, posts: [$first, $second]} | ... The variable declarations in array patterns (e.g., `. as [$first, $second]`) bind to the elements of the array in from the element at index zero on up, in order. When there is no value at the index for an array pattern element, `null` is bound to that variable. Variables are scoped over the rest of the expression that defines them, so .realnames as $names | (.posts[] | {title, author: $names[.author]}) will work, but (.realnames as $names | .posts[]) | {title, author: $names[.author]} won't. For programming language theorists, it's more accurate to say that jq variables are lexically-scoped bindings. In particular there's no way to change the value of a binding; one can only setup a new binding with the same name, but which will not be visible where the old one was. examples: - program: '.bar as $x | .foo | . + $x' input: '{"foo":10, "bar":200}' output: ['210'] - program: '. as $i|[(.*2|. as $i| $i), $i]' input: '5' output: ['[10,5]'] - program: '. as [$a, $b, {c: $c}] | $a + $b + $c' input: '[2, 3, {"c": 4, "d": 5}]' output: ['9'] - program: '.[] as [$a, $b] | {a: $a, b: $b}' input: '[[0], [0, 1], [2, 1, 0]]' output: ['{"a":0,"b":null}', '{"a":0,"b":1}', '{"a":2,"b":1}'] - title: 'Destructuring Alternative Operator: `?//`' body: | The destructuring alternative operator provides a concise mechanism for destructuring an input that can take one of several forms. Suppose we have an API that returns a list of resources and events associated with them, and we want to get the user_id and timestamp of the first event for each resource. The API (having been clumsily converted from XML) will only wrap the events in an array if the resource has multiple events: {"resources": [{"id": 1, "kind": "widget", "events": {"action": "create", "user_id": 1, "ts": 13}}, {"id": 2, "kind": "widget", "events": [{"action": "create", "user_id": 1, "ts": 14}, {"action": "destroy", "user_id": 1, "ts": 15}]}]} We can use the destructuring alternative operator to handle this structural change simply: .resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$user_id, $ts}]} | {$user_id, $kind, $id, $ts} Or, if we aren't sure if the input is an array of values or an object: .[] as [$id, $kind, $user_id, $ts] ?// {$id, $kind, $user_id, $ts} | ... Each alternative need not define all of the same variables, but all named variables will be available to the subsequent expression. Variables not matched in the alternative that succeeded will be `null`: .resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$first_user_id, $first_ts}]} | {$user_id, $first_user_id, $kind, $id, $ts, $first_ts} Additionally, if the subsequent expression returns an error, the alternative operator will attempt to try the next binding. Errors that occur during the final alternative are passed through. [[3]] | .[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end examples: - program: '.[] as {$a, $b, c: {$d, $e}} ?// {$a, $b, c: [{$d, $e}]} | {$a, $b, $d, $e}' input: '[{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]' output: ['{"a":1,"b":2,"d":3,"e":4}', '{"a":1,"b":2,"d":3,"e":4}'] - program: '.[] as {$a, $b, c: {$d}} ?// {$a, $b, c: [{$e}]} | {$a, $b, $d, $e}' input: '[{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]' output: ['{"a":1,"b":2,"d":3,"e":null}', '{"a":1,"b":2,"d":null,"e":4}'] - program: '.[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end' input: '[[3]]' output: ['{"a":null,"b":3}'] - title: 'Defining Functions' body: | You can give a filter a name using "def" syntax: def increment: . + 1; From then on, `increment` is usable as a filter just like a builtin function (in fact, this is how many of the builtins are defined). A function may take arguments: def map(f): [.[] | f]; Arguments are passed as _filters_ (functions with no arguments), _not_ as values. The same argument may be referenced multiple times with different inputs (here `f` is run for each element of the input array). Arguments to a function work more like callbacks than like value arguments. This is important to understand. Consider: def foo(f): f|f; 5|foo(.*2) The result will be 20 because `f` is `.*2`, and during the first invocation of `f` `.` will be 5, and the second time it will be 10 (5 * 2), so the result will be 20. Function arguments are filters, and filters expect an input when invoked. If you want the value-argument behaviour for defining simple functions, you can just use a variable: def addvalue(f): f as $f | map(. + $f); Or use the short-hand: def addvalue($f): ...; With either definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. Do note that calling `addvalue(.[])` will cause the `map(. + $f)` part to be evaluated once per value in the value of `.` at the call site. Multiple definitions using the same function name are allowed. Each re-definition replaces the previous one for the same number of function arguments, but only for references from functions (or main program) subsequent to the re-definition. See also the section below on scoping. examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' output: ['[[1,2,1], [10,20,10]]'] - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' input: '[[1,2],[10,20]]' output: ['[[1,2,1,2], [10,20,1,2]]'] - title: 'Scoping' body: | There are two types of symbols in jq: value bindings (a.k.a., "variables"), and functions. Both are scoped lexically, with expressions being able to refer only to symbols that have been defined "to the left" of them. The only exception to this rule is that functions can refer to themselves so as to be able to create recursive functions. For example, in the following expression there is a binding which is visible "to the right" of it, `... | .*3 as $times_three | [. + $times_three] | ...`, but not "to the left". Consider this expression now, `... | (.*3 as $times_three | [.+ $times_three]) | ...`: here the binding `$times_three` is _not_ visible past the closing parenthesis. - title: Reduce body: | The `reduce` syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer. As an example, we'll pass `[3,2,1]` to this expression: reduce .[] as $item (0; . + $item) For each result that `.[]` produces, `. + $item` is run to accumulate a running total, starting from 0. In this example, `.[]` produces the results 3, 2, and 1, so the effect is similar to running something like this: 0 | (3 as $item | . + $item) | (2 as $item | . + $item) | (1 as $item | . + $item) examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - title: "`isempty(exp)`" body: | Returns true if `exp` produces no outputs, false otherwise. examples: - program: 'isempty(empty)' input: 'null' output: ['true'] - title: "`limit(n; exp)`" body: | The `limit` function extracts up to `n` outputs from `exp`. examples: - program: '[limit(3;.[])]' input: '[0,1,2,3,4,5,6,7,8,9]' output: ['[0,1,2]'] - title: "`first(expr)`, `last(expr)`, `nth(n; expr)`" body: | The `first(expr)` and `last(expr)` functions extract the first and last values from `expr`, respectively. The `nth(n; expr)` function extracts the nth value output by `expr`. This can be defined as `def nth(n; expr): last(limit(n + 1; expr));`. Note that `nth(n; expr)` doesn't support negative values of `n`. examples: - program: '[first(range(.)), last(range(.)), nth(./2; range(.))]' input: '10' output: ['[0,9,5]'] - title: "`first`, `last`, `nth(n)`" body: | The `first` and `last` functions extract the first and last values from any array at `.`. The `nth(n)` function extracts the nth value of any array at `.`. examples: - program: '[range(.)]|[first, last, nth(5)]' input: '10' output: ['[0,9,5]'] - title: "`foreach`" body: | The `foreach` syntax is similar to `reduce`, but intended to allow the construction of `limit` and reducers that produce intermediate results (see example). The form is `foreach EXP as $var (INIT; UPDATE; EXTRACT)`. Like `reduce`, `INIT` is evaluated once to produce a state value, then each output of `EXP` is bound to `$var`, `UPDATE` is evaluated for each output of `EXP` with the current state and with `$var` visible. Each value output by `UPDATE` replaces the previous state. Finally, `EXTRACT` is evaluated for each new state to extract an output of `foreach`. This is mostly useful only for constructing `reduce`- and `limit`-like functions. But it is much more general, as it allows for partial reductions (see the example below). examples: - program: '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]] else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]' input: '[1,2,3,4,null,"a","b",null]' output: ['[[1,2,3,4],["a","b"]]'] - title: Recursion body: | As described above, `recurse` uses recursion, and any jq function can be recursive. The `while` builtin is also implemented in terms of recursion. Tail calls are optimized whenever the expression to the left of the recursive call outputs its last value. In practice this means that the expression to the left of the recursive call should not produce more than one output for each input. For example: def recurse(f): def r: ., (f | select(. != null) | r); r; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def repeat(exp): def _repeat: exp, _repeat; _repeat; - title: Generators and iterators body: | Some jq operators and functions are actually generators in that they can produce zero, one, or more values for each input, just as one might expect in other programming languages that have generators. For example, `.[]` generates all the values in its input (which must be an array or an object), `range(0; 10)` generates the integers between 0 and 10, and so on. Even the comma operator is a generator, generating first the values generated by the expression to the left of the comma, then for each of those, the values generate by the expression on the right of the comma. The `empty` builtin is the generator that produces zero outputs. The `empty` builtin backtracks to the preceding generator expression. All jq functions can be generators just by using builtin generators. It is also possible to define new generators using only recursion and the comma operator. If the recursive call(s) is(are) "in tail position" then the generator will be efficient. In the example below the recursive call by `_range` to itself is in tail position. The example shows off three advanced topics: tail recursion, generator construction, and sub-functions. examples: - program: '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)); range(0; 10; 3)' input: 'null' output: ['0', '3', '6', '9'] - program: 'def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; [while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: 'Math' body: | jq currently only has IEEE754 double-precision (64-bit) floating point number support. Besides simple arithmetic operators such as `+`, jq also has most standard math functions from the C math library. C math functions that take a single input argument (e.g., `sin()`) are available as zero-argument jq functions. C math functions that take two input arguments (e.g., `pow()`) are available as two-argument jq functions that ignore `.`. C math functions that take three input arguments are available as three-argument jq functions that ignore `.`. Availability of standard math functions depends on the availability of the corresponding math functions in your operating system and C math library. Unavailable math functions will be defined but will raise an error. One-input C math functions: `acos` `acosh` `asin` `asinh` `atan` `atanh` `cbrt` `ceil` `cos` `cosh` `erf` `erfc` `exp` `exp10` `exp2` `expm1` `fabs` `floor` `gamma` `j0` `j1` `lgamma` `log` `log10` `log1p` `log2` `logb` `nearbyint` `pow10` `rint` `round` `significand` `sin` `sinh` `sqrt` `tan` `tanh` `tgamma` `trunc` `y0` `y1`. Two-input C math functions: `atan2` `copysign` `drem` `fdim` `fmax` `fmin` `fmod` `frexp` `hypot` `jn` `ldexp` `modf` `nextafter` `nexttoward` `pow` `remainder` `scalb` `scalbln` `yn`. Three-input C math functions: `fma`. See your system's manual for more information on each of these. - title: 'I/O' body: | At this time jq has minimal support for I/O, mostly in the form of control over when inputs are read. Two builtins functions are provided for this, `input` and `inputs`, that read from the same sources (e.g., `stdin`, files named on the command-line) as jq itself. These two builtins, and jq's own reading actions, can be interleaved with each other. Two builtins provide minimal output capabilities, `debug`, and `stderr`. (Recall that a jq program's output values are always output as JSON texts on `stdout`.) The `debug` builtin can have application-specific behavior, such as for executables that use the libjq C API but aren't the jq executable itself. The `stderr` builtin outputs its input in raw mode to stder with no additional decoration, not even a newline. Most jq builtins are referentially transparent, and yield constant and repeatable value streams when applied to constant inputs. This is not true of I/O builtins. entries: - title: "`input`" body: | Outputs one new input. - title: "`inputs`" body: | Outputs all remaining inputs, one by one. This is primarily useful for reductions over a program's inputs. - title: "`debug`" body: | Causes a debug message based on the input value to be produced. The jq executable wraps the input value with `["DEBUG:", ]` and prints that and a newline on stderr, compactly. This may change in the future. - title: "`stderr`" body: | Prints its input in raw and compact mode to stderr with no additional decoration, not even a newline. - title: "`input_filename`" body: | Returns the name of the file whose input is currently being filtered. Note that this will not work well unless jq is running in a UTF-8 locale. - title: "`input_line_number`" body: | Returns the line number of the input currently being filtered. - title: 'Streaming' body: | With the `--stream` option jq can parse input texts in a streaming fashion, allowing jq programs to start processing large JSON texts immediately rather than after the parse completes. If you have a single JSON text that is 1GB in size, streaming it will allow you to process it much more quickly. However, streaming isn't easy to deal with as the jq program will have `[, ]` (and a few other forms) as inputs. Several builtins are provided to make handling streams easier. The examples below use the streamed form of `[0,[1]]`, which is `[[0],0],[[1,0],1],[[1,0]],[[1]]`. Streaming forms include `[, ]` (to indicate any scalar value, empty array, or empty object), and `[]` (to indicate the end of an array or object). Future versions of jq run with `--stream` and `-seq` may output additional forms such as `["error message"]` when an input text fails to parse. entries: - title: "`truncate_stream(stream_expression)`" body: | Consumes a number as input and truncates the corresponding number of path elements from the left of the outputs of the given streaming expression. examples: - program: '[1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])]' input: '1' output: ['[[[0],2],[[0]]]'] - title: "`fromstream(stream_expression)`" body: | Outputs values corresponding to the stream expression's outputs. examples: - program: 'fromstream(1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))' input: 'null' output: ['[2]'] - title: "`tostream`" body: | The `tostream` builtin outputs the streamed form of its input. examples: - program: '. as $dot|fromstream($dot|tostream)|.==$dot' input: '[0,[1,{"a":1},{"b":2}]]' output: ['true'] - title: Assignment body: | Assignment works a little differently in jq than in most programming languages. jq doesn't distinguish between references to and copies of something - two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object". If an object has two fields which are arrays, `.foo` and `.bar`, and you append something to `.foo`, then `.bar` will not get bigger, even if you've previously set `.bar = .foo`. If you're used to programming in languages like Python, Java, Ruby, Javascript, etc. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance it doesn't actually do that, but that's the general idea). This means that it's impossible to build circular values in jq (such as an array whose first element is itself). This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON. All the assignment operators in jq have path expressions on the left-hand side (LHS). The right-hand side (RHS) provides values to set to the paths named by the LHS path expressions. Values in jq are always immutable. Internally, assignment works by using a reduction to compute new, replacement values for `.` that have had all the desired assignments applied to `.`, then outputting the modified value. This might be made clear by this example: `{a:{b:{c:1}}} | (.a.b|=3), .`. This will output `{"a":{"b":3}}` and `{"a":{"b":{"c":1}}}` because the last sub-expression, `.`, sees the original value, not the modified value. Most users will want to use modification assignment operators, such as `|=` or `+=`, rather than `=`. Note that the LHS of assignment operators refers to a value in `.`. Thus `$var.foo = 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo = 1` instead. Note too that `.a,.b=0` does not set `.a` and `.b`, but `(.a,.b)=0` sets both. entries: - title: "Update-assignment: `|=`" body: | This is the "update" operator '|='. It takes a filter on the right-hand side and works out the new value for the property of `.` being assigned to by running the old value through this expression. For instance, (.foo, .bar) |= .+1 will build an object with the "foo" field set to the input's "foo" plus 1, and the "bar" field set to the input's "bar" plus 1. The left-hand side can be any general path expression; see `path()`. Note that the left-hand side of '|=' refers to a value in `.`. Thus `$var.foo |= . + 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo |= . + 1` instead. If the right-hand side outputs no values (i.e., `empty`), then the left-hand side path will be deleted, as with `del(path)`. If the right-hand side outputs multiple values, only the first one will be used (COMPATIBILITY NOTE: in jq 1.5 and earlier releases, it used to be that only the last one was used). examples: - program: '(..|select(type=="boolean")) |= if . then 1 else 0 end' input: '[true,false,[5,true,[true,[false]],false]]' output: ['[1,0,[5,1,[1,[0]],0]]'] - title: "Arithmetic update-assignment: `+=`, `-=`, `*=`, `/=`, `%=`, `//=`" body: | jq has a few operators of the form `a op= b`, which are all equivalent to `a |= . op b`. So, `+= 1` can be used to increment values, being the same as `|= . + 1`. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] - title: "Plain assignment: `=`" body: | This is the plain assignment operator. Unlike the others, the input to the right-hand-side (RHS) is the same as the input to the left-hand-side (LHS) rather than the value at the LHS path, and all values output by the RHS will be used (as shown below). If the RHS of '=' produces multiple values, then for each such value jq will set the paths on the left-hand side to the value and then it will output the modified `.`. For example, `(.a,.b)=range(2)` outputs `{"a":0,"b":0}`, then `{"a":1,"b":1}`. The "update" assignment forms (see above) do not do this. This example should show the difference between '=' and '|=': Provide input '{"a": {"b": 10}, "b": 20}' to the programs: .a = .b .a |= .b The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20, "b": 20}. The latter will set the "a" field of the input to the "a" field's "b" field, producing {"a": 10, "b": 20}. Another example of the difference between '=' and '|=': null|(.a,.b)=range(3) outputs '{"a":0,"b":0}', '{"a":1,"b":1}', and '{"a":2,"b":2}', while null|(.a,.b)|=range(3) outputs just '{"a":0,"b":0}'. - title: Complex assignments body: | Lots more things are allowed on the left-hand side of a jq assignment than in most languages. We've already seen simple field accesses on the left hand side, and it's no surprise that array accesses work just as well: .posts[0].title = "JQ Manual" What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: .posts[].comments |= . + ["this is great"] That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts). When jq encounters an assignment like 'a = b', it records the "path" taken to select a part of the input document while executing a. This path is then used to find which part of the input to change while executing the assignment. Any filter may be used on the left-hand side of an equals - whichever paths it selects from the input will be where the assignment is performed. This is a very powerful operation. Suppose we wanted to add a comment to blog posts, using the same "blog" input above. This time, we only want to comment on the posts written by "stedolan". We can find those posts using the "select" function described earlier: .posts[] | select(.author == "stedolan") The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: (.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."] - title: Modules body: | jq has a library/module system. Modules are files whose names end in `.jq`. Modules imported by a program are searched for in a default search path (see below). The `import` and `include` directives allow the importer to alter this path. Paths in the a search path are subject to various substitutions. For paths starting with "~/", the user's home directory is substituted for "~". For paths starting with "$ORIGIN/", the path of the jq executable is substituted for "$ORIGIN". For paths starting with "./" or paths that are ".", the path of the including file is substituted for ".". For top-level programs given on the command-line, the current directory is used. Import directives can optionally specify a search path to which the default is appended. The default search path is the search path given to the `-L` command-line option, else `["~/.jq", "$ORIGIN/../lib/jq", "$ORIGIN/../lib"]`. Null and empty string path elements terminate search path processing. A dependency with relative path "foo/bar" would be searched for in "foo/bar.jq" and "foo/bar/bar.jq" in the given search path. This is intended to allow modules to be placed in a directory along with, for example, version control files, README files, and so on, but also to allow for single-file modules. Consecutive components with the same name are not allowed to avoid ambiguities (e.g., "foo/foo"). For example, with `-L$HOME/.jq` a module `foo` can be found in `$HOME/.jq/foo.jq` and `$HOME/.jq/foo/foo.jq`. If "$HOME/.jq" is a file, it is sourced into the main program. entries: - title: "`import RelativePathString as NAME [];`" body: | Imports a module found at the given path relative to a directory in a search path. A ".jq" suffix will be added to the relative path string. The module's symbols are prefixed with "NAME::". The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`include RelativePathString [];`" body: | Imports a module found at the given path relative to a directory in a search path as if it were included in place. A ".jq" suffix will be added to the relative path string. The module's symbols are imported into the caller's namespace as if the module's content had been included directly. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. - title: "`import RelativePathString as $NAME [];`" body: | Imports a JSON file found at the given path relative to a directory in a search path. A ".json" suffix will be added to the relative path string. The file's data will be available as `$NAME::NAME`. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`module ;`" body: | This directive is entirely optional. It's not required for proper operation. It serves only the purpose of providing metadata that can be read with the `modulemeta` builtin. The metadata must be a constant jq expression. It should be an object with keys like "homepage". At this time jq doesn't use this metadata, but it is made available to users via the `modulemeta` builtin. - title: "`modulemeta`" body: | Takes a module name as input and outputs the module's metadata as an object, with the module's imports (including metadata) as an array value for the "deps" key. Programs can use this to query a module's metadata, which they could then use to, for example, search for, download, and install missing dependencies. - title: Colors body: | To configure alternative colors just set the `JQ_COLORS` environment variable to colon-delimited list of partial terminal escape sequences like `"1;31"`, in this order: - color for `null` - color for `false` - color for `true` - color for numbers - color for strings - color for arrays - color for objects The default color scheme is the same as setting `"JQ_COLORS=1;30:0;39:0;39:0;39:0;32:1;39:1;39"`. This is not a manual for VT100/ANSI escapes. However, each of these color specifications should consist of two numbers separated by a semi-colon, where the first number is one of these: - 1 (bright) - 2 (dim) - 4 (underscore) - 5 (blink) - 7 (reverse) - 8 (hidden) and the second is one of these: - 30 (black) - 31 (red) - 32 (green) - 33 (yellow) - 34 (blue) - 35 (magenta) - 36 (cyan) - 37 (white) jq-jq-1.6/docs/content/3.manual/v1.3/0000700000175000017500000000000013366726451016422 5ustar czchenczchenjq-jq-1.6/docs/content/3.manual/v1.3/manual.yml0000600000175000017500000013413413366726451020432 0ustar czchenczchen--- headline: jq 1.3 Manual history: | *The manual for the development version of jq can be found [here](/jq/manual).* body: | A jq program is a "filter": it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks. Filters can be combined in various ways - you can pipe the output of one filter into another filter, or collect the output of a filter into an array. Some filters produce multiple results, for instance there's one that produces all the elements of its input array. Piping that filter into a second runs the second filter for each element of the array. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq. It's important to remember that every filter has an input and an output. Even literals like "hello" or 42 are filters - they take an input but always produce the same literal as output. Operations that combine two filters, like addition, generally feed the same input to both and combine the results. So, you can implement an averaging filter as `add / length` - feeding the input array both to the `add` filter and the `length` filter and dividing the results. But that's getting ahead of ourselves. :) Let's start with something simpler: manpage_intro: | jq(1) -- Command-line JSON processor ==================================== ## SYNOPSIS `jq` [...] [...] `jq` can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents. For instance, running the command `jq 'map(.price) | add'` will take an array of JSON objects as input and return the sum of their "price" fields. By default, `jq` reads a stream of JSON objects (whitespace separated) from `stdin`. One or more may be specified, in which case `jq` will read input from those instead. The are described in the [INVOKING JQ] section, they mostly concern input and output formatting. The is written in the jq language and specifies how to transform the input document. ## FILTERS manpage_epilogue: | ## BUGS Presumably. Report them or discuss them at: https://github.com/stedolan/jq/issues ## AUTHOR Stephen Dolan `` sections: - title: Invoking jq body: | jq filters run on a stream of JSON data. The input to jq is parsed as a sequence of whitespace-separated JSON values which are passed through the provided filter one at a time. The output(s) of the filter are written to standard out, again as a sequence of whitespace-separated JSON data. You can affect how jq reads and writes its input and output using some command-line options: * `--slurp`/`-s`: Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once. * `--raw-input`/`-R`: Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with `--slurp`, then the entire input is passed to the filter as a single long string. * `--null-input`/`-n`: Don't read any input at all! Instead, the filter is run once using `null` as the input. This is useful when using jq as a simple calculator or to construct JSON data from scratch. * `--compact-output` / `-c`: By default, jq pretty-prints JSON output. Using this option will result in more compact output by instead putting each JSON object on a single line. * `--color-output` / `-C` and `--monochrome-output` / `-M`: By default, jq outputs colored JSON if writing to a terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even if the input specified them as escape sequences (like "\u03bc"). Using this option, you can force jq to produce pure ASCII output with every non-ASCII character replaced with the equivalent escape sequence. * `--raw-output` / `-r`: With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems. * `--arg name value`: This option passes a value to the jq program as a predefined variable. If you run jq with `--arg foo bar`, then `$foo` is available in the program and has the value `"bar"`. - title: Basic filters entries: - title: "`.`" body: | The absolute simplest (and least interesting) filter is `.`. This is a filter that takes its input and produces it unchanged as output. Since jq by default pretty-prints all output, this trivial program can be a useful way of formatting JSON output from, say, `curl`. examples: - program: '.' input: '"Hello, world!"' output: ['"Hello, world!"'] - title: "`.foo`" body: | The simplest *useful* filter is .foo. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there's none present. examples: - program: '.foo' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - title: "`.[foo]`, `.[2]`, `.[10:15]`" body: | You can also look up fields of an object using syntax like `.["foo"]` (.foo above is a shorthand version of this). This one works for arrays as well, if the key is an integer. Arrays are zero-based (like javascript), so `.[2]` returns the third element of the array. The `.[10:15]` syntax can be used to return a subarray of an array. The array returned by `.[10:15]` will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive). Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array). examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['{"name":"JSON", "good":true}'] - program: '.[2]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] - program: '.[2:4]' input: '["a","b","c","d","e"]' output: ['["c", "d"]'] - program: '.[:3]' input: '["a","b","c","d","e"]' output: ['["a", "b", "c"]'] - program: '.[-2:]' input: '["a","b","c","d","e"]' output: ['["d", "e"]'] - title: "`.[]`" body: | If you use the `.[foo]` syntax, but omit the index entirely, it will return *all* of the elements of an array. Running `.[]` with the input `[1,2,3]` will produce the numbers as three separate results, rather than as a single array. You can also use this on an object, and it will return all the values of the object. examples: - program: '.[]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: - '{"name":"JSON", "good":true}' - '{"name":"XML", "good":false}' - program: '.[]' input: '[]' output: [] - program: '.[]' input: '{"a": 1, "b": 1}' output: ['1', '1'] - title: "`,`" body: | If two filters are separated by a comma, then the input will be fed into both and there will be multiple outputs: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right. For instance, filter `.foo, .bar`, produces both the "foo" fields and "bar" fields as separate outputs. examples: - program: '.foo, .bar' input: '{"foo": 42, "bar": "something else", "baz": true}' output: ['42', '"something else"'] - program: ".user, .projects[]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['"stedolan"', '"jq"', '"wikiflow"'] - program: '.[4,2]' input: '["a","b","c","d","e"]' output: ['"e"', '"c"'] - title: "`|`" body: | The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right. It's pretty much the same as the Unix shell's pipe, if you're used to that. If the one on the left produces multiple results, the one on the right will be run for each of those results. So, the expression `.[] | .foo` retrieves the "foo" field of each element of the input array. examples: - program: '.[] | .name' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['"JSON"', '"XML"'] - title: Types and Values body: | jq supports the same set of datatypes as JSON - numbers, strings, booleans, arrays, objects (which in JSON-speak are hashes with only string keys), and "null". Booleans, null, strings and numbers are written the same way as in javascript. Just like everything else in jq, these simple values take an input and produce an output - `42` is a valid jq expression that takes an input, ignores it, and returns 42 instead. entries: - title: Array construction - `[]` body: | As in JSON, `[]` is used to construct arrays, as in `[1,2,3]`. The elements of the arrays can be any jq expression. All of the results produced by all of the expressions are collected into one big array. You can use it to construct an array out of a known quantity of values (as in `[.foo, .bar, .baz]`) or to "collect" all the results of a filter into an array (as in `[.items[].name]`) Once you understand the "," operator, you can look at jq's array syntax in a different light: the expression `[1,2,3]` is not using a built-in syntax for comma-separated arrays, but is instead applying the `[]` operator (collect results) to the expression 1,2,3 (which produces three different results). If you have a filter `X` that produces four results, then the expression `[X]` will produce a single result, an array of four elements. examples: - program: "[.user, .projects[]]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['["stedolan", "jq", "wikiflow"]'] - title: Objects - `{}` body: | Like JSON, `{}` is for constructing objects (aka dictionaries or hashes), as in: `{"a": 42, "b": 17}`. If the keys are "sensible" (all alphabetic characters), then the quotes can be left off. The value can be any expression (although you may need to wrap it in parentheses if it's a complicated one), which gets applied to the {} expression's input (remember, all filters have an input and an output). {foo: .bar} will produce the JSON object `{"foo": 42}` if given the JSON object `{"bar":42, "baz":43}`. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write {user: .user, title: .title} Because that's so common, there's a shortcut syntax: `{user, title}`. If one of the expressions produces multiple results, multiple dictionaries will be produced. If the input's {"user":"stedolan","titles":["JQ Primer", "More JQ"]} then the expression {user, title: .titles[]} will produce two outputs: {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} Putting parentheses around the key means it will be evaluated as an expression. With the same input as above, {(.user): .titles} produces {"stedolan": ["JQ Primer", "More JQ"]} examples: - program: '{user, title: .titles[]}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: - '{"user":"stedolan", "title": "JQ Primer"}' - '{"user":"stedolan", "title": "More JQ"}' - program: '{(.user): .titles}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: ['{"stedolan": ["JQ Primer", "More JQ"]}'] - title: Builtin operators and functions body: | Some jq operator (for instance, `+`) do different things depending on the type of their arguments (arrays, numbers, etc.). However, jq never does implicit type conversions. If you try to add a string to an object you'll get an error message and no result. entries: - title: Addition - `+` body: | The operator `+` takes two filters, applies them both to the same input, and adds the results together. What "adding" means depends on the types involved: - **Numbers** are added by normal arithmetic. - **Arrays** are added by being concatenated into a larger array. - **Strings** are added by being joined into a larger string. - **Objects** are added by merging, that is, inserting all the key-value pairs from both objects into a single combined object. If both objects contain a value for the same key, the object on the right of the `+` wins. `null` can be added to any value, and returns the other value unchanged. examples: - program: '.a + 1' input: '{"a": 7}' output: ['8'] - program: '.a + .b' input: '{"a": [1,2], "b": [3,4]}' output: ['[1,2,3,4]'] - program: '.a + null' input: '{"a": 1}' output: ['1'] - program: '.a + 1' input: '{}' output: ['1'] - program: '{a: 1} + {b: 2} + {c: 3} + {a: 42}' input: 'null' output: ['{"a": 42, "b": 2, "c": 3}'] - title: Subtraction - `-` body: | As well as normal arithmetic subtraction on numbers, the `-` operator can be used on arrays to remove all occurences of the second array's elements from the first array. examples: - program: '4 - .a' input: '{"a":3}' output: ['1'] - program: . - ["xml", "yaml"] input: '["xml", "yaml", "json"]' output: ['["json"]'] - title: Multiplication, division - `*` and `/` body: | These operators only work on numbers, and do the expected. examples: - program: '10 / . * 3' input: 5 output: [6] - title: `length` body: | The builtin function `length` gets the length of various different types of value: - The length of a **string** is the number of Unicode codepoints it contains (which will be the same as its JSON-encoded length in bytes if it's pure ASCII). - The length of an **array** is the number of elements. - The length of an **object** is the number of key-value pairs. - The length of **null** is zero. examples: - program: '.[] | length' input: '[[1,2], "string", {"a":2}, null]' output: [2, 6, 1, 0] - title: `keys` body: | The builtin function `keys`, when given an object, returns its keys in an array. The keys are sorted "alphabetically", by unicode codepoint order. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings. When `keys` is given an array, it returns the valid indices for that array: the integers from 0 to length-1. examples: - program: 'keys' input: '{"abc": 1, "abcd": 2, "Foo": 3}' output: ['["Foo", "abc", "abcd"]'] - program: 'keys' input: '[42,3,35]' output: ['[0,1,2]'] - title: `has` body: | The builtin function `has` returns whether the input object has the given key, or the input array has an element at the given index. `has($key)` has the same effect as checking whether `$key` is a member of the array returned by `keys`, although `has` will be faster. examples: - program: 'map(has("foo"))' input: '[{"foo": 42}, {}]' output: ['[true, false]'] - program: 'map(has(2))' input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] - title: `to_entries`, `from_entries`, `with_entries` body: | These functions convert between an object and an array of key-value pairs. If `to_entries` is passed an object, then for each `k: v` entry in the input, the output array includes `{"key": k, "value": v}`. `from_entries` does the opposite conversion, and `with_entries(foo)` is a shorthand for `to_entries | map(foo) | from_entries`, useful for doing some operation to all keys and values of an object. examples: - program: 'to_entries' input: '{"a": 1, "b": 2}' output: ['[{"key":"a", "value":1}, {"key":"b", "value":2}]'] - program: 'from_entries' input: '[{"key":"a", "value":1}, {"key":"b", "value":2}]' output: ['{"a": 1, "b": 2}'] - program: 'with_entries(.key |= "KEY_" + .)' input: '{"a": 1, "b": 2}' output: ['{"KEY_a": 1, "KEY_b": 2}'] - title: `select` body: | The function `select(foo)` produces its input unchanged if `foo` returns true for that input, and produces no output otherwise. It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))` will give you `[3]`. examples: - program: 'map(select(. >= 2))' input: '[1,5,3,0,7]' output: ['[5,3,7]'] - title: `empty` body: | `empty` returns no results. None at all. Not even `null`. It's useful on occasion. You'll know if you need it :) examples: - program: '1, empty, 2' input: 'null' output: [1, 2] - program: '[1,2,empty,3]' input: 'null' output: ['[1,2,3]'] - title: `map(x)` body: | For any filter `x`, `map(x)` will run that filter for each element of the input array, and produce the outputs a new array. `map(.+1)` will increment each element of an array of numbers. `map(x)` is equivalent to `[.[] | x]`. In fact, this is how it's defined. examples: - program: 'map(.+1)' input: '[1,2,3]' output: ['[2,3,4]'] - title: `add` body: | The filter `add` takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the `+` operator (described above). If the input is an empty array, `add` returns `null`. examples: - program: add input: '["a","b","c"]' output: ['"abc"'] - program: add input: '[1, 2, 3]' output: [6] - program: add input: '[]' output: ["null"] - title: `range` body: | The `range` function produces a range of numbers. `range(4;10)` produces 6 numbers, from 4 (inclusive) to 10 (exclusive). The numbers are produced as separate outputs. Use `[range(4;10)]` to get a range as an array. examples: - program: 'range(2;4)' input: 'null' output: ['2', '3'] - program: '[range(2;4)]' input: 'null' output: ['[2,3]'] - title: `tonumber` body: | The `tonumber` function parses its input as a number. It will convert correctly-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input. examples: - program: '.[] | tonumber' input: '[1, "1"]' output: [1, 1] - title: `tostring` body: | The `tostring` function prints its input as a string. Strings are left unchanged, and all other values are JSON-encoded. examples: - program: '.[] | tostring' input: '[1, "1", [1]]' output: ['"1"', '"1"', '"[1]"'] - title: `type` body: | The `type` function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object. examples: - program: 'map(type)' input: '[0, false, [], {}, null, "hello"]' output: ['["number", "boolean", "array", "object", "null", "string"]'] - title: `sort, sort_by` body: | The `sort` functions sorts its input, which must be an array. Values are sorted in the following order: * `null` * `false` * `true` * numbers * strings, in alphabetical order (by unicode codepoint value) * arrays, in lexical order * objects The ordering for objects is a little complex: first they're compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key. `sort_by` may be used to sort by a particular field of an object, or by applying any jq filter. `sort_by(foo)` compares two elements by comparing the result of `foo` on each element. examples: - program: 'sort' input: '[8,3,null,6]' output: ['[null,3,6,8]'] - program: 'sort_by(.foo)' input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]' output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]'] - title: `group_by` body: | `group_by(.foo)` takes as input an array, groups the elements having the same `.foo` field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the `.foo` field. Any jq expression, not just a field access, may be used in place of `.foo`. The sorting order is the same as described in the `sort` function above. examples: - program: 'group_by(.foo)' input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]'] - title: `min`, `max`, `min_by`, `max_by` body: | Find the minimum or maximum element of the input array. The `_by` versions allow you to specify a particular field or property to examine, e.g. `min_by(.foo)` finds the object with the smallest `foo` field. examples: - program: 'min' input: '[5,4,2,7]' output: ['2'] - program: 'max_by(.foo)' input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' output: ['{"foo":2, "bar":3}'] - title: `unique` body: | The `unique` function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed. examples: - program: 'unique' input: '[1,2,5,3,5,3,1,3]' output: ['[1,2,3,5]'] - title: `reverse` body: | This function reverses an array. examples: - program: 'reverse' input: '[1,2,3,4]' output: ['[4,3,2,1]'] - title: `contains` body: | The filter `contains(b)` will produce true if b is completely contained within the input. A string B is contained in a string A if B is a substring of A. An array B is contained in an array A is all elements in B are contained in any element in A. An object B is contained in object A if all of the values in B are contained in the value in A with the same key. All other types are assumed to be contained in each other if they are equal. examples: - program: 'contains("bar")' input: '"foobar"' output: ['true'] - program: 'contains(["baz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['true'] - program: 'contains(["bazzzzz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['false'] - program: 'contains({foo: 12, bar: [{barp: 12}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['true'] - program: 'contains({foo: 12, bar: [{barp: 15}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['false'] - title: `recurse` body: | The `recurse` function allows you to search through a recursive structure, and extract interesting data from all levels. Suppose your input represents a filesystem: {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} Now suppose you want to extract all of the filenames present. You need to retrieve `.name`, `.children[].name`, `.children[].children[].name`, and so on. You can do this with: recurse(.children[]) | .name examples: - program: 'recurse(.foo[])' input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}' output: - '{"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}' - '{"foo":[]}' - '{"foo":[{"foo":[]}]}' - '{"foo":[]}' - title: "String interpolation - `\(foo)`" body: | Inside a string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string. examples: - program: '"The input was \(.), which is one less than \(.+1)"' input: '42' output: ['"The input was 42, which is one less than 43"'] - title: "Format strings and escaping" body: | The `@foo` syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth. `@foo` can be used as a filter on its own, the possible escapings are: * `@text`: Calls `tostring`, see that function for details. * `@json`: Serialises the input as JSON. * `@html`: Applies HTML/XML escaping, by mapping the characters `<>&'"` to their entity equivalents `<`, `>`, `&`, `'`, `"`. * `@uri`: Applies percent-encoding, by mapping all reserved URI characters to a `%xx` sequence. * `@csv`: The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition. * `@sh`: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings. * `@base64`: The input is converted to base64 as specified by RFC 4648. This syntax can be combined with string interpolation in a useful way. You can follow a `@foo` token with a string literal. The contents of the string literal will *not* be escaped. However, all interpolations made inside that string literal will be escaped. For instance, @uri "https://www.google.com/search?q=\(.search)" will produce the following output for the input `{"search":"jq!"}`: https://www.google.com/search?q=jq%21 Note that the slashes, question mark, etc. in the URL are not escaped, as they were part of the string literal. examples: - program: '@html' input: '"This works if x < y"' output: ['"This works if x < y"'] # - program: '@html "Anonymous said: \(.)"' # input: '""' # output: ["Anonymous said: <script>alert("lol hax");</script>"] - program: '@sh "echo \(.)"' input: "\"O'Hara's Ale\"" output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""] - title: Conditionals and Comparisons entries: - title: `==`, `!=` body: | The expression 'a == b' will produce 'true' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and 'false' otherwise. In particular, strings are never considered equal to numbers. If you're coming from Javascript, jq's == is like Javascript's === - considering values equal only when they have the same type as well as the same value. != is "not equal", and 'a != b' returns the opposite value of 'a == b' examples: - program: '.[] == 1' input: '[1, 1.0, "1", "banana"]' output: ['true', 'true', 'false', 'false'] - title: if-then-else body: | `if A then B else C end` will act the same as `B` if `A` produces a value other than false or null, but act the same as `C` otherwise. Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you'll sometimes have to be more explicit about the condition you want: you can't test whether, e.g. a string is empty using `if .name then A else B end`, you'll need something more like `if (.name | count) > 0 then A else B end` instead. If the condition `A` produces multiple results, then `B` is evaluated once for each result that is not false or null, and `C` is evaluated once for each false or null. More cases can be added to an if using `elif A then B` syntax. examples: - program: |- if . == 0 then "zero" elif . == 1 then "one" else "many" end input: 2 output: ['"many"'] - title: `>, >=, <=, <` body: | The comparison operators `>`, `>=`, `<=`, `<` return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively). The ordering is the same as that described for `sort`, above. examples: - program: '. < 5' input: 2 output: ['true'] - title: and/or/not body: | jq supports the normal Boolean operators and/or/not. They have the same standard of truth as if expressions - false and null are considered "false values", and anything else is a "true value". If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input. `not` is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in `.foo and .bar | not`. These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default". If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below. examples: - program: '42 and "a string"' input: 'null' output: ['true'] - program: '(true, false) or false' input: 'null' output: ['true', 'false'] # - program: '(true, false) and (true, false)' # input: 'null' # output: ['true', 'false', 'false', 'false'] - program: '(true, true) and (true, false)' input: 'null' output: ['true', 'false', 'true', 'false'] - program: '[true, false | not]' input: 'null' output: ['[false, true]'] - title: Alternative operator - `//` body: | A filter of the form `a // b` produces the same results as `a`, if `a` produces results other than `false` and `null`. Otherwise, `a // b` produces the same results as `b`. This is useful for providing defaults: `.foo // 1` will evaluate to `1` if there's no `.foo` element in the input. It's similar to how `or` is sometimes used in Python (jq's `or` operator is reserved for strictly Boolean operations). examples: - program: '.foo // 42' input: '{"foo": 19}' output: [19] - program: '.foo // 42' input: '{}' output: [42] - title: Advanced features body: | Variables are an absolute necessity in most programming languages, but they're relegated to an "advanced feature" in jq. In most languages, variables are the only means of passing around data. If you calculate a value, and you want to use it more than once, you'll need to store it in a variable. To pass a value to another part of the program, you'll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data. It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq's standard library (many jq functions such as `map` and `find` are in fact written in jq). Finally, jq has a `reduce` operation, which is very powerful but a bit tricky. Again, it's mostly used internally, to define some useful bits of jq's standard library. entries: - title: Variables body: | In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next. Many expressions, for instance `a + b`, pass their input to two distinct subexpressions (here `a` and `b` are both passed the same input), so variables aren't usually necessary in order to use a value twice. For instance, calculating the average value of an array of numbers requires a few variables in most languages - at least one to hold the array, perhaps one for each element or for a loop counter. In jq, it's simply `add / length` - the `add` expression is given the array and produces its sum, and the `length` expression is given the array and produces its length. So, there's generally a cleaner way to solve most problems in jq that defining variables. Still, sometimes they do make things easier, so jq lets you define variables using `expression as $variable`. All variable names start with `$`. Here's a slightly uglier version of the array-averaging example: length as $array_length | add / $array_length We'll need a more complicated problem to find a situation where using variables actually makes our lives easier. Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names. Our input looks like: {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} We want to produce the posts with the author field containing a real name, as in: {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"} We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: .realnames as $names | .posts[] | {title, author: $names[.author]} The expression `exp as $x | ...` means: for each value of expression `exp`, run the rest of the pipeline with the entire original input, and with `$x` set to that value. Thus `as` functions as something of a foreach loop. Variables are scoped over the rest of the expression that defines them, so .realnames as $names | (.posts[] | {title, author: $names[.author]}) will work, but (.realnames as $names | .posts[]) | {title, author: $names[.author]} won't. examples: - program: '.bar as $x | .foo | . + $x' input: '{"foo":10, "bar":200}' output: ['210'] - title: 'Defining Functions' body: | You can give a filter a name using "def" syntax: def increment: . + 1; From then on, `increment` is usable as a filter just like a builtin function (in fact, this is how some of the builtins are defined). A function may take arguments: def map(f): [.[] | f]; Arguments are passed as filters, not as values. The same argument may be referenced multiple times with different inputs (here `f` is run for each element of the input array). Arguments to a function work more like callbacks than like value arguments. If you want the value-argument behaviour for defining simple functions, you can just use a variable: def addvalue(f): f as $value | map(. + $value); With that definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' output: ['[[1,2,1], [10,20,10]]'] - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' input: '[[1,2],[10,20]]' output: ['[[1,2,1,2], [10,20,1,2]]'] - title: Reduce body: | The `reduce` syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer. As an example, we'll pass `[3,2,1]` to this expression: reduce .[] as $item (0; . + $item) For each result that `.[]` produces, `. + $item` is run to accumulate a running total, starting from 0. In this example, `.[]` produces the results 3, 2, and 1, so the effect is similar to running something like this: 0 | (3 as $item | . + $item) | (2 as $item | . + $item) | (1 as $item | . + $item) examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - title: Assignment body: | Assignment works a little differently in jq than in most programming languages. jq doesn't distinguish between references to and copies of something - two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object". If an object has two fields which are arrays, `.foo` and `.bar`, and you append something to `.foo`, then `.bar` will not get bigger. Even if you've just set `.bar = .foo`. If you're used to programming in languages like Python, Java, Ruby, Javascript, etc. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance, it doesn't actually do that, but that's the general idea). entries: - title: "`=`" body: | The filter `.foo = 1` will take as input an object and produce as output an object with the "foo" field set to 1. There is no notion of "modifying" or "changing" something in jq - all jq values are immutable. For instance, .foo = .bar | .foo.baz = 1 will not have the side-effect of setting .bar.baz to be set to 1, as the similar-looking program in Javascript, Python, Ruby or other languages would. Unlike these languages (but like Haskell and some other functional languages), there is no notion of two arrays or objects being "the same array" or "the same object". They can be equal, or not equal, but if we change one of them in no circumstances will the other change behind our backs. This means that it's impossible to build circular values in jq (such as an array whose first element is itself). This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON. - title: "`|=`" body: | As well as the assignment operator '=', jq provides the "update" operator '|=', which takes a filter on the right-hand side and works out the new value for the property being assigned to by running the old value through this expression. For instance, .foo |= .+1 will build an object with the "foo" field set to the input's "foo" plus 1. This example should show the difference between '=' and '|=': Provide input '{"a": {"b": 10}, "b": 20}' to the programs: .a = .b .a |= .b The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20}. The latter will set the "a" field of the input to the "a" field's "b" field, producing {"a": 10}. - title: "`+=`, `-=`, `*=`, `/=`, `//=`" body: | jq has a few operators of the form `a op= b`, which are all equivalent to `a |= . op b`. So, `+= 1` can be used to increment values. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] - title: Complex assignments body: | Lots more things are allowed on the left-hand side of a jq assignment than in most langauges. We've already seen simple field accesses on the left hand side, and it's no surprise that array accesses work just as well: .posts[0].title = "JQ Manual" What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: .posts[].comments |= . + ["this is great"] That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts). When jq encounters an assignment like 'a = b', it records the "path" taken to select a part of the input document while executing a. This path is then used to find which part of the input to change while executing the assignment. Any filter may be used on the left-hand side of an equals - whichever paths it selects from the input will be where the assignment is performed. This is a very powerful operation. Suppose we wanted to add a comment to blog posts, using the same "blog" input above. This time, we only want to comment on the posts written by "stedolan". We can find those posts using the "select" function described earlier: .posts[] | select(.author == "stedolan") The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: (.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."] jq-jq-1.6/docs/content/3.manual/v1.6/0000700000175000017500000000000013366726451016425 5ustar czchenczchenjq-jq-1.6/docs/content/3.manual/v1.6/manual.yml0000600000175000017500000036266413366726451020450 0ustar czchenczchen--- headline: jq Manual (development version) history: | *For released versions, see [jq 1.5](/jq/manual/v1.5), [jq 1.4](/jq/manual/v1.4) or [jq 1.3](/jq/manual/v1.3).* body: | A jq program is a "filter": it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks. Filters can be combined in various ways - you can pipe the output of one filter into another filter, or collect the output of a filter into an array. Some filters produce multiple results, for instance there's one that produces all the elements of its input array. Piping that filter into a second runs the second filter for each element of the array. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq. It's important to remember that every filter has an input and an output. Even literals like "hello" or 42 are filters - they take an input but always produce the same literal as output. Operations that combine two filters, like addition, generally feed the same input to both and combine the results. So, you can implement an averaging filter as `add / length` - feeding the input array both to the `add` filter and the `length` filter and then performing the division. But that's getting ahead of ourselves. :) Let's start with something simpler: manpage_intro: | jq(1) -- Command-line JSON processor ==================================== ## SYNOPSIS `jq` [...] [...] `jq` can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents. For instance, running the command `jq 'map(.price) | add'` will take an array of JSON objects as input and return the sum of their "price" fields. `jq` can accept text input as well, but by default, `jq` reads a stream of JSON entities (including numbers and other literals) from `stdin`. Whitespace is only needed to separate entities such as 1 and 2, and true and false. One or more may be specified, in which case `jq` will read input from those instead. The are described in the [INVOKING JQ] section; they mostly concern input and output formatting. The is written in the jq language and specifies how to transform the input file or document. ## FILTERS manpage_epilogue: | ## BUGS Presumably. Report them or discuss them at: https://github.com/stedolan/jq/issues ## AUTHOR Stephen Dolan `` sections: - title: Invoking jq body: | jq filters run on a stream of JSON data. The input to jq is parsed as a sequence of whitespace-separated JSON values which are passed through the provided filter one at a time. The output(s) of the filter are written to standard out, again as a sequence of whitespace-separated JSON data. Note: it is important to mind the shell's quoting rules. As a general rule it's best to always quote (with single-quote characters) the jq program, as too many characters with special meaning to jq are also shell meta-characters. For example, `jq "foo"` will fail on most Unix shells because that will be the same as `jq foo`, which will generally fail because `foo is not defined`. When using the Windows command shell (cmd.exe) it's best to use double quotes around your jq program when given on the command-line (instead of the `-f program-file` option), but then double-quotes in the jq program need backslash escaping. You can affect how jq reads and writes its input and output using some command-line options: * `--version`: Output the jq version and exit with zero. * `--seq`: Use the `application/json-seq` MIME type scheme for separating JSON texts in jq's input and output. This means that an ASCII RS (record separator) character is printed before each value on output and an ASCII LF (line feed) is printed after every output. Input JSON texts that fail to parse are ignored (but warned about), discarding all subsequent input until the next RS. This mode also parses the output of jq without the `--seq` option. * `--stream`: Parse the input in streaming fashion, outputing arrays of path and leaf values (scalars and empty arrays or empty objects). For example, `"a"` becomes `[[],"a"]`, and `[[],"a",["b"]]` becomes `[[0],[]]`, `[[1],"a"]`, and `[[1,0],"b"]`. This is useful for processing very large inputs. Use this in conjunction with filtering and the `reduce` and `foreach` syntax to reduce large inputs incrementally. * `--slurp`/`-s`: Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once. * `--raw-input`/`-R`: Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with `--slurp`, then the entire input is passed to the filter as a single long string. * `--null-input`/`-n`: Don't read any input at all! Instead, the filter is run once using `null` as the input. This is useful when using jq as a simple calculator or to construct JSON data from scratch. * `--compact-output` / `-c`: By default, jq pretty-prints JSON output. Using this option will result in more compact output by instead putting each JSON object on a single line. * `--tab`: Use a tab for each indentation level instead of two spaces. * `--indent n`: Use the given number of spaces (no more than 8) for indentation. * `--color-output` / `-C` and `--monochrome-output` / `-M`: By default, jq outputs colored JSON if writing to a terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. Colors can be configured with the `JQ_COLORS` environment variable (see below). * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even if the input specified them as escape sequences (like "\u03bc"). Using this option, you can force jq to produce pure ASCII output with every non-ASCII character replaced with the equivalent escape sequence. * `--unbuffered` Flush the output after each JSON object is printed (useful if you're piping a slow data source into jq and piping jq's output elsewhere). * `--sort-keys` / `-S`: Output the fields of each object with the keys in sorted order. * `--raw-output` / `-r`: With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems. * `--join-output` / `-j`: Like `-r` but jq won't print a newline after each output. * `-f filename` / `--from-file filename`: Read filter from the file rather than from a command line, like awk's -f option. You can also use '#' to make comments. * `-Ldirectory` / `-L directory`: Prepend `directory` to the search list for modules. If this option is used then no builtin search list is used. See the section on modules below. * `-e` / `--exit-status`: Sets the exit status of jq to 0 if the last output values was neither `false` nor `null`, 1 if the last output value was either `false` or `null`, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran. Another way to set the exit status is with the `halt_error` builtin function. * `--arg name value`: This option passes a value to the jq program as a predefined variable. If you run jq with `--arg foo bar`, then `$foo` is available in the program and has the value `"bar"`. Note that `value` will be treated as a string, so `--arg foo 123` will bind `$foo` to `"123"`. Named arguments are also available to the jq program as `$ARGS.named`. * `--argjson name JSON-text`: This option passes a JSON-encoded value to the jq program as a predefined variable. If you run jq with `--argjson foo 123`, then `$foo` is available in the program and has the value `123`. * `--slurpfile variable-name filename`: This option reads all the JSON texts in the named file and binds an array of the parsed JSON values to the given global variable. If you run jq with `--slurpfile foo bar`, then `$foo` is available in the program and has an array whose elements correspond to the texts in the file named `bar`. * `--rawfile variable-name filename`: This option reads in the named file and binds its contents to the given global variable. If you run jq with `--rawfile foo bar`, then `$foo` is available in the program and has a string whose contents are to the texs in the file named `bar`. * `--argfile variable-name filename`: Do not use. Use `--slurpfile` instead. (This option is like `--slurpfile`, but when the file has just one text, then that is used, else an array of texts is used as in `--slurpfile`.) * `--args`: Remaining arguments are positional string arguments. These are available to the jq program as `$ARGS.positional[]`. * `--jsonargs`: Remaining arguments are positional JSON text arguments. These are available to the jq program as `$ARGS.positional[]`. * `--run-tests [filename]`: Runs the tests in the given file or standard input. This must be the last option given and does not honor all preceding options. The input consists of comment lines, empty lines, and program lines followed by one input line, as many lines of output as are expected (one per output), and a terminating empty line. Compilation failure tests start with a line containing only "%%FAIL", then a line containing the program to compile, then a line containing an error message to compare to the actual. Be warned that this option can change backwards-incompatibly. - title: Basic filters entries: - title: "Identity: `.`" body: | The absolute simplest filter is `.` . This is a filter that takes its input and produces it unchanged as output. That is, this is the identity operator. Since jq by default pretty-prints all output, this trivial program can be a useful way of formatting JSON output from, say, `curl`. examples: - program: '.' input: '"Hello, world!"' output: ['"Hello, world!"'] - title: "Object Identifier-Index: `.foo`, `.foo.bar`" body: | The simplest *useful* filter is `.foo`. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there's none present. A filter of the form `.foo.bar` is equivalent to `.foo|.bar`. This syntax only works for simple, identifier-like keys, that is, keys that are all made of alphanumeric characters and underscore, and which do not start with a digit. If the key contains special characters, you need to surround it with double quotes like this: `."foo$"`, or else `.["foo$"]`. For example `.["foo::bar"]` and `.["foo.bar"]` work while `.foo::bar` does not, and `.foo.bar` means `.["foo"].["bar"]`. examples: - program: '.foo' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]' input: '{"foo": 42}' output: [42] - title: "Optional Object Identifier-Index: `.foo?`" body: | Just like `.foo`, but does not output even an error when `.` is not an array or an object. examples: - program: '.foo?' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo?' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]?' input: '{"foo": 42}' output: [42] - program: '[.foo?]' input: '[1,2]' output: ['[]'] - title: "Generic Object Index: `.[]`" body: | You can also look up fields of an object using syntax like `.["foo"]` (.foo above is a shorthand version of this, but only for identifier-like strings). - title: "Array Index: `.[2]`" body: | When the index value is an integer, `.[]` can index arrays. Arrays are zero-based, so `.[2]` returns the third element. Negative indices are allowed, with -1 referring to the last element, -2 referring to the next to last element, and so on. examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['{"name":"JSON", "good":true}'] - program: '.[2]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] - program: '.[-2]' input: '[1,2,3]' output: ['2'] - title: "Array/String Slice: `.[10:15]`" body: | The `.[10:15]` syntax can be used to return a subarray of an array or substring of a string. The array returned by `.[10:15]` will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive). Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array). examples: - program: '.[2:4]' input: '["a","b","c","d","e"]' output: ['["c", "d"]'] - program: '.[2:4]' input: '"abcdefghi"' output: ['"cd"'] - program: '.[:3]' input: '["a","b","c","d","e"]' output: ['["a", "b", "c"]'] - program: '.[-2:]' input: '["a","b","c","d","e"]' output: ['["d", "e"]'] - title: "Array/Object Value Iterator: `.[]`" body: | If you use the `.[index]` syntax, but omit the index entirely, it will return *all* of the elements of an array. Running `.[]` with the input `[1,2,3]` will produce the numbers as three separate results, rather than as a single array. You can also use this on an object, and it will return all the values of the object. examples: - program: '.[]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: - '{"name":"JSON", "good":true}' - '{"name":"XML", "good":false}' - program: '.[]' input: '[]' output: [] - program: '.[]' input: '{"a": 1, "b": 1}' output: ['1', '1'] - title: "`.[]?`" body: | Like `.[]`, but no errors will be output if . is not an array or object. - title: "Comma: `,`" body: | If two filters are separated by a comma, then the same input will be fed into both and the two filters' output value streams will be concatenated in order: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right. For instance, filter `.foo, .bar`, produces both the "foo" fields and "bar" fields as separate outputs. examples: - program: '.foo, .bar' input: '{"foo": 42, "bar": "something else", "baz": true}' output: ['42', '"something else"'] - program: ".user, .projects[]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['"stedolan"', '"jq"', '"wikiflow"'] - program: '.[4,2]' input: '["a","b","c","d","e"]' output: ['"e"', '"c"'] - title: "Pipe: `|`" body: | The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right. It's pretty much the same as the Unix shell's pipe, if you're used to that. If the one on the left produces multiple results, the one on the right will be run for each of those results. So, the expression `.[] | .foo` retrieves the "foo" field of each element of the input array. Note that `.a.b.c` is the same as `.a | .b | .c`. Note too that `.` is the input value at the particular stage in a "pipeline", specifically: where the `.` expression appears. Thus `.a | . | .b` is the same as `.a.b`, as the `.` in the middle refers to whatever value `.a` produced. examples: - program: '.[] | .name' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['"JSON"', '"XML"'] - title: "Parenthesis" body: | Parenthesis work as a grouping operator just as in any typical programming language. examples: - program: '(. + 2) * 5' input: '1' output: [15] - title: Types and Values body: | jq supports the same set of datatypes as JSON - numbers, strings, booleans, arrays, objects (which in JSON-speak are hashes with only string keys), and "null". Booleans, null, strings and numbers are written the same way as in javascript. Just like everything else in jq, these simple values take an input and produce an output - `42` is a valid jq expression that takes an input, ignores it, and returns 42 instead. entries: - title: "Array construction: `[]`" body: | As in JSON, `[]` is used to construct arrays, as in `[1,2,3]`. The elements of the arrays can be any jq expression, including a pipeline. All of the results produced by all of the expressions are collected into one big array. You can use it to construct an array out of a known quantity of values (as in `[.foo, .bar, .baz]`) or to "collect" all the results of a filter into an array (as in `[.items[].name]`) Once you understand the "," operator, you can look at jq's array syntax in a different light: the expression `[1,2,3]` is not using a built-in syntax for comma-separated arrays, but is instead applying the `[]` operator (collect results) to the expression 1,2,3 (which produces three different results). If you have a filter `X` that produces four results, then the expression `[X]` will produce a single result, an array of four elements. examples: - program: "[.user, .projects[]]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['["stedolan", "jq", "wikiflow"]'] - program: "[ .[] | . * 2]" input: '[1, 2, 3]' output: ['[2, 4, 6]'] - title: "Object Construction: `{}`" body: | Like JSON, `{}` is for constructing objects (aka dictionaries or hashes), as in: `{"a": 42, "b": 17}`. If the keys are "identifier-like", then the quotes can be left off, as in `{a:42, b:17}`. Keys generated by expressions need to be parenthesized, e.g., `{("a"+"b"):59}`. The value can be any expression (although you may need to wrap it in parentheses if it's a complicated one), which gets applied to the {} expression's input (remember, all filters have an input and an output). {foo: .bar} will produce the JSON object `{"foo": 42}` if given the JSON object `{"bar":42, "baz":43}` as its input. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write {user: .user, title: .title} Because that is so common, there's a shortcut syntax for it: `{user, title}`. If one of the expressions produces multiple results, multiple dictionaries will be produced. If the input's {"user":"stedolan","titles":["JQ Primer", "More JQ"]} then the expression {user, title: .titles[]} will produce two outputs: {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} Putting parentheses around the key means it will be evaluated as an expression. With the same input as above, {(.user): .titles} produces {"stedolan": ["JQ Primer", "More JQ"]} examples: - program: '{user, title: .titles[]}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: - '{"user":"stedolan", "title": "JQ Primer"}' - '{"user":"stedolan", "title": "More JQ"}' - program: '{(.user): .titles}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: ['{"stedolan": ["JQ Primer", "More JQ"]}'] - title: "Recursive Descent: `..`" body: | Recursively descends `.`, producing every value. This is the same as the zero-argument `recurse` builtin (see below). This is intended to resemble the XPath `//` operator. Note that `..a` does not work; use `..|.a` instead. In the example below we use `..|.a?` to find all the values of object keys "a" in any object found "below" `.`. This is particularly useful in conjunction with `path(EXP)` (also see below) and the `?` operator. examples: - program: '..|.a?' input: '[[{"a":1}]]' output: ['1'] - title: Builtin operators and functions body: | Some jq operator (for instance, `+`) do different things depending on the type of their arguments (arrays, numbers, etc.). However, jq never does implicit type conversions. If you try to add a string to an object you'll get an error message and no result. entries: - title: "Addition: `+`" body: | The operator `+` takes two filters, applies them both to the same input, and adds the results together. What "adding" means depends on the types involved: - **Numbers** are added by normal arithmetic. - **Arrays** are added by being concatenated into a larger array. - **Strings** are added by being joined into a larger string. - **Objects** are added by merging, that is, inserting all the key-value pairs from both objects into a single combined object. If both objects contain a value for the same key, the object on the right of the `+` wins. (For recursive merge use the `*` operator.) `null` can be added to any value, and returns the other value unchanged. examples: - program: '.a + 1' input: '{"a": 7}' output: ['8'] - program: '.a + .b' input: '{"a": [1,2], "b": [3,4]}' output: ['[1,2,3,4]'] - program: '.a + null' input: '{"a": 1}' output: ['1'] - program: '.a + 1' input: '{}' output: ['1'] - program: '{a: 1} + {b: 2} + {c: 3} + {a: 42}' input: 'null' output: ['{"a": 42, "b": 2, "c": 3}'] - title: "Subtraction: `-`" body: | As well as normal arithmetic subtraction on numbers, the `-` operator can be used on arrays to remove all occurrences of the second array's elements from the first array. examples: - program: '4 - .a' input: '{"a":3}' output: ['1'] - program: . - ["xml", "yaml"] input: '["xml", "yaml", "json"]' output: ['["json"]'] - title: "Multiplication, division, modulo: `*`, `/`, and `%`" body: | These infix operators behave as expected when given two numbers. Division by zero raises an error. `x % y` computes x modulo y. Multiplying a string by a number produces the concatenation of that string that many times. `"x" * 0` produces **null**. Dividing a string by another splits the first using the second as separators. Multiplying two objects will merge them recursively: this works like addition but if both objects contain a value for the same key, and the values are objects, the two are merged with the same strategy. examples: - program: '10 / . * 3' input: 5 output: [6] - program: '. / ", "' input: '"a, b,c,d, e"' output: ['["a","b,c,d","e"]'] - program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}' input: 'null' output: ['{"k": {"a": 0, "b": 2, "c": 3}}'] - program: '.[] | (1 / .)?' input: '[1,0,-1]' output: ['1', '-1'] - title: "`length`" body: | The builtin function `length` gets the length of various different types of value: - The length of a **string** is the number of Unicode codepoints it contains (which will be the same as its JSON-encoded length in bytes if it's pure ASCII). - The length of an **array** is the number of elements. - The length of an **object** is the number of key-value pairs. - The length of **null** is zero. examples: - program: '.[] | length' input: '[[1,2], "string", {"a":2}, null]' output: [2, 6, 1, 0] - title: "`utf8bytelength`" body: | The builtin function `utf8bytelength` outputs the number of bytes used to encode a string in UTF-8. examples: - program: 'utf8bytelength' input: '"\u03bc"' output: [2] - title: "`keys`, `keys_unsorted`" body: | The builtin function `keys`, when given an object, returns its keys in an array. The keys are sorted "alphabetically", by unicode codepoint order. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings. When `keys` is given an array, it returns the valid indices for that array: the integers from 0 to length-1. The `keys_unsorted` function is just like `keys`, but if the input is an object then the keys will not be sorted, instead the keys will roughly be in insertion order. examples: - program: 'keys' input: '{"abc": 1, "abcd": 2, "Foo": 3}' output: ['["Foo", "abc", "abcd"]'] - program: 'keys' input: '[42,3,35]' output: ['[0,1,2]'] - title: "`has(key)`" body: | The builtin function `has` returns whether the input object has the given key, or the input array has an element at the given index. `has($key)` has the same effect as checking whether `$key` is a member of the array returned by `keys`, although `has` will be faster. examples: - program: 'map(has("foo"))' input: '[{"foo": 42}, {}]' output: ['[true, false]'] - program: 'map(has(2))' input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] - title: "`in`" body: | The builtin function `in` returns whether or not the input key is in the given object, or the input index corresponds to an element in the given array. It is, essentially, an inversed version of `has`. examples: - program: '.[] | in({"foo": 42})' input: '["foo", "bar"]' output: ['true', 'false'] - program: 'map(in([0,1]))' input: '[2, 0]' output: ['[false, true]'] - title: "`map(x)`, `map_values(x)`" body: | For any filter `x`, `map(x)` will run that filter for each element of the input array, and return the outputs in a new array. `map(.+1)` will increment each element of an array of numbers. Similarly, `map_values(x)` will run that filter for each element, but it will return an object when an object is passed. `map(x)` is equivalent to `[.[] | x]`. In fact, this is how it's defined. Similarly, `map_values(x)` is defined as `.[] |= x`. examples: - program: 'map(.+1)' input: '[1,2,3]' output: ['[2,3,4]'] - program: 'map_values(.+1)' input: '{"a": 1, "b": 2, "c": 3}' output: ['{"a": 2, "b": 3, "c": 4}'] - title: "`path(path_expression)`" body: | Outputs array representations of the given path expression in `.`. The outputs are arrays of strings (object keys) and/or numbers (array indices). Path expressions are jq expressions like `.a`, but also `.[]`. There are two types of path expressions: ones that can match exactly, and ones that cannot. For example, `.a.b.c` is an exact match path expression, while `.a[].b` is not. `path(exact_path_expression)` will produce the array representation of the path expression even if it does not exist in `.`, if `.` is `null` or an array or an object. `path(pattern)` will produce array representations of the paths matching `pattern` if the paths exist in `.`. Note that the path expressions are not different from normal expressions. The expression `path(..|select(type=="boolean"))` outputs all the paths to boolean values in `.`, and only those paths. examples: - program: 'path(.a[0].b)' input: 'null' output: ['["a",0,"b"]'] - program: '[path(..)]' input: '{"a":[{"b":1}]}' output: ['[[],["a"],["a",0],["a",0,"b"]]'] - title: "`del(path_expression)`" body: | The builtin function `del` removes a key and its corresponding value from an object. examples: - program: 'del(.foo)' input: '{"foo": 42, "bar": 9001, "baz": 42}' output: ['{"bar": 9001, "baz": 42}'] - program: 'del(.[1, 2])' input: '["foo", "bar", "baz"]' output: ['["foo"]'] - title: "`getpath(PATHS)`" body: | The builtin function `getpath` outputs the values in `.` found at each path in `PATHS`. examples: - program: 'getpath(["a","b"])' input: 'null' output: ['null'] - program: '[getpath(["a","b"], ["a","c"])]' input: '{"a":{"b":0, "c":1}}' output: ['[0, 1]'] - title: "`setpath(PATHS; VALUE)`" body: | The builtin function `setpath` sets the `PATHS` in `.` to `VALUE`. examples: - program: 'setpath(["a","b"]; 1)' input: 'null' output: ['{"a": {"b": 1}}'] - program: 'setpath(["a","b"]; 1)' input: '{"a":{"b":0}}' output: ['{"a": {"b": 1}}'] - program: 'setpath([0,"a"]; 1)' input: 'null' output: ['[{"a":1}]'] - title: "`delpaths(PATHS)`" body: | The builtin function `delpaths` sets the `PATHS` in `.`. `PATHS` must be an array of paths, where each path is an array of strings and numbers. examples: - program: 'delpaths([["a","b"]])' input: '{"a":{"b":1},"x":{"y":2}}' output: ['{"a":{},"x":{"y":2}}'] - title: "`to_entries`, `from_entries`, `with_entries`" body: | These functions convert between an object and an array of key-value pairs. If `to_entries` is passed an object, then for each `k: v` entry in the input, the output array includes `{"key": k, "value": v}`. `from_entries` does the opposite conversion, and `with_entries(foo)` is a shorthand for `to_entries | map(foo) | from_entries`, useful for doing some operation to all keys and values of an object. `from_entries` accepts key, Key, name, Name, value and Value as keys. examples: - program: 'to_entries' input: '{"a": 1, "b": 2}' output: ['[{"key":"a", "value":1}, {"key":"b", "value":2}]'] - program: 'from_entries' input: '[{"key":"a", "value":1}, {"key":"b", "value":2}]' output: ['{"a": 1, "b": 2}'] - program: 'with_entries(.key |= "KEY_" + .)' input: '{"a": 1, "b": 2}' output: ['{"KEY_a": 1, "KEY_b": 2}'] - title: "`select(boolean_expression)`" body: | The function `select(foo)` produces its input unchanged if `foo` returns true for that input, and produces no output otherwise. It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))` will give you `[2,3]`. examples: - program: 'map(select(. >= 2))' input: '[1,5,3,0,7]' output: ['[5,3,7]'] - program: '.[] | select(.id == "second")' input: '[{"id": "first", "val": 1}, {"id": "second", "val": 2}]' output: ['{"id": "second", "val": 2}'] - title: "`arrays`, `objects`, `iterables`, `booleans`, `numbers`, `normals`, `finites`, `strings`, `nulls`, `values`, `scalars`" body: | These built-ins select only inputs that are arrays, objects, iterables (arrays or objects), booleans, numbers, normal numbers, finite numbers, strings, null, non-null values, and non-iterables, respectively. examples: - program: '.[]|numbers' input: '[[],{},1,"foo",null,true,false]' output: ['1'] - title: "`empty`" body: | `empty` returns no results. None at all. Not even `null`. It's useful on occasion. You'll know if you need it :) examples: - program: '1, empty, 2' input: 'null' output: [1, 2] - program: '[1,2,empty,3]' input: 'null' output: ['[1,2,3]'] - title: "`error(message)`" body: | Produces an error, just like `.a` applied to values other than null and objects would, but with the given message as the error's value. Errors can be caught with try/catch; see below. - title: "`halt`" body: | Stops the jq program with no further outputs. jq will exit with exit status `0`. - title: "`halt_error`, `halt_error(exit_code)`" body: | Stops the jq program with no further outputs. The input will be printed on `stderr` as raw output (i.e., strings will not have double quotes) with no decoration, not even a newline. The given `exit_code` (defaulting to `5`) will be jq's exit status. For example, `"Error: somthing went wrong\n"|halt_error(1)`. - title: "`$__loc__`" body: | Produces an object with a "file" key and a "line" key, with the filename and line number where `$__loc__` occurs, as values. examples: - program: 'try error("\($__loc__)") catch .' input: 'null' output: ['"{\"file\":\"\",\"line\":1}"'] - title: "`paths`, `paths(node_filter)`, `leaf_paths`" body: | `paths` outputs the paths to all the elements in its input (except it does not output the empty list, representing . itself). `paths(f)` outputs the paths to any values for which `f` is true. That is, `paths(numbers)` outputs the paths to all numeric values. `leaf_paths` is an alias of `paths(scalars)`; `leaf_paths` is *deprecated* and will be removed in the next major release. examples: - program: '[paths]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1],[1,0],[1,1],[1,1,"a"]]'] - program: '[paths(scalars)]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1,1,"a"]]'] - title: "`add`" body: | The filter `add` takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the `+` operator (described above). If the input is an empty array, `add` returns `null`. examples: - program: add input: '["a","b","c"]' output: ['"abc"'] - program: add input: '[1, 2, 3]' output: [6] - program: add input: '[]' output: ["null"] - title: "`any`, `any(condition)`, `any(generator; condition)`" body: | The filter `any` takes as input an array of boolean values, and produces `true` as output if any of the elements of the array are `true`. If the input is an empty array, `any` returns `false`. The `any(condition)` form applies the given condition to the elements of the input array. The `any(generator; condition)` form applies the given condition to all the outputs of the given generator. examples: - program: any input: '[true, false]' output: ["true"] - program: any input: '[false, false]' output: ["false"] - program: any input: '[]' output: ["false"] - title: "`all`, `all(condition)`, `all(generator; condition)`" body: | The filter `all` takes as input an array of boolean values, and produces `true` as output if all of the elements of the array are `true`. The `all(condition)` form applies the given condition to the elements of the input array. The `all(generator; condition)` form applies the given condition to all the outputs of the given generator. If the input is an empty array, `all` returns `true`. examples: - program: all input: '[true, false]' output: ["false"] - program: all input: '[true, true]' output: ["true"] - program: all input: '[]' output: ["true"] - title: "`flatten`, `flatten(depth)`" body: | The filter `flatten` takes as input an array of nested arrays, and produces a flat array in which all arrays inside the original array have been recursively replaced by their values. You can pass an argument to it to specify how many levels of nesting to flatten. `flatten(2)` is like `flatten`, but going only up to two levels deep. examples: - program: flatten input: '[1, [2], [[3]]]' output: ["[1, 2, 3]"] - program: flatten(1) input: '[1, [2], [[3]]]' output: ["[1, 2, [3]]"] - program: flatten input: '[[]]' output: ["[]"] - program: flatten input: '[{"foo": "bar"}, [{"foo": "baz"}]]' output: ['[{"foo": "bar"}, {"foo": "baz"}]'] - title: "`range(upto)`, `range(from;upto)` `range(from;upto;by)`" body: | The `range` function produces a range of numbers. `range(4;10)` produces 6 numbers, from 4 (inclusive) to 10 (exclusive). The numbers are produced as separate outputs. Use `[range(4;10)]` to get a range as an array. The one argument form generates numbers from 0 to the given number, with an increment of 1. The two argument form generates numbers from `from` to `upto` with an increment of 1. The three argument form generates numbers `from` to `upto` with an increment of `by`. examples: - program: 'range(2;4)' input: 'null' output: ['2', '3'] - program: '[range(2;4)]' input: 'null' output: ['[2,3]'] - program: '[range(4)]' input: 'null' output: ['[0,1,2,3]'] - program: '[range(0;10;3)]' input: 'null' output: ['[0,3,6,9]'] - program: '[range(0;10;-1)]' input: 'null' output: ['[]'] - program: '[range(0;-5;-1)]' input: 'null' output: ['[0,-1,-2,-3,-4]'] - title: "`floor`" body: | The `floor` function returns the floor of its numeric input. examples: - program: 'floor' input: '3.14159' output: ['3'] - title: "`sqrt`" body: | The `sqrt` function returns the square root of its numeric input. examples: - program: 'sqrt' input: '9' output: ['3'] - title: "`tonumber`" body: | The `tonumber` function parses its input as a number. It will convert correctly-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input. examples: - program: '.[] | tonumber' input: '[1, "1"]' output: [1, 1] - title: "`tostring`" body: | The `tostring` function prints its input as a string. Strings are left unchanged, and all other values are JSON-encoded. examples: - program: '.[] | tostring' input: '[1, "1", [1]]' output: ['"1"', '"1"', '"[1]"'] - title: "`type`" body: | The `type` function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object. examples: - program: 'map(type)' input: '[0, false, [], {}, null, "hello"]' output: ['["number", "boolean", "array", "object", "null", "string"]'] - title: "`infinite`, `nan`, `isinfinite`, `isnan`, `isfinite`, `isnormal`" body: | Some arithmetic operations can yield infinities and "not a number" (NaN) values. The `isinfinite` builtin returns `true` if its input is infinite. The `isnan` builtin returns `true` if its input is a NaN. The `infinite` builtin returns a positive infinite value. The `nan` builtin returns a NaN. The `isnormal` builtin returns true if its input is a normal number. Note that division by zero raises an error. Currently most arithmetic operations operating on infinities, NaNs, and sub-normals do not raise errors. examples: - program: '.[] | (infinite * .) < 0' input: '[-1, 1]' output: ['true', 'false'] - program: 'infinite, nan | type' input: 'null' output: ['"number"', '"number"'] - title: "`sort, sort_by(path_expression)`" body: | The `sort` functions sorts its input, which must be an array. Values are sorted in the following order: * `null` * `false` * `true` * numbers * strings, in alphabetical order (by unicode codepoint value) * arrays, in lexical order * objects The ordering for objects is a little complex: first they're compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key. `sort` may be used to sort by a particular field of an object, or by applying any jq filter. `sort_by(foo)` compares two elements by comparing the result of `foo` on each element. examples: - program: 'sort' input: '[8,3,null,6]' output: ['[null,3,6,8]'] - program: 'sort_by(.foo)' input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]' output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]'] - title: "`group_by(path_expression)`" body: | `group_by(.foo)` takes as input an array, groups the elements having the same `.foo` field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the `.foo` field. Any jq expression, not just a field access, may be used in place of `.foo`. The sorting order is the same as described in the `sort` function above. examples: - program: 'group_by(.foo)' input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]'] - title: "`min`, `max`, `min_by(path_exp)`, `max_by(path_exp)`" body: | Find the minimum or maximum element of the input array. The `min_by(path_exp)` and `max_by(path_exp)` functions allow you to specify a particular field or property to examine, e.g. `min_by(.foo)` finds the object with the smallest `foo` field. examples: - program: 'min' input: '[5,4,2,7]' output: ['2'] - program: 'max_by(.foo)' input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' output: ['{"foo":2, "bar":3}'] - title: "`unique`, `unique_by(path_exp)`" body: | The `unique` function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed. The `unique_by(path_exp)` function will keep only one element for each value obtained by applying the argument. Think of it as making an array by taking one element out of every group produced by `group`. examples: - program: 'unique' input: '[1,2,5,3,5,3,1,3]' output: ['[1,2,3,5]'] - program: 'unique_by(.foo)' input: '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' output: ['[{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]'] - program: 'unique_by(length)' input: '["chunky", "bacon", "kitten", "cicada", "asparagus"]' output: ['["bacon", "chunky", "asparagus"]'] - title: "`reverse`" body: | This function reverses an array. examples: - program: 'reverse' input: '[1,2,3,4]' output: ['[4,3,2,1]'] - title: "`contains(element)`" body: | The filter `contains(b)` will produce true if b is completely contained within the input. A string B is contained in a string A if B is a substring of A. An array B is contained in an array A if all elements in B are contained in any element in A. An object B is contained in object A if all of the values in B are contained in the value in A with the same key. All other types are assumed to be contained in each other if they are equal. examples: - program: 'contains("bar")' input: '"foobar"' output: ['true'] - program: 'contains(["baz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['true'] - program: 'contains(["bazzzzz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['false'] - program: 'contains({foo: 12, bar: [{barp: 12}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['true'] - program: 'contains({foo: 12, bar: [{barp: 15}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['false'] - title: "`indices(s)`" body: | Outputs an array containing the indices in `.` where `s` occurs. The input may be an array, in which case if `s` is an array then the indices output will be those where all elements in `.` match those of `s`. examples: - program: 'indices(", ")' input: '"a,b, cd, efg, hijk"' output: ['[3,7,12]'] - program: 'indices(1)' input: '[0,1,2,1,3,1,4]' output: ['[1,3,5]'] - program: 'indices([1,2])' input: '[0,1,2,3,1,4,2,5,1,2,6,7]' output: ['[1,8]'] - title: "`index(s)`, `rindex(s)`" body: | Outputs the index of the first (`index`) or last (`rindex`) occurrence of `s` in the input. examples: - program: 'index(", ")' input: '"a,b, cd, efg, hijk"' output: ['3'] - program: 'rindex(", ")' input: '"a,b, cd, efg, hijk"' output: ['12'] - title: "`inside`" body: | The filter `inside(b)` will produce true if the input is completely contained within b. It is, essentially, an inversed version of `contains`. examples: - program: 'inside("foobar")' input: '"bar"' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["baz", "bar"]' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["bazzzzz", "bar"]' output: ['false'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 12}]}' output: ['true'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 15}]}' output: ['false'] - title: "`startswith(str)`" body: | Outputs `true` if . starts with the given string argument. examples: - program: '[.[]|startswith("foo")]' input: '["fo", "foo", "barfoo", "foobar", "barfoob"]' output: ['[false, true, false, true, false]'] - title: "`endswith(str)`" body: | Outputs `true` if . ends with the given string argument. examples: - program: '[.[]|endswith("foo")]' input: '["foobar", "barfoo"]' output: ['[false, true]'] - title: "`combinations`, `combinations(n)`" body: | Outputs all combinations of the elements of the arrays in the input array. If given an argument `n`, it outputs all combinations of `n` repetitions of the input array. examples: - program: 'combinations' input: '[[1,2], [3, 4]]' output: ['[1, 3]', '[1, 4]', '[2, 3]', '[2, 4]'] - program: 'combinations(2)' input: '[0, 1]' output: ['[0, 0]', '[0, 1]', '[1, 0]', '[1, 1]'] - title: "`ltrimstr(str)`" body: | Outputs its input with the given prefix string removed, if it starts with it. examples: - program: '[.[]|ltrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "afoo"]' output: ['["fo","","barfoo","bar","afoo"]'] - title: "`rtrimstr(str)`" body: | Outputs its input with the given suffix string removed, if it ends with it. examples: - program: '[.[]|rtrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "foob"]' output: ['["fo","","bar","foobar","foob"]'] - title: "`explode`" body: | Converts an input string into an array of the string's codepoint numbers. examples: - program: 'explode' input: '"foobar"' output: ['[102,111,111,98,97,114]'] - title: "`implode`" body: | The inverse of explode. examples: - program: 'implode' input: '[65, 66, 67]' output: ['"ABC"'] - title: "`split(str)`" body: | Splits an input string on the separator argument. examples: - program: 'split(", ")' input: '"a, b,c,d, e, "' output: ['["a","b,c,d","e",""]'] - title: "`join(str)`" body: | Joins the array of elements given as input, using the argument as separator. It is the inverse of `split`: that is, running `split("foo") | join("foo")` over any input string returns said input string. Numbers and booleans in the input are converted to strings. Null values are treated as empty strings. Arrays and objects in the input are not supported. examples: - program: 'join(", ")' input: '["a","b,c,d","e"]' output: ['"a, b,c,d, e"'] - program: 'join(" ")' input: '["a",1,2.3,true,null,false]' output: ['"a 1 2.3 true false"'] - title: "`ascii_downcase`, `ascii_upcase`" body: | Emit a copy of the input string with its alphabetic characters (a-z and A-Z) converted to the specified case. example: - program: 'ascii_upcase' input: '"useful but not for é"' output: '"USEFUL BUT NOT FOR é"' - title: "`while(cond; update)`" body: | The `while(cond; update)` function allows you to repeatedly apply an update to `.` until `cond` is false. Note that `while(cond; update)` is internally defined as a recursive jq function. Recursive calls within `while` will not consume additional memory if `update` produces at most one output for each input. See advanced topics below. examples: - program: '[while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: "`until(cond; next)`" body: | The `until(cond; next)` function allows you to repeatedly apply the expression `next`, initially to `.` then to its own output, until `cond` is true. For example, this can be used to implement a factorial function (see below). Note that `until(cond; next)` is internally defined as a recursive jq function. Recursive calls within `until()` will not consume additional memory if `next` produces at most one output for each input. See advanced topics below. examples: - program: '[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]' input: '4' output: ['24'] - title: "`recurse(f)`, `recurse`, `recurse(f; condition)`, `recurse_down`" body: | The `recurse(f)` function allows you to search through a recursive structure, and extract interesting data from all levels. Suppose your input represents a filesystem: {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} Now suppose you want to extract all of the filenames present. You need to retrieve `.name`, `.children[].name`, `.children[].children[].name`, and so on. You can do this with: recurse(.children[]) | .name When called without an argument, `recurse` is equivalent to `recurse(.[]?)`. `recurse(f)` is identical to `recurse(f; . != null)` and can be used without concerns about recursion depth. `recurse(f; condition)` is a generator which begins by emitting . and then emits in turn .|f, .|f|f, .|f|f|f, ... so long as the computed value satisfies the condition. For example, to generate all the integers, at least in principle, one could write `recurse(.+1; true)`. For legacy reasons, `recurse_down` exists as an alias to calling `recurse` without arguments. This alias is considered *deprecated* and will be removed in the next major release. The recursive calls in `recurse` will not consume additional memory whenever `f` produces at most a single output for each input. examples: - program: 'recurse(.foo[])' input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}' output: - '{"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}' - '{"foo":[]}' - '{"foo":[{"foo":[]}]}' - '{"foo":[]}' - program: 'recurse' input: '{"a":0,"b":[1]}' output: - '{"a":0,"b":[1]}' - '0' - '[1]' - '1' - program: 'recurse(. * .; . < 20)' input: 2 output: - 2 - 4 - 16 - title: "`walk(f)`" body: | The `walk(f)` function applies f recursively to every component of the input entity. When an array is encountered, f is first applied to its elements and then to the array itself; when an object is encountered, f is first applied to all the values and then to the object. In practice, f will usually test the type of its input, as illustrated in the following examples. The first example highlights the usefulness of processing the elements of an array of arrays before processing the array itself. The second example shows how all the keys of all the objects within the input can be considered for alteration. examples: - program: 'walk(if type == "array" then sort else . end)' input: '[[4, 1, 7], [8, 5, 2], [3, 6, 9]]' output: - '[[1,4,7],[2,5,8],[3,6,9]]' - program: 'walk( if type == "object" then with_entries( .key |= sub( "^_+"; "") ) else . end )' input: '[ { "_a": { "__b": 2 } } ]' output: - '[{"a":{"b":2}}]' - title: "`$ENV`, `env`" body: | `$ENV` is an object representing the environment variables as set when the jq program started. `env` outputs an object representing jq's current environment. At the moment there is no builtin for setting environment variables. examples: - program: '$ENV.PAGER' input: 'null' output: ['"less"'] - program: 'env.PAGER' input: 'null' output: ['"less"'] - title: "`transpose`" body: | Transpose a possibly jagged matrix (an array of arrays). Rows are padded with nulls so the result is always rectangular. examples: - program: 'transpose' input: '[[1], [2,3]]' output: ['[[1,2],[null,3]]'] - title: "`bsearch(x)`" body: | bsearch(x) conducts a binary search for x in the input array. If the input is sorted and contains x, then bsearch(x) will return its index in the array; otherwise, if the array is sorted, it will return (-1 - ix) where ix is an insertion point such that the array would still be sorted after the insertion of x at ix. If the array is not sorted, bsearch(x) will return an integer that is probably of no interest. examples: - program: 'bsearch(0)' input: '[0,1]' output: ['0'] - program: 'bsearch(0)' input: '[1,2,3]' output: ['-1'] - program: 'bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end' input: '[1,2,3]' output: ['[1,2,3,4]'] - title: "String interpolation - `\\(foo)`" body: | Inside a string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string. examples: - program: '"The input was \(.), which is one less than \(.+1)"' input: '42' output: ['"The input was 42, which is one less than 43"'] - title: "Convert to/from JSON" body: | The `tojson` and `fromjson` builtins dump values as JSON texts or parse JSON texts into values, respectively. The tojson builtin differs from tostring in that tostring returns strings unmodified, while tojson encodes strings as JSON strings. examples: - program: '[.[]|tostring]' input: '[1, "foo", ["foo"]]' output: ['["1","foo","[\"foo\"]"]'] - program: '[.[]|tojson]' input: '[1, "foo", ["foo"]]' output: ['["1","\"foo\"","[\"foo\"]"]'] - program: '[.[]|tojson|fromjson]' input: '[1, "foo", ["foo"]]' output: ['[1,"foo",["foo"]]'] - title: "Format strings and escaping" body: | The `@foo` syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth. `@foo` can be used as a filter on its own, the possible escapings are: * `@text`: Calls `tostring`, see that function for details. * `@json`: Serializes the input as JSON. * `@html`: Applies HTML/XML escaping, by mapping the characters `<>&'"` to their entity equivalents `<`, `>`, `&`, `'`, `"`. * `@uri`: Applies percent-encoding, by mapping all reserved URI characters to a `%XX` sequence. * `@csv`: The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition. * `@tsv`: The input must be an array, and it is rendered as TSV (tab-separated values). Each input array will be printed as a single line. Fields are separated by a single tab (ascii `0x09`). Input characters line-feed (ascii `0x0a`), carriage-return (ascii `0x0d`), tab (ascii `0x09`) and backslash (ascii `0x5c`) will be output as escape sequences `\n`, `\r`, `\t`, `\\` respectively. * `@sh`: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings. * `@base64`: The input is converted to base64 as specified by RFC 4648. * `@base64d`: The inverse of `@base64`, input is decoded as specified by RFC 4648. Note\: If the decoded string is not UTF-8, the results are undefined. This syntax can be combined with string interpolation in a useful way. You can follow a `@foo` token with a string literal. The contents of the string literal will *not* be escaped. However, all interpolations made inside that string literal will be escaped. For instance, @uri "https://www.google.com/search?q=\(.search)" will produce the following output for the input `{"search":"what is jq?"}`: "https://www.google.com/search?q=what%20is%20jq%3F" Note that the slashes, question mark, etc. in the URL are not escaped, as they were part of the string literal. examples: - program: '@html' input: '"This works if x < y"' output: ['"This works if x < y"'] # - program: '@html "Anonymous said: \(.)"' # input: '""' # output: ["Anonymous said: <script>alert("lol hax");</script>"] - program: '@sh "echo \(.)"' input: "\"O'Hara's Ale\"" output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""] - program: '@base64' input: '"This is a message"' output: ['"VGhpcyBpcyBhIG1lc3NhZ2U="'] - program: '@base64d' input: '"VGhpcyBpcyBhIG1lc3NhZ2U="' output: ['"This is a message"'] - title: "Dates" body: | jq provides some basic date handling functionality, with some high-level and low-level builtins. In all cases these builtins deal exclusively with time in UTC. The `fromdateiso8601` builtin parses datetimes in the ISO 8601 format to a number of seconds since the Unix epoch (1970-01-01T00:00:00Z). The `todateiso8601` builtin does the inverse. The `fromdate` builtin parses datetime strings. Currently `fromdate` only supports ISO 8601 datetime strings, but in the future it will attempt to parse datetime strings in more formats. The `todate` builtin is an alias for `todateiso8601`. The `now` builtin outputs the current time, in seconds since the Unix epoch. Low-level jq interfaces to the C-library time functions are also provided: `strptime`, `strftime`, `strflocaltime`, `mktime`, `gmtime`, and `localtime`. Refer to your host operating system's documentation for the format strings used by `strptime` and `strftime`. Note: these are not necessarily stable interfaces in jq, particularly as to their localization functionality. The `gmtime` builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of Greenwhich Meridian time as an array of numbers representing (in this order): the year, the month (zero-based), the day of the month (one-based), the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year -- all one-based unless otherwise stated. The day of the week number may be wrong on some systems for dates before March 1st 1900, or after December 31 2099. The `localtime` builtin works like the `gmtime` builtin, but using the local timezone setting. The `mktime` builtin consumes "broken down time" representations of time output by `gmtime` and `strptime`. The `strptime(fmt)` builtin parses input strings matching the `fmt` argument. The output is in the "broken down time" representation consumed by `gmtime` and output by `mktime`. The `strftime(fmt)` builtin formats a time (GMT) with the given format. The `strflocaltime` does the same, but using the local timezone setting. The format strings for `strptime` and `strftime` are described in typical C library documentation. The format string for ISO 8601 datetime is `"%Y-%m-%dT%H:%M:%SZ"`. jq may not support some or all of this date functionality on some systems. In particular, the `%u` and `%j` specifiers for `strptime(fmt)` are not supported on macOS. examples: - program: 'fromdate' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")' input: '"2015-03-05T23:51:47Z"' output: ['[2015,2,5,23,51,47,4,63]'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")|mktime' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - title: "SQL-Style Operators" body: | jq provides a few SQL-style operators. * INDEX(stream; index_expression): This builtin produces an object whose keys are computed by the given index expression applied to each value from the given stream. * JOIN($idx; stream; idx_expr; join_expr): This builtin joins the values from the given stream to the given index. The index's keys are computed by applying the given index expression to each value from the given stream. An array of the value in the stream and the corresponding value from the index is fed to the given join expression to produce each result. * JOIN($idx; stream; idx_expr): Same as `JOIN($idx; stream; idx_expr; .)`. * JOIN($idx; idx_expr): This builtin joins the input `.` to the given index, applying the given index expression to `.` to compute the index key. The join operation is as described above. * IN(s): This builtin outputs `true` if `.` appears in the given stream, otherwise it outputs `false`. * IN(source; s): This builtin outputs `true` if any value in the source stream appears in the second stream, otherwise it outputs `false`. - title: "`builtins`" body: | Returns a list of all builtin functions in the format `name/arity`. Since functions with the same name but different arities are considered separate functions, `all/0`, `all/1`, and `all/2` would all be present in the list. - title: Conditionals and Comparisons entries: - title: "`==`, `!=`" body: | The expression 'a == b' will produce 'true' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and 'false' otherwise. In particular, strings are never considered equal to numbers. If you're coming from Javascript, jq's == is like Javascript's === - considering values equal only when they have the same type as well as the same value. != is "not equal", and 'a != b' returns the opposite value of 'a == b' examples: - program: '.[] == 1' input: '[1, 1.0, "1", "banana"]' output: ['true', 'true', 'false', 'false'] - title: if-then-else body: | `if A then B else C end` will act the same as `B` if `A` produces a value other than false or null, but act the same as `C` otherwise. Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you'll sometimes have to be more explicit about the condition you want: you can't test whether, e.g. a string is empty using `if .name then A else B end`, you'll need something more like `if (.name | length) > 0 then A else B end` instead. If the condition `A` produces multiple results, then `B` is evaluated once for each result that is not false or null, and `C` is evaluated once for each false or null. More cases can be added to an if using `elif A then B` syntax. examples: - program: |- if . == 0 then "zero" elif . == 1 then "one" else "many" end input: 2 output: ['"many"'] - title: "`>, >=, <=, <`" body: | The comparison operators `>`, `>=`, `<=`, `<` return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively). The ordering is the same as that described for `sort`, above. examples: - program: '. < 5' input: 2 output: ['true'] - title: and/or/not body: | jq supports the normal Boolean operators and/or/not. They have the same standard of truth as if expressions - false and null are considered "false values", and anything else is a "true value". If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input. `not` is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in `.foo and .bar | not`. These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default". If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below. examples: - program: '42 and "a string"' input: 'null' output: ['true'] - program: '(true, false) or false' input: 'null' output: ['true', 'false'] # - program: '(true, false) and (true, false)' # input: 'null' # output: ['true', 'false', 'false', 'false'] - program: '(true, true) and (true, false)' input: 'null' output: ['true', 'false', 'true', 'false'] - program: '[true, false | not]' input: 'null' output: ['[false, true]'] - title: "Alternative operator: `//`" body: | A filter of the form `a // b` produces the same results as `a`, if `a` produces results other than `false` and `null`. Otherwise, `a // b` produces the same results as `b`. This is useful for providing defaults: `.foo // 1` will evaluate to `1` if there's no `.foo` element in the input. It's similar to how `or` is sometimes used in Python (jq's `or` operator is reserved for strictly Boolean operations). examples: - program: '.foo // 42' input: '{"foo": 19}' output: [19] - program: '.foo // 42' input: '{}' output: [42] - title: try-catch body: | Errors can be caught by using `try EXP catch EXP`. The first expression is executed, and if it fails then the second is executed with the error message. The output of the handler, if any, is output as if it had been the output of the expression to try. The `try EXP` form uses `empty` as the exception handler. examples: - program: 'try .a catch ". is not an object"' input: 'true' output: ['". is not an object"'] - program: '[.[]|try .a]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - program: 'try error("some exception") catch .' input: 'true' output: ['"some exception"'] - title: Breaking out of control structures body: | A convenient use of try/catch is to break out of control structures like `reduce`, `foreach`, `while`, and so on. For example: # Repeat an expression until it raises "break" as an # error, then stop repeating without re-raising the error. # But if the error caught is not "break" then re-raise it. try repeat(exp) catch .=="break" then empty else error; jq has a syntax for named lexical labels to "break" or "go (back) to": label $out | ... break $out ... The `break $label_name` expression will cause the program to to act as though the nearest (to the left) `label $label_name` produced `empty`. The relationship between the `break` and corresponding `label` is lexical: the label has to be "visible" from the break. To break out of a `reduce`, for example: label $out | reduce .[] as $item (null; if .==false then break $out else ... end) The following jq program produces a syntax error: break $out because no label `$out` is visible. - title: "Error Suppression / Optional Operator: `?`" body: | The `?` operator, used as `EXP?`, is shorthand for `try EXP`. examples: - program: '[.[]|(.a)?]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - title: Regular expressions (PCRE) body: | jq uses the Oniguruma regular expression library, as do php, ruby, TextMate, Sublime Text, etc, so the description here will focus on jq specifics. The jq regex filters are defined so that they can be used using one of these patterns: STRING | FILTER( REGEX ) STRING | FILTER( REGEX; FLAGS ) STRING | FILTER( [REGEX] ) STRING | FILTER( [REGEX, FLAGS] ) where: * STRING, REGEX and FLAGS are jq strings and subject to jq string interpolation; * REGEX, after string interpolation, should be a valid PCRE regex; * FILTER is one of `test`, `match`, or `capture`, as described below. FLAGS is a string consisting of one of more of the supported flags: * `g` - Global search (find all matches, not just the first) * `i` - Case insensitive search * `m` - Multi line mode ('.' will match newlines) * `n` - Ignore empty matches * `p` - Both s and m modes are enabled * `s` - Single line mode ('^' -> '\A', '$' -> '\Z') * `l` - Find longest possible matches * `x` - Extended regex format (ignore whitespace and comments) To match whitespace in an x pattern use an escape such as \s, e.g. * test( "a\\sb", "x" ). Note that certain flags may also be specified within REGEX, e.g. * jq -n '("test", "TEst", "teST", "TEST") | test( "(?i)te(?-i)st" )' evaluates to: true, true, false, false. entries: - title: "`test(val)`, `test(regex; flags)`" body: | Like `match`, but does not return match objects, only `true` or `false` for whether or not the regex matches the input. examples: - program: 'test("foo")' input: '"foo"' output: ['true'] - program: '.[] | test("a b c # spaces are ignored"; "ix")' input: '["xabcd", "ABC"]' output: ['true', 'true'] - title: "`match(val)`, `match(regex; flags)`" body: | **match** outputs an object for each match it finds. Matches have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of the match * `string` - the string that it matched * `captures` - an array of objects representing capturing groups. Capturing group objects have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of this capturing group * `string` - the string that was captured * `name` - the name of the capturing group (or `null` if it was unnamed) Capturing groups that did not match anything return an offset of -1 examples: - program: 'match("(abc)+"; "g")' input: '"abc abc"' output: - '{"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}' - '{"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]}' - program: 'match("foo")' input: '"foo bar foo"' output: ['{"offset": 0, "length": 3, "string": "foo", "captures": []}'] - program: 'match(["foo", "ig"])' input: '"foo bar FOO"' output: - '{"offset": 0, "length": 3, "string": "foo", "captures": []}' - '{"offset": 8, "length": 3, "string": "FOO", "captures": []}' - program: 'match("foo (?bar)? foo"; "ig")' input: '"foo bar foo foo foo"' output: - '{"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}' - '{"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}' - program: '[ match("."; "g")] | length' input: '"abc"' output: [3] - title: "`capture(val)`, `capture(regex; flags)`" body: | Collects the named captures in a JSON object, with the name of each capture as the key, and the matched string as the corresponding value. examples: - program: 'capture("(?[a-z]+)-(?[0-9]+)")' input: '"xyzzy-14"' output: ['{ "a": "xyzzy", "n": "14" }'] - title: "`scan(regex)`, `scan(regex; flags)`" body: | Emit a stream of the non-overlapping substrings of the input that match the regex in accordance with the flags, if any have been specified. If there is no match, the stream is empty. To capture all the matches for each input string, use the idiom `[ expr ]`, e.g. `[ scan(regex) ]`. example: - program: 'scan("c")' input: '"abcdefabc"' output: ['"c"', '"c"'] - program: 'scan("b")' input: ("", "") output: ['[]', '[]'] - title: "`split(regex; flags)`" body: | For backwards compatibility, `split` splits on a string, not a regex. example: - program: 'split(", *"; null)' input: '"ab,cd, ef"' output: ['"ab","cd","ef"'] - title: "`splits(regex)`, `splits(regex; flags)`" body: | These provide the same results as their `split` counterparts, but as a stream instead of an array. example: - program: 'splits(", *")' input: '("ab,cd", "ef, gh")' output: ['"ab"', '"cd"', '"ef"', '"gh"'] - title: "`sub(regex; tostring)` `sub(regex; string; flags)`" body: | Emit the string obtained by replacing the first match of regex in the input string with `tostring`, after interpolation. `tostring` should be a jq string, and may contain references to named captures. The named captures are, in effect, presented as a JSON object (as constructed by `capture`) to `tostring`, so a reference to a captured variable named "x" would take the form: "\(.x)". example: - program: 'sub("^[^a-z]*(?[a-z]*).*")' input: '"123abc456"' output: '"ZabcZabc"' - title: "`gsub(regex; string)`, `gsub(regex; string; flags)`" body: | `gsub` is like `sub` but all the non-overlapping occurrences of the regex are replaced by the string, after interpolation. example: - program: 'gsub("(?.)[^a]*"; "+\(.x)-")' input: '"Abcabc"' output: '"+A-+a-"' - title: Advanced features body: | Variables are an absolute necessity in most programming languages, but they're relegated to an "advanced feature" in jq. In most languages, variables are the only means of passing around data. If you calculate a value, and you want to use it more than once, you'll need to store it in a variable. To pass a value to another part of the program, you'll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data. It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq's standard library (many jq functions such as `map` and `find` are in fact written in jq). jq has reduction operators, which are very powerful but a bit tricky. Again, these are mostly used internally, to define some useful bits of jq's standard library. It may not be obvious at first, but jq is all about generators (yes, as often found in other languages). Some utilities are provided to help deal with generators. Some minimal I/O support (besides reading JSON from standard input, and writing JSON to standard output) is available. Finally, there is a module/library system. entries: - title: "Variable / Symbolic Binding Operator: `... as $identifier | ...`" body: | In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next. Many expressions, for instance `a + b`, pass their input to two distinct subexpressions (here `a` and `b` are both passed the same input), so variables aren't usually necessary in order to use a value twice. For instance, calculating the average value of an array of numbers requires a few variables in most languages - at least one to hold the array, perhaps one for each element or for a loop counter. In jq, it's simply `add / length` - the `add` expression is given the array and produces its sum, and the `length` expression is given the array and produces its length. So, there's generally a cleaner way to solve most problems in jq than defining variables. Still, sometimes they do make things easier, so jq lets you define variables using `expression as $variable`. All variable names start with `$`. Here's a slightly uglier version of the array-averaging example: length as $array_length | add / $array_length We'll need a more complicated problem to find a situation where using variables actually makes our lives easier. Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names. Our input looks like: {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} We want to produce the posts with the author field containing a real name, as in: {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"} We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: .realnames as $names | .posts[] | {title, author: $names[.author]} The expression `exp as $x | ...` means: for each value of expression `exp`, run the rest of the pipeline with the entire original input, and with `$x` set to that value. Thus `as` functions as something of a foreach loop. Just as `{foo}` is a handy way of writing `{foo: .foo}`, so `{$foo}` is a handy way of writing `{foo:$foo}`. Multiple variables may be declared using a single `as` expression by providing a pattern that matches the structure of the input (this is known as "destructuring"): . as {realnames: $names, posts: [$first, $second]} | ... The variable declarations in array patterns (e.g., `. as [$first, $second]`) bind to the elements of the array in from the element at index zero on up, in order. When there is no value at the index for an array pattern element, `null` is bound to that variable. Variables are scoped over the rest of the expression that defines them, so .realnames as $names | (.posts[] | {title, author: $names[.author]}) will work, but (.realnames as $names | .posts[]) | {title, author: $names[.author]} won't. For programming language theorists, it's more accurate to say that jq variables are lexically-scoped bindings. In particular there's no way to change the value of a binding; one can only setup a new binding with the same name, but which will not be visible where the old one was. examples: - program: '.bar as $x | .foo | . + $x' input: '{"foo":10, "bar":200}' output: ['210'] - program: '. as $i|[(.*2|. as $i| $i), $i]' input: '5' output: ['[10,5]'] - program: '. as [$a, $b, {c: $c}] | $a + $b + $c' input: '[2, 3, {"c": 4, "d": 5}]' output: ['9'] - program: '.[] as [$a, $b] | {a: $a, b: $b}' input: '[[0], [0, 1], [2, 1, 0]]' output: ['{"a":0,"b":null}', '{"a":0,"b":1}', '{"a":2,"b":1}'] - title: 'Destructuring Alternative Operator: `?//`' body: | The destructuring alternative operator provides a concise mechanism for destructuring an input that can take one of several forms. Suppose we have an API that returns a list of resources and events associated with them, and we want to get the user_id and timestamp of the first event for each resource. The API (having been clumsily converted from XML) will only wrap the events in an array if the resource has multiple events: {"resources": [{"id": 1, "kind": "widget", "events": {"action": "create", "user_id": 1, "ts": 13}}, {"id": 2, "kind": "widget", "events": [{"action": "create", "user_id": 1, "ts": 14}, {"action": "destroy", "user_id": 1, "ts": 15}]}]} We can use the destructuring alternative operator to handle this structural change simply: .resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$user_id, $ts}]} | {$user_id, $kind, $id, $ts} Or, if we aren't sure if the input is an array of values or an object: .[] as [$id, $kind, $user_id, $ts] ?// {$id, $kind, $user_id, $ts} | ... Each alternative need not define all of the same variables, but all named variables will be available to the subsequent expression. Variables not matched in the alternative that succeeded will be `null`: .resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$first_user_id, $first_ts}]} | {$user_id, $first_user_id, $kind, $id, $ts, $first_ts} Additionally, if the subsequent expression returns an error, the alternative operator will attempt to try the next binding. Errors that occur during the final alternative are passed through. [[3]] | .[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end examples: - program: '.[] as {$a, $b, c: {$d, $e}} ?// {$a, $b, c: [{$d, $e}]} | {$a, $b, $d, $e}' input: '[{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]' output: ['{"a":1,"b":2,"d":3,"e":4}', '{"a":1,"b":2,"d":3,"e":4}'] - program: '.[] as {$a, $b, c: {$d}} ?// {$a, $b, c: [{$e}]} | {$a, $b, $d, $e}' input: '[{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]' output: ['{"a":1,"b":2,"d":3,"e":null}', '{"a":1,"b":2,"d":null,"e":4}'] - program: '.[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end' input: '[[3]]' output: ['{"a":null,"b":3}'] - title: 'Defining Functions' body: | You can give a filter a name using "def" syntax: def increment: . + 1; From then on, `increment` is usable as a filter just like a builtin function (in fact, this is how many of the builtins are defined). A function may take arguments: def map(f): [.[] | f]; Arguments are passed as _filters_ (functions with no arguments), _not_ as values. The same argument may be referenced multiple times with different inputs (here `f` is run for each element of the input array). Arguments to a function work more like callbacks than like value arguments. This is important to understand. Consider: def foo(f): f|f; 5|foo(.*2) The result will be 20 because `f` is `.*2`, and during the first invocation of `f` `.` will be 5, and the second time it will be 10 (5 * 2), so the result will be 20. Function arguments are filters, and filters expect an input when invoked. If you want the value-argument behaviour for defining simple functions, you can just use a variable: def addvalue(f): f as $f | map(. + $f); Or use the short-hand: def addvalue($f): ...; With either definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. Do note that calling `addvalue(.[])` will cause the `map(. + $f)` part to be evaluated once per value in the value of `.` at the call site. Multiple definitions using the same function name are allowed. Each re-definition replaces the previous one for the same number of function arguments, but only for references from functions (or main program) subsequent to the re-definition. See also the section below on scoping. examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' output: ['[[1,2,1], [10,20,10]]'] - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' input: '[[1,2],[10,20]]' output: ['[[1,2,1,2], [10,20,1,2]]'] - title: 'Scoping' body: | There are two types of symbols in jq: value bindings (a.k.a., "variables"), and functions. Both are scoped lexically, with expressions being able to refer only to symbols that have been defined "to the left" of them. The only exception to this rule is that functions can refer to themselves so as to be able to create recursive functions. For example, in the following expression there is a binding which is visible "to the right" of it, `... | .*3 as $times_three | [. + $times_three] | ...`, but not "to the left". Consider this expression now, `... | (.*3 as $times_three | [.+ $times_three]) | ...`: here the binding `$times_three` is _not_ visible past the closing parenthesis. - title: Reduce body: | The `reduce` syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer. As an example, we'll pass `[3,2,1]` to this expression: reduce .[] as $item (0; . + $item) For each result that `.[]` produces, `. + $item` is run to accumulate a running total, starting from 0. In this example, `.[]` produces the results 3, 2, and 1, so the effect is similar to running something like this: 0 | (3 as $item | . + $item) | (2 as $item | . + $item) | (1 as $item | . + $item) examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - title: "`isempty(exp)`" body: | Returns true if `exp` produces no outputs, false otherwise. examples: - program: 'isempty(empty)' input: 'null' output: ['true'] - title: "`limit(n; exp)`" body: | The `limit` function extracts up to `n` outputs from `exp`. examples: - program: '[limit(3;.[])]' input: '[0,1,2,3,4,5,6,7,8,9]' output: ['[0,1,2]'] - title: "`first(expr)`, `last(expr)`, `nth(n; expr)`" body: | The `first(expr)` and `last(expr)` functions extract the first and last values from `expr`, respectively. The `nth(n; expr)` function extracts the nth value output by `expr`. This can be defined as `def nth(n; expr): last(limit(n + 1; expr));`. Note that `nth(n; expr)` doesn't support negative values of `n`. examples: - program: '[first(range(.)), last(range(.)), nth(./2; range(.))]' input: '10' output: ['[0,9,5]'] - title: "`first`, `last`, `nth(n)`" body: | The `first` and `last` functions extract the first and last values from any array at `.`. The `nth(n)` function extracts the nth value of any array at `.`. examples: - program: '[range(.)]|[first, last, nth(5)]' input: '10' output: ['[0,9,5]'] - title: "`foreach`" body: | The `foreach` syntax is similar to `reduce`, but intended to allow the construction of `limit` and reducers that produce intermediate results (see example). The form is `foreach EXP as $var (INIT; UPDATE; EXTRACT)`. Like `reduce`, `INIT` is evaluated once to produce a state value, then each output of `EXP` is bound to `$var`, `UPDATE` is evaluated for each output of `EXP` with the current state and with `$var` visible. Each value output by `UPDATE` replaces the previous state. Finally, `EXTRACT` is evaluated for each new state to extract an output of `foreach`. This is mostly useful only for constructing `reduce`- and `limit`-like functions. But it is much more general, as it allows for partial reductions (see the example below). examples: - program: '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]] else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]' input: '[1,2,3,4,null,"a","b",null]' output: ['[[1,2,3,4],["a","b"]]'] - title: Recursion body: | As described above, `recurse` uses recursion, and any jq function can be recursive. The `while` builtin is also implemented in terms of recursion. Tail calls are optimized whenever the expression to the left of the recursive call outputs its last value. In practice this means that the expression to the left of the recursive call should not produce more than one output for each input. For example: def recurse(f): def r: ., (f | select(. != null) | r); r; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def repeat(exp): def _repeat: exp, _repeat; _repeat; - title: Generators and iterators body: | Some jq operators and functions are actually generators in that they can produce zero, one, or more values for each input, just as one might expect in other programming languages that have generators. For example, `.[]` generates all the values in its input (which must be an array or an object), `range(0; 10)` generates the integers between 0 and 10, and so on. Even the comma operator is a generator, generating first the values generated by the expression to the left of the comma, then for each of those, the values generate by the expression on the right of the comma. The `empty` builtin is the generator that produces zero outputs. The `empty` builtin backtracks to the preceding generator expression. All jq functions can be generators just by using builtin generators. It is also possible to define new generators using only recursion and the comma operator. If the recursive call(s) is(are) "in tail position" then the generator will be efficient. In the example below the recursive call by `_range` to itself is in tail position. The example shows off three advanced topics: tail recursion, generator construction, and sub-functions. examples: - program: '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)); range(0; 10; 3)' input: 'null' output: ['0', '3', '6', '9'] - program: 'def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; [while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: 'Math' body: | jq currently only has IEEE754 double-precision (64-bit) floating point number support. Besides simple arithmetic operators such as `+`, jq also has most standard math functions from the C math library. C math functions that take a single input argument (e.g., `sin()`) are available as zero-argument jq functions. C math functions that take two input arguments (e.g., `pow()`) are available as two-argument jq functions that ignore `.`. C math functions that take three input arguments are available as three-argument jq functions that ignore `.`. Availability of standard math functions depends on the availability of the corresponding math functions in your operating system and C math library. Unavailable math functions will be defined but will raise an error. One-input C math functions: `acos` `acosh` `asin` `asinh` `atan` `atanh` `cbrt` `ceil` `cos` `cosh` `erf` `erfc` `exp` `exp10` `exp2` `expm1` `fabs` `floor` `gamma` `j0` `j1` `lgamma` `log` `log10` `log1p` `log2` `logb` `nearbyint` `pow10` `rint` `round` `significand` `sin` `sinh` `sqrt` `tan` `tanh` `tgamma` `trunc` `y0` `y1`. Two-input C math functions: `atan2` `copysign` `drem` `fdim` `fmax` `fmin` `fmod` `frexp` `hypot` `jn` `ldexp` `modf` `nextafter` `nexttoward` `pow` `remainder` `scalb` `scalbln` `yn`. Three-input C math functions: `fma`. See your system's manual for more information on each of these. - title: 'I/O' body: | At this time jq has minimal support for I/O, mostly in the form of control over when inputs are read. Two builtins functions are provided for this, `input` and `inputs`, that read from the same sources (e.g., `stdin`, files named on the command-line) as jq itself. These two builtins, and jq's own reading actions, can be interleaved with each other. Two builtins provide minimal output capabilities, `debug`, and `stderr`. (Recall that a jq program's output values are always output as JSON texts on `stdout`.) The `debug` builtin can have application-specific behavior, such as for executables that use the libjq C API but aren't the jq executable itself. The `stderr` builtin outputs its input in raw mode to stder with no additional decoration, not even a newline. Most jq builtins are referentially transparent, and yield constant and repeatable value streams when applied to constant inputs. This is not true of I/O builtins. entries: - title: "`input`" body: | Outputs one new input. - title: "`inputs`" body: | Outputs all remaining inputs, one by one. This is primarily useful for reductions over a program's inputs. - title: "`debug`" body: | Causes a debug message based on the input value to be produced. The jq executable wraps the input value with `["DEBUG:", ]` and prints that and a newline on stderr, compactly. This may change in the future. - title: "`stderr`" body: | Prints its input in raw and compact mode to stderr with no additional decoration, not even a newline. - title: "`input_filename`" body: | Returns the name of the file whose input is currently being filtered. Note that this will not work well unless jq is running in a UTF-8 locale. - title: "`input_line_number`" body: | Returns the line number of the input currently being filtered. - title: 'Streaming' body: | With the `--stream` option jq can parse input texts in a streaming fashion, allowing jq programs to start processing large JSON texts immediately rather than after the parse completes. If you have a single JSON text that is 1GB in size, streaming it will allow you to process it much more quickly. However, streaming isn't easy to deal with as the jq program will have `[, ]` (and a few other forms) as inputs. Several builtins are provided to make handling streams easier. The examples below use the streamed form of `[0,[1]]`, which is `[[0],0],[[1,0],1],[[1,0]],[[1]]`. Streaming forms include `[, ]` (to indicate any scalar value, empty array, or empty object), and `[]` (to indicate the end of an array or object). Future versions of jq run with `--stream` and `-seq` may output additional forms such as `["error message"]` when an input text fails to parse. entries: - title: "`truncate_stream(stream_expression)`" body: | Consumes a number as input and truncates the corresponding number of path elements from the left of the outputs of the given streaming expression. examples: - program: '[1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])]' input: '1' output: ['[[[0],2],[[0]]]'] - title: "`fromstream(stream_expression)`" body: | Outputs values corresponding to the stream expression's outputs. examples: - program: 'fromstream(1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))' input: 'null' output: ['[2]'] - title: "`tostream`" body: | The `tostream` builtin outputs the streamed form of its input. examples: - program: '. as $dot|fromstream($dot|tostream)|.==$dot' input: '[0,[1,{"a":1},{"b":2}]]' output: ['true'] - title: Assignment body: | Assignment works a little differently in jq than in most programming languages. jq doesn't distinguish between references to and copies of something - two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object". If an object has two fields which are arrays, `.foo` and `.bar`, and you append something to `.foo`, then `.bar` will not get bigger, even if you've previously set `.bar = .foo`. If you're used to programming in languages like Python, Java, Ruby, Javascript, etc. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance it doesn't actually do that, but that's the general idea). This means that it's impossible to build circular values in jq (such as an array whose first element is itself). This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON. All the assignment operators in jq have path expressions on the left-hand side (LHS). The right-hand side (RHS) provides values to set to the paths named by the LHS path expressions. Values in jq are always immutable. Internally, assignment works by using a reduction to compute new, replacement values for `.` that have had all the desired assignments applied to `.`, then outputting the modified value. This might be made clear by this example: `{a:{b:{c:1}}} | (.a.b|=3), .`. This will output `{"a":{"b":3}}` and `{"a":{"b":{"c":1}}}` because the last sub-expression, `.`, sees the original value, not the modified value. Most users will want to use modification assignment operators, such as `|=` or `+=`, rather than `=`. Note that the LHS of assignment operators refers to a value in `.`. Thus `$var.foo = 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo = 1` instead. Note too that `.a,.b=0` does not set `.a` and `.b`, but `(.a,.b)=0` sets both. entries: - title: "Update-assignment: `|=`" body: | This is the "update" operator '|='. It takes a filter on the right-hand side and works out the new value for the property of `.` being assigned to by running the old value through this expression. For instance, (.foo, .bar) |= .+1 will build an object with the "foo" field set to the input's "foo" plus 1, and the "bar" field set to the input's "bar" plus 1. The left-hand side can be any general path expression; see `path()`. Note that the left-hand side of '|=' refers to a value in `.`. Thus `$var.foo |= . + 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo |= . + 1` instead. If the right-hand side outputs no values (i.e., `empty`), then the left-hand side path will be deleted, as with `del(path)`. If the right-hand side outputs multiple values, only the first one will be used (COMPATIBILITY NOTE: in jq 1.5 and earlier releases, it used to be that only the last one was used). examples: - program: '(..|select(type=="boolean")) |= if . then 1 else 0 end' input: '[true,false,[5,true,[true,[false]],false]]' output: ['[1,0,[5,1,[1,[0]],0]]'] - title: "Arithmetic update-assignment: `+=`, `-=`, `*=`, `/=`, `%=`, `//=`" body: | jq has a few operators of the form `a op= b`, which are all equivalent to `a |= . op b`. So, `+= 1` can be used to increment values, being the same as `|= . + 1`. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] - title: "Plain assignment: `=`" body: | This is the plain assignment operator. Unlike the others, the input to the right-hand-side (RHS) is the same as the input to the left-hand-side (LHS) rather than the value at the LHS path, and all values output by the RHS will be used (as shown below). If the RHS of '=' produces multiple values, then for each such value jq will set the paths on the left-hand side to the value and then it will output the modified `.`. For example, `(.a,.b)=range(2)` outputs `{"a":0,"b":0}`, then `{"a":1,"b":1}`. The "update" assignment forms (see above) do not do this. This example should show the difference between '=' and '|=': Provide input '{"a": {"b": 10}, "b": 20}' to the programs: .a = .b .a |= .b The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20, "b": 20}. The latter will set the "a" field of the input to the "a" field's "b" field, producing {"a": 10, "b": 20}. Another example of the difference between '=' and '|=': null|(.a,.b)=range(3) outputs '{"a":0,"b":0}', '{"a":1,"b":1}', and '{"a":2,"b":2}', while null|(.a,.b)|=range(3) outputs just '{"a":0,"b":0}'. - title: Complex assignments body: | Lots more things are allowed on the left-hand side of a jq assignment than in most languages. We've already seen simple field accesses on the left hand side, and it's no surprise that array accesses work just as well: .posts[0].title = "JQ Manual" What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: .posts[].comments |= . + ["this is great"] That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts). When jq encounters an assignment like 'a = b', it records the "path" taken to select a part of the input document while executing a. This path is then used to find which part of the input to change while executing the assignment. Any filter may be used on the left-hand side of an equals - whichever paths it selects from the input will be where the assignment is performed. This is a very powerful operation. Suppose we wanted to add a comment to blog posts, using the same "blog" input above. This time, we only want to comment on the posts written by "stedolan". We can find those posts using the "select" function described earlier: .posts[] | select(.author == "stedolan") The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: (.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."] - title: Modules body: | jq has a library/module system. Modules are files whose names end in `.jq`. Modules imported by a program are searched for in a default search path (see below). The `import` and `include` directives allow the importer to alter this path. Paths in the a search path are subject to various substitutions. For paths starting with "~/", the user's home directory is substituted for "~". For paths starting with "$ORIGIN/", the path of the jq executable is substituted for "$ORIGIN". For paths starting with "./" or paths that are ".", the path of the including file is substituted for ".". For top-level programs given on the command-line, the current directory is used. Import directives can optionally specify a search path to which the default is appended. The default search path is the search path given to the `-L` command-line option, else `["~/.jq", "$ORIGIN/../lib/jq", "$ORIGIN/../lib"]`. Null and empty string path elements terminate search path processing. A dependency with relative path "foo/bar" would be searched for in "foo/bar.jq" and "foo/bar/bar.jq" in the given search path. This is intended to allow modules to be placed in a directory along with, for example, version control files, README files, and so on, but also to allow for single-file modules. Consecutive components with the same name are not allowed to avoid ambiguities (e.g., "foo/foo"). For example, with `-L$HOME/.jq` a module `foo` can be found in `$HOME/.jq/foo.jq` and `$HOME/.jq/foo/foo.jq`. If "$HOME/.jq" is a file, it is sourced into the main program. entries: - title: "`import RelativePathString as NAME [];`" body: | Imports a module found at the given path relative to a directory in a search path. A ".jq" suffix will be added to the relative path string. The module's symbols are prefixed with "NAME::". The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`include RelativePathString [];`" body: | Imports a module found at the given path relative to a directory in a search path as if it were included in place. A ".jq" suffix will be added to the relative path string. The module's symbols are imported into the caller's namespace as if the module's content had been included directly. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. - title: "`import RelativePathString as $NAME [];`" body: | Imports a JSON file found at the given path relative to a directory in a search path. A ".json" suffix will be added to the relative path string. The file's data will be available as `$NAME::NAME`. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`module ;`" body: | This directive is entirely optional. It's not required for proper operation. It serves only the purpose of providing metadata that can be read with the `modulemeta` builtin. The metadata must be a constant jq expression. It should be an object with keys like "homepage". At this time jq doesn't use this metadata, but it is made available to users via the `modulemeta` builtin. - title: "`modulemeta`" body: | Takes a module name as input and outputs the module's metadata as an object, with the module's imports (including metadata) as an array value for the "deps" key. Programs can use this to query a module's metadata, which they could then use to, for example, search for, download, and install missing dependencies. - title: Colors body: | To configure alternative colors just set the `JQ_COLORS` environment variable to colon-delimited list of partial terminal escape sequences like `"1;31"`, in this order: - color for `null` - color for `false` - color for `true` - color for numbers - color for strings - color for arrays - color for objects The default color scheme is the same as setting `"JQ_COLORS=1;30:0;39:0;39:0;39:0;32:1;39:1;39"`. This is not a manual for VT100/ANSI escapes. However, each of these color specifications should consist of two numbers separated by a semi-colon, where the first number is one of these: - 1 (bright) - 2 (dim) - 4 (underscore) - 5 (blink) - 7 (reverse) - 8 (hidden) and the second is one of these: - 30 (black) - 31 (red) - 32 (green) - 33 (yellow) - 34 (blue) - 35 (magenta) - 36 (cyan) - 37 (white) jq-jq-1.6/docs/content/3.manual/v1.4/0000700000175000017500000000000013366726451016423 5ustar czchenczchenjq-jq-1.6/docs/content/3.manual/v1.4/manual.yml0000600000175000017500000016535713366726451020446 0ustar czchenczchen--- headline: jq 1.4 Manual history: | *The manual for the development version of jq can be found [here](/jq/manual).* body: | A jq program is a "filter": it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks. Filters can be combined in various ways - you can pipe the output of one filter into another filter, or collect the output of a filter into an array. Some filters produce multiple results, for instance there's one that produces all the elements of its input array. Piping that filter into a second runs the second filter for each element of the array. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq. It's important to remember that every filter has an input and an output. Even literals like "hello" or 42 are filters - they take an input but always produce the same literal as output. Operations that combine two filters, like addition, generally feed the same input to both and combine the results. So, you can implement an averaging filter as `add / length` - feeding the input array both to the `add` filter and the `length` filter and dividing the results. But that's getting ahead of ourselves. :) Let's start with something simpler: manpage_intro: | jq(1) -- Command-line JSON processor ==================================== ## SYNOPSIS `jq` [...] [...] `jq` can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents. For instance, running the command `jq 'map(.price) | add'` will take an array of JSON objects as input and return the sum of their "price" fields. By default, `jq` reads a stream of JSON objects (whitespace separated) from `stdin`. One or more may be specified, in which case `jq` will read input from those instead. The are described in the [INVOKING JQ] section, they mostly concern input and output formatting. The is written in the jq language and specifies how to transform the input document. ## FILTERS manpage_epilogue: | ## BUGS Presumably. Report them or discuss them at: https://github.com/stedolan/jq/issues ## AUTHOR Stephen Dolan `` sections: - title: Invoking jq body: | jq filters run on a stream of JSON data. The input to jq is parsed as a sequence of whitespace-separated JSON values which are passed through the provided filter one at a time. The output(s) of the filter are written to standard out, again as a sequence of whitespace-separated JSON data. Note: it is important to mind the shell's quoting rules. As a general rule it's best to always quote (with single-quote characters) the jq program, as too many characters with special meaning to jq are also shell meta-characters. For example, `jq "foo"` will fail on most Unix shells because that will be the same as `jq foo`, which will generally fail because `foo is not defined`. When using the Windows command shell (cmd.exe) it's best to use double quotes around your jq program when given on the command-line (instead of the `-f program-file` option), but then double-quotes in the jq program need backslash escaping. You can affect how jq reads and writes its input and output using some command-line options: * `--version`: Output the jq version and exit with zero. * `--slurp`/`-s`: Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once. * `--online-input`/`-I`: When the top-level input value is an array produce its elements instead of the array. This allows on-line processing of potentially very large top-level arrays' elements. * `--raw-input`/`-R`: Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with `--slurp`, then the entire input is passed to the filter as a single long string. * `--null-input`/`-n`: Don't read any input at all! Instead, the filter is run once using `null` as the input. This is useful when using jq as a simple calculator or to construct JSON data from scratch. * `--compact-output` / `-c`: By default, jq pretty-prints JSON output. Using this option will result in more compact output by instead putting each JSON object on a single line. * `--color-output` / `-C` and `--monochrome-output` / `-M`: By default, jq outputs colored JSON if writing to a terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even if the input specified them as escape sequences (like "\u03bc"). Using this option, you can force jq to produce pure ASCII output with every non-ASCII character replaced with the equivalent escape sequence. * `--unbuffered` Flush the output after each JSON object is printed (useful if you're piping a slow data source into jq and piping jq's output elsewhere). * `--sort-keys` / `-S`: Output the fields of each object with the keys in sorted order. * `--raw-output` / `-r`: With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems. * `-f filename` / `--from-file filename`: Read filter from the file rather than from a command line, like awk's -f option. You can also use '#' to make comments. * `-e` / `--exit-status`: Sets the exit status of jq to 0 if the last output values was neither `false` nor `null`, 1 if the last output value was either `false` or `null`, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran. * `--arg name value`: This option passes a value to the jq program as a predefined variable. If you run jq with `--arg foo bar`, then `$foo` is available in the program and has the value `"bar"`. * `--argfile name filename`: This option passes the first value from the named file as a value to the jq program as a predefined variable. If you run jq with `--argfile foo bar`, then `$foo` is available in the program and has the value resulting from parsing the content of the file named `bar`. - title: Basic filters entries: - title: "`.`" body: | The absolute simplest (and least interesting) filter is `.`. This is a filter that takes its input and produces it unchanged as output. Since jq by default pretty-prints all output, this trivial program can be a useful way of formatting JSON output from, say, `curl`. examples: - program: '.' input: '"Hello, world!"' output: ['"Hello, world!"'] - title: "`.foo`, `.foo.bar`" body: | The simplest *useful* filter is `.foo`. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there's none present. If the key contains special characters, you need to surround it with double quotes like this: `."foo$"`. A filter of the form `.foo.bar` is equivalent to `.foo|.bar`. examples: - program: '.foo' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]' input: '{"foo": 42}' output: [42] - title: "`.foo?`" body: | Just like `.foo`, but does not output even an error when `.` is not an array or an object. examples: - program: '.foo?' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo?' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]?' input: '{"foo": 42}' output: [42] - program: '[.foo?]' input: '[1,2]' output: ['[]'] - title: "`.[]`, `.[2]`, `.[10:15]`" body: | You can also look up fields of an object using syntax like `.["foo"]` (.foo above is a shorthand version of this). This one works for arrays as well, if the key is an integer. Arrays are zero-based (like javascript), so `.[2]` returns the third element of the array. The `.[10:15]` syntax can be used to return a subarray of an array or substring of a string. The array returned by `.[10:15]` will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive). Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array). The `?` "operator" can also be used with the slice operator, as in `.[10:15]?`, which outputs values where the inputs are slice-able. examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['{"name":"JSON", "good":true}'] - program: '.[2]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] - program: '.[2:4]' input: '["a","b","c","d","e"]' output: ['["c", "d"]'] - program: '.[2:4]' input: '"abcdefghi"' output: ['"cd"'] - program: '.[:3]' input: '["a","b","c","d","e"]' output: ['["a", "b", "c"]'] - program: '.[-2:]' input: '["a","b","c","d","e"]' output: ['["d", "e"]'] - title: "`.[]`" body: | If you use the `.[index]` syntax, but omit the index entirely, it will return *all* of the elements of an array. Running `.[]` with the input `[1,2,3]` will produce the numbers as three separate results, rather than as a single array. You can also use this on an object, and it will return all the values of the object. examples: - program: '.[]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: - '{"name":"JSON", "good":true}' - '{"name":"XML", "good":false}' - program: '.[]' input: '[]' output: [] - program: '.[]' input: '{"a": 1, "b": 1}' output: ['1', '1'] - title: "`.[]?`" body: | Like `.[]`, but no errors will be output if . is not an array or object. - title: "`,`" body: | If two filters are separated by a comma, then the input will be fed into both and there will be multiple outputs: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right. For instance, filter `.foo, .bar`, produces both the "foo" fields and "bar" fields as separate outputs. examples: - program: '.foo, .bar' input: '{"foo": 42, "bar": "something else", "baz": true}' output: ['42', '"something else"'] - program: ".user, .projects[]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['"stedolan"', '"jq"', '"wikiflow"'] - program: '.[4,2]' input: '["a","b","c","d","e"]' output: ['"e"', '"c"'] - title: "`|`" body: | The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right. It's pretty much the same as the Unix shell's pipe, if you're used to that. If the one on the left produces multiple results, the one on the right will be run for each of those results. So, the expression `.[] | .foo` retrieves the "foo" field of each element of the input array. examples: - program: '.[] | .name' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['"JSON"', '"XML"'] - title: Types and Values body: | jq supports the same set of datatypes as JSON - numbers, strings, booleans, arrays, objects (which in JSON-speak are hashes with only string keys), and "null". Booleans, null, strings and numbers are written the same way as in javascript. Just like everything else in jq, these simple values take an input and produce an output - `42` is a valid jq expression that takes an input, ignores it, and returns 42 instead. entries: - title: Array construction - `[]` body: | As in JSON, `[]` is used to construct arrays, as in `[1,2,3]`. The elements of the arrays can be any jq expression. All of the results produced by all of the expressions are collected into one big array. You can use it to construct an array out of a known quantity of values (as in `[.foo, .bar, .baz]`) or to "collect" all the results of a filter into an array (as in `[.items[].name]`) Once you understand the "," operator, you can look at jq's array syntax in a different light: the expression `[1,2,3]` is not using a built-in syntax for comma-separated arrays, but is instead applying the `[]` operator (collect results) to the expression 1,2,3 (which produces three different results). If you have a filter `X` that produces four results, then the expression `[X]` will produce a single result, an array of four elements. examples: - program: "[.user, .projects[]]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['["stedolan", "jq", "wikiflow"]'] - title: Objects - `{}` body: | Like JSON, `{}` is for constructing objects (aka dictionaries or hashes), as in: `{"a": 42, "b": 17}`. If the keys are "sensible" (all alphabetic characters), then the quotes can be left off. The value can be any expression (although you may need to wrap it in parentheses if it's a complicated one), which gets applied to the {} expression's input (remember, all filters have an input and an output). {foo: .bar} will produce the JSON object `{"foo": 42}` if given the JSON object `{"bar":42, "baz":43}`. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write {user: .user, title: .title} Because that's so common, there's a shortcut syntax: `{user, title}`. If one of the expressions produces multiple results, multiple dictionaries will be produced. If the input's {"user":"stedolan","titles":["JQ Primer", "More JQ"]} then the expression {user, title: .titles[]} will produce two outputs: {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} Putting parentheses around the key means it will be evaluated as an expression. With the same input as above, {(.user): .titles} produces {"stedolan": ["JQ Primer", "More JQ"]} examples: - program: '{user, title: .titles[]}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: - '{"user":"stedolan", "title": "JQ Primer"}' - '{"user":"stedolan", "title": "More JQ"}' - program: '{(.user): .titles}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: ['{"stedolan": ["JQ Primer", "More JQ"]}'] - title: Builtin operators and functions body: | Some jq operator (for instance, `+`) do different things depending on the type of their arguments (arrays, numbers, etc.). However, jq never does implicit type conversions. If you try to add a string to an object you'll get an error message and no result. entries: - title: Addition - `+` body: | The operator `+` takes two filters, applies them both to the same input, and adds the results together. What "adding" means depends on the types involved: - **Numbers** are added by normal arithmetic. - **Arrays** are added by being concatenated into a larger array. - **Strings** are added by being joined into a larger string. - **Objects** are added by merging, that is, inserting all the key-value pairs from both objects into a single combined object. If both objects contain a value for the same key, the object on the right of the `+` wins. (For recursive merge use the `*` operator.) `null` can be added to any value, and returns the other value unchanged. examples: - program: '.a + 1' input: '{"a": 7}' output: ['8'] - program: '.a + .b' input: '{"a": [1,2], "b": [3,4]}' output: ['[1,2,3,4]'] - program: '.a + null' input: '{"a": 1}' output: ['1'] - program: '.a + 1' input: '{}' output: ['1'] - program: '{a: 1} + {b: 2} + {c: 3} + {a: 42}' input: 'null' output: ['{"a": 42, "b": 2, "c": 3}'] - title: Subtraction - `-` body: | As well as normal arithmetic subtraction on numbers, the `-` operator can be used on arrays to remove all occurences of the second array's elements from the first array. examples: - program: '4 - .a' input: '{"a":3}' output: ['1'] - program: . - ["xml", "yaml"] input: '["xml", "yaml", "json"]' output: ['["json"]'] - title: Multiplication, division, modulo - `*`, `/`, and `%` body: | These operators only work on numbers, and do the expected. Multiplying a string by a number produces the concatenation of that string that many times. Dividing a string by another splits the first using the second as separators. Multiplying two objects will merge them recursively: this works like addition but if both objects contain a value for the same key, and the values are objects, the two are merged with the same strategy. examples: - program: '10 / . * 3' input: 5 output: [6] - program: '. / ", "' input: '"a, b,c,d, e"' output: ['["a","b,c,d","e"]'] - program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}' input: 'null' output: ['{"k": {"a": 0, "b": 2, "c": 3}}'] - title: "`length`" body: | The builtin function `length` gets the length of various different types of value: - The length of a **string** is the number of Unicode codepoints it contains (which will be the same as its JSON-encoded length in bytes if it's pure ASCII). - The length of an **array** is the number of elements. - The length of an **object** is the number of key-value pairs. - The length of **null** is zero. examples: - program: '.[] | length' input: '[[1,2], "string", {"a":2}, null]' output: [2, 6, 1, 0] - title: "`keys`" body: | The builtin function `keys`, when given an object, returns its keys in an array. The keys are sorted "alphabetically", by unicode codepoint order. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings. When `keys` is given an array, it returns the valid indices for that array: the integers from 0 to length-1. examples: - program: 'keys' input: '{"abc": 1, "abcd": 2, "Foo": 3}' output: ['["Foo", "abc", "abcd"]'] - program: 'keys' input: '[42,3,35]' output: ['[0,1,2]'] - title: "`has`" body: | The builtin function `has` returns whether the input object has the given key, or the input array has an element at the given index. `has($key)` has the same effect as checking whether `$key` is a member of the array returned by `keys`, although `has` will be faster. examples: - program: 'map(has("foo"))' input: '[{"foo": 42}, {}]' output: ['[true, false]'] - program: 'map(has(2))' input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] - title: "`del`" body: | The builtin function `del` removes a key and its corresponding value from an object. examples: - program: 'del(.foo)' input: '{"foo": 42, "bar": 9001, "baz": 42}' output: ['{"bar": 9001, "baz": 42}'] - program: 'del(.[1, 2])' input: '["foo", "bar", "baz"]' output: ['["foo"]'] - title: "`to_entries`, `from_entries`, `with_entries`" body: | These functions convert between an object and an array of key-value pairs. If `to_entries` is passed an object, then for each `k: v` entry in the input, the output array includes `{"key": k, "value": v}`. `from_entries` does the opposite conversion, and `with_entries(foo)` is a shorthand for `to_entries | map(foo) | from_entries`, useful for doing some operation to all keys and values of an object. examples: - program: 'to_entries' input: '{"a": 1, "b": 2}' output: ['[{"key":"a", "value":1}, {"key":"b", "value":2}]'] - program: 'from_entries' input: '[{"key":"a", "value":1}, {"key":"b", "value":2}]' output: ['{"a": 1, "b": 2}'] - program: 'with_entries(.key |= "KEY_" + .)' input: '{"a": 1, "b": 2}' output: ['{"KEY_a": 1, "KEY_b": 2}'] - title: "`select`" body: | The function `select(foo)` produces its input unchanged if `foo` returns true for that input, and produces no output otherwise. It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))` will give you `[2,3]`. examples: - program: 'map(select(. >= 2))' input: '[1,5,3,0,7]' output: ['[5,3,7]'] - title: "`arrays`, `objects`, `iterables`, `booleans`, `numbers`, `strings`, `nulls`, `values`, `scalars`" body: | These built-ins select only inputs that are arrays, objects, iterables (arrays or objects), booleans, numbers, strings, null, non-null values, and non-iterables, respectively. examples: - program: '.[]|numbers' input: '[[],{},1,"foo",null,true,false]' output: ['1'] - title: "`empty`" body: | `empty` returns no results. None at all. Not even `null`. It's useful on occasion. You'll know if you need it :) examples: - program: '1, empty, 2' input: 'null' output: [1, 2] - program: '[1,2,empty,3]' input: 'null' output: ['[1,2,3]'] - title: "`map(x)`" body: | For any filter `x`, `map(x)` will run that filter for each element of the input array, and produce the outputs a new array. `map(.+1)` will increment each element of an array of numbers. `map(x)` is equivalent to `[.[] | x]`. In fact, this is how it's defined. examples: - program: 'map(.+1)' input: '[1,2,3]' output: ['[2,3,4]'] - title: "`paths`" body: | Outputs the paths to all the elements in its input (except it does not output the empty list, representing . itself). `paths` is equivalent to def paths: path(recurse(if (type|. == "array" or . == "object") then .[] else empty end))|select(length > 0); examples: - program: '[paths]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1],[1,0],[1,1],[1,1,"a"]]'] - title: "`leaf_paths`" body: | Outputs the paths to all the leaves (non-array, non-object elements) in its input. examples: - program: '[leaf_paths]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1,1,"a"]]'] - title: "`add`" body: | The filter `add` takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the `+` operator (described above). If the input is an empty array, `add` returns `null`. examples: - program: add input: '["a","b","c"]' output: ['"abc"'] - program: add input: '[1, 2, 3]' output: [6] - program: add input: '[]' output: ["null"] - title: "`any`" body: | The filter `any` takes as input an array of boolean values, and produces `true` as output if any of the elements of the array are `true`. If the input is an empty array, `any` returns `false`. examples: - program: any input: '[true, false]' output: ["true"] - program: any input: '[false, false]' output: ["false"] - program: any input: '[]' output: ["false"] - title: "`all`" body: | The filter `all` takes as input an array of boolean values, and produces `true` as output if all of the elements of the array are `true`. If the input is an empty array, `all` returns `true`. examples: - program: all input: '[true, false]' output: ["false"] - program: all input: '[true, true]' output: ["true"] - program: all input: '[]' output: ["true"] - title: "`range`" body: | The `range` function produces a range of numbers. `range(4;10)` produces 6 numbers, from 4 (inclusive) to 10 (exclusive). The numbers are produced as separate outputs. Use `[range(4;10)]` to get a range as an array. examples: - program: 'range(2;4)' input: 'null' output: ['2', '3'] - program: '[range(2;4)]' input: 'null' output: ['[2,3]'] - title: "`floor`" body: | The `floor` function returns the floor of its numeric input. examples: - program: 'floor' input: '3.14159' output: ['3'] - title: "`sqrt`" body: | The `sqrt` function returns the square root of its numeric input. examples: - program: 'sqrt' input: '9' output: ['3'] - title: "`tonumber`" body: | The `tonumber` function parses its input as a number. It will convert correctly-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input. examples: - program: '.[] | tonumber' input: '[1, "1"]' output: [1, 1] - title: "`tostring`" body: | The `tostring` function prints its input as a string. Strings are left unchanged, and all other values are JSON-encoded. examples: - program: '.[] | tostring' input: '[1, "1", [1]]' output: ['"1"', '"1"', '"[1]"'] - title: "`type`" body: | The `type` function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object. examples: - program: 'map(type)' input: '[0, false, [], {}, null, "hello"]' output: ['["number", "boolean", "array", "object", "null", "string"]'] - title: "`sort, sort_by`" body: | The `sort` functions sorts its input, which must be an array. Values are sorted in the following order: * `null` * `false` * `true` * numbers * strings, in alphabetical order (by unicode codepoint value) * arrays, in lexical order * objects The ordering for objects is a little complex: first they're compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key. `sort_by` may be used to sort by a particular field of an object, or by applying any jq filter. `sort_by(foo)` compares two elements by comparing the result of `foo` on each element. examples: - program: 'sort' input: '[8,3,null,6]' output: ['[null,3,6,8]'] - program: 'sort_by(.foo)' input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]' output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]'] - title: "`group_by`" body: | `group_by(.foo)` takes as input an array, groups the elements having the same `.foo` field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the `.foo` field. Any jq expression, not just a field access, may be used in place of `.foo`. The sorting order is the same as described in the `sort` function above. examples: - program: 'group_by(.foo)' input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]'] - title: "`min`, `max`, `min_by`, `max_by`" body: | Find the minimum or maximum element of the input array. The `_by` versions allow you to specify a particular field or property to examine, e.g. `min_by(.foo)` finds the object with the smallest `foo` field. examples: - program: 'min' input: '[5,4,2,7]' output: ['2'] - program: 'max_by(.foo)' input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' output: ['{"foo":2, "bar":3}'] - title: "`unique`" body: | The `unique` function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed. examples: - program: 'unique' input: '[1,2,5,3,5,3,1,3]' output: ['[1,2,3,5]'] - title: "`unique_by`" body: | The `unique_by(.foo)` function takes as input an array and produces an array of the same elements, in sorted order, with elqements with a duplicate `.foo` field removed. Think of it as making an array by taking one element out of every group produced by `group_by`. examples: - program: 'unique_by(.foo)' input: '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' output: ['[{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]'] - program: 'unique_by(length)' input: '["chunky", "bacon", "kitten", "cicada", "asparagus"]' output: ['["bacon", "chunky", "asparagus"]'] - title: "`reverse`" body: | This function reverses an array. examples: - program: 'reverse' input: '[1,2,3,4]' output: ['[4,3,2,1]'] - title: "`contains`" body: | The filter `contains(b)` will produce true if b is completely contained within the input. A string B is contained in a string A if B is a substring of A. An array B is contained in an array A is all elements in B are contained in any element in A. An object B is contained in object A if all of the values in B are contained in the value in A with the same key. All other types are assumed to be contained in each other if they are equal. examples: - program: 'contains("bar")' input: '"foobar"' output: ['true'] - program: 'contains(["baz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['true'] - program: 'contains(["bazzzzz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['false'] - program: 'contains({foo: 12, bar: [{barp: 12}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['true'] - program: 'contains({foo: 12, bar: [{barp: 15}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['false'] - title: "`indices(s)`" body: | Outputs an array containing the indices in `.` where `s` occurs. The input may be an array, in which case if `s` is an array then the indices output will be those where all elements in `.` match those of `s`. examples: - program: 'indices(", ")' input: '"a,b, cd, efg, hijk"' output: ['[3,7,12]'] - program: 'indices(1)' input: '[0,1,2,1,3,1,4]' output: ['[1,3,5]'] - program: 'indices([1,2])' input: '[0,1,2,3,1,4,2,5,1,2,6,7]' output: ['[1,8]'] - title: "`index(s)`, `rindex(s)`" body: | Outputs the index of the first (`index`) or last (`rindex`) occurrence of `s` in the input. examples: - program: 'index(", ")' input: '"a,b, cd, efg, hijk"' output: ['3'] - program: 'rindex(", ")' input: '"a,b, cd, efg, hijk"' output: ['12'] - title: "`startswith`" body: | Outputs `true` if . starts with the given string argument. examples: - program: '[.[]|startswith("foo")]' input: '["fo", "foo", "barfoo", "foobar", "barfoob"]' output: ['[false, true, false, true, false]'] - title: "`endswith`" body: | Outputs `true` if . ends with the given string argument. examples: - program: '[.[]|endswith("foo")]' input: '["foobar", "barfoo"]' output: ['[false, true]'] - title: "`ltrimstr`" body: | Outputs its input with the given prefix string removed, if it starts with it. examples: - program: '[.[]|ltrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "afoo"]' output: ['["fo","","barfoo","bar","afoo"]'] - title: "`rtrimstr`" body: | Outputs its input with the given suffix string removed, if it starts with it. examples: - program: '[.[]|rtrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "foob"]' output: ['["fo","","bar","foobar","foob"]'] - title: "`explode`" body: | Converts an input string into an array of the string's codepoint numbers. examples: - program: 'explode' input: '"foobar"' output: ['[102,111,111,98,97,114]'] - title: "`implode`" body: | The inverse of explode. examples: - program: 'implode' input: '[65, 66, 67]' output: ['"ABC"'] - title: "`split`" body: | Splits an input string on the separator argument. examples: - program: 'split(", ")' input: '"a, b,c,d, e"' output: ['["a","b,c,d","e"]'] - title: "`join`" body: | Joins the array of elements given as input, using the argument as separator. It is the inverse of `split`: that is, running `split("foo") | join("foo")` over any input string returns said input string. examples: - program: 'join(", ")' input: '["a","b,c,d","e"]' output: ['"a, b,c,d, e"'] - title: "`recurse`" body: | The `recurse` function allows you to search through a recursive structure, and extract interesting data from all levels. Suppose your input represents a filesystem: {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} Now suppose you want to extract all of the filenames present. You need to retrieve `.name`, `.children[].name`, `.children[].children[].name`, and so on. You can do this with: recurse(.children[]) | .name examples: - program: 'recurse(.foo[])' input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}' output: - '{"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}' - '{"foo":[]}' - '{"foo":[{"foo":[]}]}' - '{"foo":[]}' - title: "`recurse_down`" body: | A quieter version of `recurse(.[])`, equivalent to: def recurse_down: recurse(.[]?); - title: "`..`" body: | Short-hand for `recurse_down`. This is intended to resemble the XPath `//` operator. Note that `..a` does not work; use `..|a` instead. examples: - program: '..|.a?' input: '[[{"a":1}]]' output: ['1'] - title: "String interpolation - `\\(foo)`" body: | Inside a string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string. examples: - program: '"The input was \(.), which is one less than \(.+1)"' input: '42' output: ['"The input was 42, which is one less than 43"'] - title: "Convert to/from JSON" body: | The `tojson` and `fromjson` builtins dump values as JSON texts or parse JSON texts into values, respectively. The tojson builtin differs from tostring in that tostring returns strings unmodified, while tojson encodes strings as JSON strings. examples: - program: '[.[]|tostring]' input: '[1, "foo", ["foo"]]' output: ['["1","foo","[\"foo\"]"]'] - program: '[.[]|tojson]' input: '[1, "foo", ["foo"]]' output: ['["1","\"foo\"","[\"foo\"]"]'] - program: '[.[]|tojson|fromjson]' input: '[1, "foo", ["foo"]]' output: ['[1,"foo",["foo"]]'] - title: "Format strings and escaping" body: | The `@foo` syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth. `@foo` can be used as a filter on its own, the possible escapings are: * `@text`: Calls `tostring`, see that function for details. * `@json`: Serialises the input as JSON. * `@html`: Applies HTML/XML escaping, by mapping the characters `<>&'"` to their entity equivalents `<`, `>`, `&`, `'`, `"`. * `@uri`: Applies percent-encoding, by mapping all reserved URI characters to a `%xx` sequence. * `@csv`: The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition. * `@sh`: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings. * `@base64`: The input is converted to base64 as specified by RFC 4648. This syntax can be combined with string interpolation in a useful way. You can follow a `@foo` token with a string literal. The contents of the string literal will *not* be escaped. However, all interpolations made inside that string literal will be escaped. For instance, @uri "https://www.google.com/search?q=\(.search)" will produce the following output for the input `{"search":"what is jq?"}`: "https://www.google.com/search?q=what%20is%20jq%3f" Note that the slashes, question mark, etc. in the URL are not escaped, as they were part of the string literal. examples: - program: '@html' input: '"This works if x < y"' output: ['"This works if x < y"'] # - program: '@html "Anonymous said: \(.)"' # input: '""' # output: ["Anonymous said: <script>alert("lol hax");</script>"] - program: '@sh "echo \(.)"' input: "\"O'Hara's Ale\"" output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""] - title: Conditionals and Comparisons entries: - title: "`==`, `!=`" body: | The expression 'a == b' will produce 'true' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and 'false' otherwise. In particular, strings are never considered equal to numbers. If you're coming from Javascript, jq's == is like Javascript's === - considering values equal only when they have the same type as well as the same value. != is "not equal", and 'a != b' returns the opposite value of 'a == b' examples: - program: '.[] == 1' input: '[1, 1.0, "1", "banana"]' output: ['true', 'true', 'false', 'false'] - title: if-then-else body: | `if A then B else C end` will act the same as `B` if `A` produces a value other than false or null, but act the same as `C` otherwise. Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you'll sometimes have to be more explicit about the condition you want: you can't test whether, e.g. a string is empty using `if .name then A else B end`, you'll need something more like `if (.name | length) > 0 then A else B end` instead. If the condition `A` produces multiple results, then `B` is evaluated once for each result that is not false or null, and `C` is evaluated once for each false or null. More cases can be added to an if using `elif A then B` syntax. examples: - program: |- if . == 0 then "zero" elif . == 1 then "one" else "many" end input: 2 output: ['"many"'] - title: "`>, >=, <=, <`" body: | The comparison operators `>`, `>=`, `<=`, `<` return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively). The ordering is the same as that described for `sort`, above. examples: - program: '. < 5' input: 2 output: ['true'] - title: and/or/not body: | jq supports the normal Boolean operators and/or/not. They have the same standard of truth as if expressions - false and null are considered "false values", and anything else is a "true value". If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input. `not` is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in `.foo and .bar | not`. These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default". If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below. examples: - program: '42 and "a string"' input: 'null' output: ['true'] - program: '(true, false) or false' input: 'null' output: ['true', 'false'] # - program: '(true, false) and (true, false)' # input: 'null' # output: ['true', 'false', 'false', 'false'] - program: '(true, true) and (true, false)' input: 'null' output: ['true', 'false', 'true', 'false'] - program: '[true, false | not]' input: 'null' output: ['[false, true]'] - title: Alternative operator - `//` body: | A filter of the form `a // b` produces the same results as `a`, if `a` produces results other than `false` and `null`. Otherwise, `a // b` produces the same results as `b`. This is useful for providing defaults: `.foo // 1` will evaluate to `1` if there's no `.foo` element in the input. It's similar to how `or` is sometimes used in Python (jq's `or` operator is reserved for strictly Boolean operations). examples: - program: '.foo // 42' input: '{"foo": 19}' output: [19] - program: '.foo // 42' input: '{}' output: [42] - title: Advanced features body: | Variables are an absolute necessity in most programming languages, but they're relegated to an "advanced feature" in jq. In most languages, variables are the only means of passing around data. If you calculate a value, and you want to use it more than once, you'll need to store it in a variable. To pass a value to another part of the program, you'll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data. It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq's standard library (many jq functions such as `map` and `find` are in fact written in jq). Finally, jq has a `reduce` operation, which is very powerful but a bit tricky. Again, it's mostly used internally, to define some useful bits of jq's standard library. entries: - title: Variables body: | In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next. Many expressions, for instance `a + b`, pass their input to two distinct subexpressions (here `a` and `b` are both passed the same input), so variables aren't usually necessary in order to use a value twice. For instance, calculating the average value of an array of numbers requires a few variables in most languages - at least one to hold the array, perhaps one for each element or for a loop counter. In jq, it's simply `add / length` - the `add` expression is given the array and produces its sum, and the `length` expression is given the array and produces its length. So, there's generally a cleaner way to solve most problems in jq than defining variables. Still, sometimes they do make things easier, so jq lets you define variables using `expression as $variable`. All variable names start with `$`. Here's a slightly uglier version of the array-averaging example: length as $array_length | add / $array_length We'll need a more complicated problem to find a situation where using variables actually makes our lives easier. Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names. Our input looks like: {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} We want to produce the posts with the author field containing a real name, as in: {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"} We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: .realnames as $names | .posts[] | {title, author: $names[.author]} The expression `exp as $x | ...` means: for each value of expression `exp`, run the rest of the pipeline with the entire original input, and with `$x` set to that value. Thus `as` functions as something of a foreach loop. Variables are scoped over the rest of the expression that defines them, so .realnames as $names | (.posts[] | {title, author: $names[.author]}) will work, but (.realnames as $names | .posts[]) | {title, author: $names[.author]} won't. examples: - program: '.bar as $x | .foo | . + $x' input: '{"foo":10, "bar":200}' output: ['210'] - title: 'Defining Functions' body: | You can give a filter a name using "def" syntax: def increment: . + 1; From then on, `increment` is usable as a filter just like a builtin function (in fact, this is how some of the builtins are defined). A function may take arguments: def map(f): [.[] | f]; Arguments are passed as filters, not as values. The same argument may be referenced multiple times with different inputs (here `f` is run for each element of the input array). Arguments to a function work more like callbacks than like value arguments. If you want the value-argument behaviour for defining simple functions, you can just use a variable: def addvalue(f): f as $value | map(. + $value); With that definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' output: ['[[1,2,1], [10,20,10]]'] - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' input: '[[1,2],[10,20]]' output: ['[[1,2,1,2], [10,20,1,2]]'] - title: Reduce body: | The `reduce` syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer. As an example, we'll pass `[3,2,1]` to this expression: reduce .[] as $item (0; . + $item) For each result that `.[]` produces, `. + $item` is run to accumulate a running total, starting from 0. In this example, `.[]` produces the results 3, 2, and 1, so the effect is similar to running something like this: 0 | (3 as $item | . + $item) | (2 as $item | . + $item) | (1 as $item | . + $item) examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - title: Assignment body: | Assignment works a little differently in jq than in most programming languages. jq doesn't distinguish between references to and copies of something - two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object". If an object has two fields which are arrays, `.foo` and `.bar`, and you append something to `.foo`, then `.bar` will not get bigger. Even if you've just set `.bar = .foo`. If you're used to programming in languages like Python, Java, Ruby, Javascript, etc. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance, it doesn't actually do that, but that's the general idea). entries: - title: "`=`" body: | The filter `.foo = 1` will take as input an object and produce as output an object with the "foo" field set to 1. There is no notion of "modifying" or "changing" something in jq - all jq values are immutable. For instance, .foo = .bar | .foo.baz = 1 will not have the side-effect of setting .bar.baz to be set to 1, as the similar-looking program in Javascript, Python, Ruby or other languages would. Unlike these languages (but like Haskell and some other functional languages), there is no notion of two arrays or objects being "the same array" or "the same object". They can be equal, or not equal, but if we change one of them in no circumstances will the other change behind our backs. This means that it's impossible to build circular values in jq (such as an array whose first element is itself). This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON. - title: "`|=`" body: | As well as the assignment operator '=', jq provides the "update" operator '|=', which takes a filter on the right-hand side and works out the new value for the property being assigned to by running the old value through this expression. For instance, .foo |= .+1 will build an object with the "foo" field set to the input's "foo" plus 1. This example should show the difference between '=' and '|=': Provide input '{"a": {"b": 10}, "b": 20}' to the programs: .a = .b .a |= .b The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20}. The latter will set the "a" field of the input to the "a" field's "b" field, producing {"a": 10}. - title: "`+=`, `-=`, `*=`, `/=`, `%=`, `//=`" body: | jq has a few operators of the form `a op= b`, which are all equivalent to `a |= . op b`. So, `+= 1` can be used to increment values. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] - title: Complex assignments body: | Lots more things are allowed on the left-hand side of a jq assignment than in most langauges. We've already seen simple field accesses on the left hand side, and it's no surprise that array accesses work just as well: .posts[0].title = "JQ Manual" What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: .posts[].comments |= . + ["this is great"] That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts). When jq encounters an assignment like 'a = b', it records the "path" taken to select a part of the input document while executing a. This path is then used to find which part of the input to change while executing the assignment. Any filter may be used on the left-hand side of an equals - whichever paths it selects from the input will be where the assignment is performed. This is a very powerful operation. Suppose we wanted to add a comment to blog posts, using the same "blog" input above. This time, we only want to comment on the posts written by "stedolan". We can find those posts using the "select" function described earlier: .posts[] | select(.author == "stedolan") The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: (.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."] jq-jq-1.6/docs/content/3.manual/v1.5/0000700000175000017500000000000013366726451016424 5ustar czchenczchenjq-jq-1.6/docs/content/3.manual/v1.5/manual.yml0000600000175000017500000032276413366726451020444 0ustar czchenczchen--- headline: jq 1.5 Manual history: | *The manual for the development version of jq can be found [here](/jq/manual).* body: | A jq program is a "filter": it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks. Filters can be combined in various ways - you can pipe the output of one filter into another filter, or collect the output of a filter into an array. Some filters produce multiple results, for instance there's one that produces all the elements of its input array. Piping that filter into a second runs the second filter for each element of the array. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq. It's important to remember that every filter has an input and an output. Even literals like "hello" or 42 are filters - they take an input but always produce the same literal as output. Operations that combine two filters, like addition, generally feed the same input to both and combine the results. So, you can implement an averaging filter as `add / length` - feeding the input array both to the `add` filter and the `length` filter and then performing the division. But that's getting ahead of ourselves. :) Let's start with something simpler: manpage_intro: | jq(1) -- Command-line JSON processor ==================================== ## SYNOPSIS `jq` [...] [...] `jq` can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents. For instance, running the command `jq 'map(.price) | add'` will take an array of JSON objects as input and return the sum of their "price" fields. `jq` can accept text input as well, but by default, `jq` reads a stream of JSON entities (including numbers and other literals) from `stdin`. Whitespace is only needed to separate entities such as 1 and 2, and true and false. One or more may be specified, in which case `jq` will read input from those instead. The are described in the [INVOKING JQ] section; they mostly concern input and output formatting. The is written in the jq language and specifies how to transform the input file or document. ## FILTERS manpage_epilogue: | ## BUGS Presumably. Report them or discuss them at: https://github.com/stedolan/jq/issues ## AUTHOR Stephen Dolan `` sections: - title: Invoking jq body: | jq filters run on a stream of JSON data. The input to jq is parsed as a sequence of whitespace-separated JSON values which are passed through the provided filter one at a time. The output(s) of the filter are written to standard out, again as a sequence of whitespace-separated JSON data. Note: it is important to mind the shell's quoting rules. As a general rule it's best to always quote (with single-quote characters) the jq program, as too many characters with special meaning to jq are also shell meta-characters. For example, `jq "foo"` will fail on most Unix shells because that will be the same as `jq foo`, which will generally fail because `foo is not defined`. When using the Windows command shell (cmd.exe) it's best to use double quotes around your jq program when given on the command-line (instead of the `-f program-file` option), but then double-quotes in the jq program need backslash escaping. You can affect how jq reads and writes its input and output using some command-line options: * `--version`: Output the jq version and exit with zero. * `--seq`: Use the `application/json-seq` MIME type scheme for separating JSON texts in jq's input and output. This means that an ASCII RS (record separator) character is printed before each value on output and an ASCII LF (line feed) is printed after every output. Input JSON texts that fail to parse are ignored (but warned about), discarding all subsequent input until the next RS. This more also parses the output of jq without the `--seq` option. * `--stream`: Parse the input in streaming fashion, outputing arrays of path and leaf values (scalars and empty arrays or empty objects). For example, `"a"` becomes `[[],"a"]`, and `[[],"a",["b"]]` becomes `[[0],[]]`, `[[1],"a"]`, and `[[1,0],"b"]`. This is useful for processing very large inputs. Use this in conjunction with filtering and the `reduce` and `foreach` syntax to reduce large inputs incrementally. * `--slurp`/`-s`: Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once. * `--raw-input`/`-R`: Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with `--slurp`, then the entire input is passed to the filter as a single long string. * `--null-input`/`-n`: Don't read any input at all! Instead, the filter is run once using `null` as the input. This is useful when using jq as a simple calculator or to construct JSON data from scratch. * `--compact-output` / `-c`: By default, jq pretty-prints JSON output. Using this option will result in more compact output by instead putting each JSON object on a single line. * `--tab`: Use a tab for each indentation level instead of two spaces. * `--indent n`: Use the given number of spaces (no more than 8) for indentation. * `--color-output` / `-C` and `--monochrome-output` / `-M`: By default, jq outputs colored JSON if writing to a terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even if the input specified them as escape sequences (like "\u03bc"). Using this option, you can force jq to produce pure ASCII output with every non-ASCII character replaced with the equivalent escape sequence. * `--unbuffered` Flush the output after each JSON object is printed (useful if you're piping a slow data source into jq and piping jq's output elsewhere). * `--sort-keys` / `-S`: Output the fields of each object with the keys in sorted order. * `--raw-output` / `-r`: With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems. * `--join-output` / `-j`: Like `-r` but jq won't print a newline after each output. * `-f filename` / `--from-file filename`: Read filter from the file rather than from a command line, like awk's -f option. You can also use '#' to make comments. * `-Ldirectory` / `-L directory`: Prepend `directory` to the search list for modules. If this option is used then no builtin search list is used. See the section on modules below. * `-e` / `--exit-status`: Sets the exit status of jq to 0 if the last output values was neither `false` nor `null`, 1 if the last output value was either `false` or `null`, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran. * `--arg name value`: This option passes a value to the jq program as a predefined variable. If you run jq with `--arg foo bar`, then `$foo` is available in the program and has the value `"bar"`. Note that `value` will be treated as a string, so `--arg foo 123` will bind `$foo` to `"123"`. * `--argjson name JSON-text`: This option passes a JSON-encoded value to the jq program as a predefined variable. If you run jq with `--argjson foo 123`, then `$foo` is available in the program and has the value `123`. * `--slurpfile variable-name filename`: This option reads all the JSON texts in the named file and binds an array of the parsed JSON values to the given global variable. If you run jq with `--argfile foo bar`, then `$foo` is available in the program and has an array whose elements correspond to the texts in the file named `bar`. * `--argfile variable-name filename`: Do not use. Use `--slurpfile` instead. (This option is like `--slurpfile`, but when the file has just one text, then that is used, else an array of texts is used as in `--slurpfile`.) * `--run-tests [filename]`: Runs the tests in the given file or standard input. This must be the last option given and does not honor all preceding options. The input consists of comment lines, empty lines, and program lines followed by one input line, as many lines of output as are expected (one per output), and a terminating empty line. Compilation failure tests start with a line containing only "%%FAIL", then a line containing the program to compile, then a line containing an error message to compare to the actual. Be warned that this option can change backwards-incompatibly. - title: Basic filters entries: - title: "`.`" body: | The absolute simplest (and least interesting) filter is `.`. This is a filter that takes its input and produces it unchanged as output. Since jq by default pretty-prints all output, this trivial program can be a useful way of formatting JSON output from, say, `curl`. examples: - program: '.' input: '"Hello, world!"' output: ['"Hello, world!"'] - title: "`.foo`, `.foo.bar`" body: | The simplest *useful* filter is `.foo`. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there's none present. If the key contains special characters, you need to surround it with double quotes like this: `."foo$"`. A filter of the form `.foo.bar` is equivalent to `.foo|.bar`. examples: - program: '.foo' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]' input: '{"foo": 42}' output: [42] - title: "`.foo?`" body: | Just like `.foo`, but does not output even an error when `.` is not an array or an object. examples: - program: '.foo?' input: '{"foo": 42, "bar": "less interesting data"}' output: [42] - program: '.foo?' input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - program: '.["foo"]?' input: '{"foo": 42}' output: [42] - program: '[.foo?]' input: '[1,2]' output: ['[]'] - title: "`.[]`, `.[2]`, `.[10:15]`" body: | You can also look up fields of an object using syntax like `.["foo"]` (.foo above is a shorthand version of this). This one works for arrays as well, if the key is an integer. Arrays are zero-based (like javascript), so `.[2]` returns the third element of the array. The `.[10:15]` syntax can be used to return a subarray of an array or substring of a string. The array returned by `.[10:15]` will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive). Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array). The `.[2]` syntax can be used to return the element at the given index. Negative indices are allowed, with -1 referring to the last element, -2 referring to the next to last element, and so on. The `.foo` syntax only works for simple keys i.e. keys that are all alphanumeric characters. `.[]` works with keys that contain special characters such as colons and dots. For example `.["foo::bar"]` and `.["foo.bar"]` work while `.foo::bar` and `.foo.bar` would not. The `?` "operator" can also be used with the slice operator, as in `.[10:15]?`, which outputs values where the inputs are slice-able. examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['{"name":"JSON", "good":true}'] - program: '.[2]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] - program: '.[2:4]' input: '["a","b","c","d","e"]' output: ['["c", "d"]'] - program: '.[2:4]' input: '"abcdefghi"' output: ['"cd"'] - program: '.[:3]' input: '["a","b","c","d","e"]' output: ['["a", "b", "c"]'] - program: '.[-2:]' input: '["a","b","c","d","e"]' output: ['["d", "e"]'] - program: '.[-2]' input: '[1,2,3]' output: ['2'] - title: "`.[]`" body: | If you use the `.[index]` syntax, but omit the index entirely, it will return *all* of the elements of an array. Running `.[]` with the input `[1,2,3]` will produce the numbers as three separate results, rather than as a single array. You can also use this on an object, and it will return all the values of the object. examples: - program: '.[]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: - '{"name":"JSON", "good":true}' - '{"name":"XML", "good":false}' - program: '.[]' input: '[]' output: [] - program: '.[]' input: '{"a": 1, "b": 1}' output: ['1', '1'] - title: "`.[]?`" body: | Like `.[]`, but no errors will be output if . is not an array or object. - title: "`,`" body: | If two filters are separated by a comma, then the input will be fed into both and there will be multiple outputs: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right. For instance, filter `.foo, .bar`, produces both the "foo" fields and "bar" fields as separate outputs. examples: - program: '.foo, .bar' input: '{"foo": 42, "bar": "something else", "baz": true}' output: ['42', '"something else"'] - program: ".user, .projects[]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['"stedolan"', '"jq"', '"wikiflow"'] - program: '.[4,2]' input: '["a","b","c","d","e"]' output: ['"e"', '"c"'] - title: "`|`" body: | The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right. It's pretty much the same as the Unix shell's pipe, if you're used to that. If the one on the left produces multiple results, the one on the right will be run for each of those results. So, the expression `.[] | .foo` retrieves the "foo" field of each element of the input array. examples: - program: '.[] | .name' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['"JSON"', '"XML"'] - title: Types and Values body: | jq supports the same set of datatypes as JSON - numbers, strings, booleans, arrays, objects (which in JSON-speak are hashes with only string keys), and "null". Booleans, null, strings and numbers are written the same way as in javascript. Just like everything else in jq, these simple values take an input and produce an output - `42` is a valid jq expression that takes an input, ignores it, and returns 42 instead. entries: - title: Array construction - `[]` body: | As in JSON, `[]` is used to construct arrays, as in `[1,2,3]`. The elements of the arrays can be any jq expression. All of the results produced by all of the expressions are collected into one big array. You can use it to construct an array out of a known quantity of values (as in `[.foo, .bar, .baz]`) or to "collect" all the results of a filter into an array (as in `[.items[].name]`) Once you understand the "," operator, you can look at jq's array syntax in a different light: the expression `[1,2,3]` is not using a built-in syntax for comma-separated arrays, but is instead applying the `[]` operator (collect results) to the expression 1,2,3 (which produces three different results). If you have a filter `X` that produces four results, then the expression `[X]` will produce a single result, an array of four elements. examples: - program: "[.user, .projects[]]" input: '{"user":"stedolan", "projects": ["jq", "wikiflow"]}' output: ['["stedolan", "jq", "wikiflow"]'] - title: Objects - `{}` body: | Like JSON, `{}` is for constructing objects (aka dictionaries or hashes), as in: `{"a": 42, "b": 17}`. If the keys are "sensible" (all alphabetic characters), then the quotes can be left off. The value can be any expression (although you may need to wrap it in parentheses if it's a complicated one), which gets applied to the {} expression's input (remember, all filters have an input and an output). {foo: .bar} will produce the JSON object `{"foo": 42}` if given the JSON object `{"bar":42, "baz":43}`. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write {user: .user, title: .title} Because that's so common, there's a shortcut syntax: `{user, title}`. If one of the expressions produces multiple results, multiple dictionaries will be produced. If the input's {"user":"stedolan","titles":["JQ Primer", "More JQ"]} then the expression {user, title: .titles[]} will produce two outputs: {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} Putting parentheses around the key means it will be evaluated as an expression. With the same input as above, {(.user): .titles} produces {"stedolan": ["JQ Primer", "More JQ"]} examples: - program: '{user, title: .titles[]}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: - '{"user":"stedolan", "title": "JQ Primer"}' - '{"user":"stedolan", "title": "More JQ"}' - program: '{(.user): .titles}' input: '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' output: ['{"stedolan": ["JQ Primer", "More JQ"]}'] - title: Builtin operators and functions body: | Some jq operator (for instance, `+`) do different things depending on the type of their arguments (arrays, numbers, etc.). However, jq never does implicit type conversions. If you try to add a string to an object you'll get an error message and no result. entries: - title: Addition - `+` body: | The operator `+` takes two filters, applies them both to the same input, and adds the results together. What "adding" means depends on the types involved: - **Numbers** are added by normal arithmetic. - **Arrays** are added by being concatenated into a larger array. - **Strings** are added by being joined into a larger string. - **Objects** are added by merging, that is, inserting all the key-value pairs from both objects into a single combined object. If both objects contain a value for the same key, the object on the right of the `+` wins. (For recursive merge use the `*` operator.) `null` can be added to any value, and returns the other value unchanged. examples: - program: '.a + 1' input: '{"a": 7}' output: ['8'] - program: '.a + .b' input: '{"a": [1,2], "b": [3,4]}' output: ['[1,2,3,4]'] - program: '.a + null' input: '{"a": 1}' output: ['1'] - program: '.a + 1' input: '{}' output: ['1'] - program: '{a: 1} + {b: 2} + {c: 3} + {a: 42}' input: 'null' output: ['{"a": 42, "b": 2, "c": 3}'] - title: Subtraction - `-` body: | As well as normal arithmetic subtraction on numbers, the `-` operator can be used on arrays to remove all occurrences of the second array's elements from the first array. examples: - program: '4 - .a' input: '{"a":3}' output: ['1'] - program: . - ["xml", "yaml"] input: '["xml", "yaml", "json"]' output: ['["json"]'] - title: Multiplication, division, modulo - `*`, `/`, and `%` body: | These infix operators behave as expected when given two numbers. Division by zero raises an error. `x % y` computes x modulo y. Multiplying a string by a number produces the concatenation of that string that many times. `"x" * 0` produces **null**. Dividing a string by another splits the first using the second as separators. Multiplying two objects will merge them recursively: this works like addition but if both objects contain a value for the same key, and the values are objects, the two are merged with the same strategy. examples: - program: '10 / . * 3' input: 5 output: [6] - program: '. / ", "' input: '"a, b,c,d, e"' output: ['["a","b,c,d","e"]'] - program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}' input: 'null' output: ['{"k": {"a": 0, "b": 2, "c": 3}}'] - program: '.[] | (1 / .)?' input: '[1,0,-1]' output: ['1', '-1'] - title: "`length`" body: | The builtin function `length` gets the length of various different types of value: - The length of a **string** is the number of Unicode codepoints it contains (which will be the same as its JSON-encoded length in bytes if it's pure ASCII). - The length of an **array** is the number of elements. - The length of an **object** is the number of key-value pairs. - The length of **null** is zero. examples: - program: '.[] | length' input: '[[1,2], "string", {"a":2}, null]' output: [2, 6, 1, 0] - title: "`keys`, `keys_unsorted`" body: | The builtin function `keys`, when given an object, returns its keys in an array. The keys are sorted "alphabetically", by unicode codepoint order. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings. When `keys` is given an array, it returns the valid indices for that array: the integers from 0 to length-1. The `keys_unsorted` function is just like `keys`, but if the input is an object then the keys will not be sorted, instead the keys will roughly be in insertion order. examples: - program: 'keys' input: '{"abc": 1, "abcd": 2, "Foo": 3}' output: ['["Foo", "abc", "abcd"]'] - program: 'keys' input: '[42,3,35]' output: ['[0,1,2]'] - title: "`has(key)`" body: | The builtin function `has` returns whether the input object has the given key, or the input array has an element at the given index. `has($key)` has the same effect as checking whether `$key` is a member of the array returned by `keys`, although `has` will be faster. examples: - program: 'map(has("foo"))' input: '[{"foo": 42}, {}]' output: ['[true, false]'] - program: 'map(has(2))' input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] - title: "`in`" body: | The builtin function `in` returns whether or not the input key is in the given object, or the input index corresponds to an element in the given array. It is, essentially, an inversed version of `has`. examples: - program: '.[] | in({"foo": 42})' input: '["foo", "bar"]' output: ['true', 'false'] - program: 'map(in([0,1]))' input: '[2, 0]' output: ['[false, true]'] - title: "`path(path_expression)`" body: | Outputs array representations of the given path expression in `.`. The outputs are arrays of strings (object keys) and/or numbers (array indices). Path expressions are jq expressions like `.a`, but also `.[]`. There are two types of path expressions: ones that can match exactly, and ones that cannot. For example, `.a.b.c` is an exact match path expression, while `.a[].b` is not. `path(exact_path_expression)` will produce the array representation of the path expression even if it does not exist in `.`, if `.` is `null` or an array or an object. `path(pattern)` will produce array representations of the paths matching `pattern` if the paths exist in `.`. Note that the path expressions are not different from normal expressions. The expression `path(..|select(type=="boolean"))` outputs all the paths to boolean values in `.`, and only those paths. examples: - program: 'path(.a[0].b)' input: 'null' output: ['["a",0,"b"]'] - program: '[path(..)]' input: '{"a":[{"b":1}]}' output: ['[[],["a"],["a",0],["a",0,"b"]]'] - title: "`del(path_expression)`" body: | The builtin function `del` removes a key and its corresponding value from an object. examples: - program: 'del(.foo)' input: '{"foo": 42, "bar": 9001, "baz": 42}' output: ['{"bar": 9001, "baz": 42}'] - program: 'del(.[1, 2])' input: '["foo", "bar", "baz"]' output: ['["foo"]'] - title: "`to_entries`, `from_entries`, `with_entries`" body: | These functions convert between an object and an array of key-value pairs. If `to_entries` is passed an object, then for each `k: v` entry in the input, the output array includes `{"key": k, "value": v}`. `from_entries` does the opposite conversion, and `with_entries(foo)` is a shorthand for `to_entries | map(foo) | from_entries`, useful for doing some operation to all keys and values of an object. `from_entries` accepts key, Key, Name, value and Value as keys. examples: - program: 'to_entries' input: '{"a": 1, "b": 2}' output: ['[{"key":"a", "value":1}, {"key":"b", "value":2}]'] - program: 'from_entries' input: '[{"key":"a", "value":1}, {"key":"b", "value":2}]' output: ['{"a": 1, "b": 2}'] - program: 'with_entries(.key |= "KEY_" + .)' input: '{"a": 1, "b": 2}' output: ['{"KEY_a": 1, "KEY_b": 2}'] - title: "`select(boolean_expression)`" body: | The function `select(foo)` produces its input unchanged if `foo` returns true for that input, and produces no output otherwise. It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))` will give you `[2,3]`. examples: - program: 'map(select(. >= 2))' input: '[1,5,3,0,7]' output: ['[5,3,7]'] - program: '.[] | select(.id == "second")' input: '[{"id": "first", "val": 1}, {"id": "second", "val": 2}]' output: ['{"id": "second", "val": 2}'] - title: "`arrays`, `objects`, `iterables`, `booleans`, `numbers`, `normals`, `finites`, `strings`, `nulls`, `values`, `scalars`" body: | These built-ins select only inputs that are arrays, objects, iterables (arrays or objects), booleans, numbers, normal numbers, finite numbers, strings, null, non-null values, and non-iterables, respectively. examples: - program: '.[]|numbers' input: '[[],{},1,"foo",null,true,false]' output: ['1'] - title: "`empty`" body: | `empty` returns no results. None at all. Not even `null`. It's useful on occasion. You'll know if you need it :) examples: - program: '1, empty, 2' input: 'null' output: [1, 2] - program: '[1,2,empty,3]' input: 'null' output: ['[1,2,3]'] - title: "`error(message)`" body: | Produces an error, just like `.a` applied to values other than null and objects would, but with the given message as the error's value. - title: "`$__loc__`" body: | Produces an object with a "file" key and a "line" key, with the filename and line number where `$__loc__` occurs, as values. examples: - program: 'try error("\($__loc__)") catch .' input: 'null' output: ['"{\"file\":\"\",\"line\":1}"'] - title: "`map(x)`, `map_values(x)`" body: | For any filter `x`, `map(x)` will run that filter for each element of the input array, and return the outputs in a new array. `map(.+1)` will increment each element of an array of numbers. Similarly, `map_values(x)` will run that filter for each element, but it will return an object when an object is passed. `map(x)` is equivalent to `[.[] | x]`. In fact, this is how it's defined. Similarly, `map_values(x)` is defined as `.[] |= x`. examples: - program: 'map(.+1)' input: '[1,2,3]' output: ['[2,3,4]'] - program: 'map_values(.+1)' input: '{"a": 1, "b": 2, "c": 3}' output: ['{"a": 2, "b": 3, "c": 4}'] - title: "`paths`, `paths(node_filter)`, `leaf_paths`" body: | `paths` outputs the paths to all the elements in its input (except it does not output the empty list, representing . itself). `paths(f)` outputs the paths to any values for which `f` is true. That is, `paths(numbers)` outputs the paths to all numeric values. `leaf_paths` is an alias of `paths(scalars)`; `leaf_paths` is *deprecated* and will be removed in the next major release. examples: - program: '[paths]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1],[1,0],[1,1],[1,1,"a"]]'] - program: '[paths(scalars)]' input: '[1,[[],{"a":2}]]' output: ['[[0],[1,1,"a"]]'] - title: "`add`" body: | The filter `add` takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the `+` operator (described above). If the input is an empty array, `add` returns `null`. examples: - program: add input: '["a","b","c"]' output: ['"abc"'] - program: add input: '[1, 2, 3]' output: [6] - program: add input: '[]' output: ["null"] - title: "`any`, `any(condition)`, `any(generator; condition)`" body: | The filter `any` takes as input an array of boolean values, and produces `true` as output if any of the elements of the array are `true`. If the input is an empty array, `any` returns `false`. The `any(condition)` form applies the given condition to the elements of the input array. The `any(generator; condition)` form applies the given condition to all the outputs of the given generator. examples: - program: any input: '[true, false]' output: ["true"] - program: any input: '[false, false]' output: ["false"] - program: any input: '[]' output: ["false"] - title: "`all`, `all(condition)`, `all(generator; condition)`" body: | The filter `all` takes as input an array of boolean values, and produces `true` as output if all of the elements of the array are `true`. The `all(condition)` form applies the given condition to the elements of the input array. The `all(generator; condition)` form applies the given condition to all the outputs of the given generator. If the input is an empty array, `all` returns `true`. examples: - program: all input: '[true, false]' output: ["false"] - program: all input: '[true, true]' output: ["true"] - program: all input: '[]' output: ["true"] - title: "`flatten`, `flatten(depth)`" body: | The filter `flatten` takes as input an array of nested arrays, and produces a flat array in which all arrays inside the original array have been recursively replaced by their values. You can pass an argument to it to specify how many levels of nesting to flatten. `flatten(2)` is like `flatten`, but going only up to two levels deep. examples: - program: flatten input: '[1, [2], [[3]]]' output: ["[1, 2, 3]"] - program: flatten(1) input: '[1, [2], [[3]]]' output: ["[1, 2, [3]]"] - program: flatten input: '[[]]' output: ["[]"] - program: flatten input: '[{"foo": "bar"}, [{"foo": "baz"}]]' output: ['[{"foo": "bar"}, {"foo": "baz"}]'] - title: "`range(upto)`, `range(from;upto)` `range(from;upto;by)`" body: | The `range` function produces a range of numbers. `range(4;10)` produces 6 numbers, from 4 (inclusive) to 10 (exclusive). The numbers are produced as separate outputs. Use `[range(4;10)]` to get a range as an array. The one argument form generates numbers from 0 to the given number, with an increment of 1. The two argument form generates numbers from `from` to `upto` with an increment of 1. The three argument form generates numbers `from` to `upto` with an increment of `by`. examples: - program: 'range(2;4)' input: 'null' output: ['2', '3'] - program: '[range(2;4)]' input: 'null' output: ['[2,3]'] - program: '[range(4)]' input: 'null' output: ['[0,1,2,3]'] - program: '[range(0;10;3)]' input: 'null' output: ['[0,3,6,9]'] - program: '[range(0;10;-1)]' input: 'null' output: ['[]'] - program: '[range(0;-5;-1)]' input: 'null' output: ['[0,-1,-2,-3,-4]'] - title: "`floor`" body: | The `floor` function returns the floor of its numeric input. examples: - program: 'floor' input: '3.14159' output: ['3'] - title: "`sqrt`" body: | The `sqrt` function returns the square root of its numeric input. examples: - program: 'sqrt' input: '9' output: ['3'] - title: "`tonumber`" body: | The `tonumber` function parses its input as a number. It will convert correctly-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input. examples: - program: '.[] | tonumber' input: '[1, "1"]' output: [1, 1] - title: "`tostring`" body: | The `tostring` function prints its input as a string. Strings are left unchanged, and all other values are JSON-encoded. examples: - program: '.[] | tostring' input: '[1, "1", [1]]' output: ['"1"', '"1"', '"[1]"'] - title: "`type`" body: | The `type` function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object. examples: - program: 'map(type)' input: '[0, false, [], {}, null, "hello"]' output: ['["number", "boolean", "array", "object", "null", "string"]'] - title: "`infinite`, `nan`, `isinfinite`, `isnan`, `isfinite`, `isnormal`" body: | Some arithmetic operations can yield infinities and "not a number" (NaN) values. The `isinfinite` builtin returns `true` if its input is infinite. The `isnan` builtin returns `true` if its input is a NaN. The `infinite` builtin returns a positive infinite value. The `nan` builtin returns a NaN. The `isnormal` builtin returns true if its input is a normal number. Note that division by zero raises an error. Currently most arithmetic operations operating on infinities, NaNs, and sub-normals do not raise errors. examples: - program: '.[] | (infinite * .) < 0' input: '[-1, 1]' output: ['true', 'false'] - program: 'infinite, nan | type' input: 'null' output: ['"number"', '"number"'] - title: "`sort, sort_by(path_expression)`" body: | The `sort` functions sorts its input, which must be an array. Values are sorted in the following order: * `null` * `false` * `true` * numbers * strings, in alphabetical order (by unicode codepoint value) * arrays, in lexical order * objects The ordering for objects is a little complex: first they're compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key. `sort` may be used to sort by a particular field of an object, or by applying any jq filter. `sort_by(foo)` compares two elements by comparing the result of `foo` on each element. examples: - program: 'sort' input: '[8,3,null,6]' output: ['[null,3,6,8]'] - program: 'sort_by(.foo)' input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]' output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]'] - title: "`group_by(path_expression)`" body: | `group_by(.foo)` takes as input an array, groups the elements having the same `.foo` field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the `.foo` field. Any jq expression, not just a field access, may be used in place of `.foo`. The sorting order is the same as described in the `sort` function above. examples: - program: 'group_by(.foo)' input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]'] - title: "`min`, `max`, `min_by(path_exp)`, `max_by(path_exp)`" body: | Find the minimum or maximum element of the input array. The `min_by(path_exp)` and `max_by(path_exp)` functions allow you to specify a particular field or property to examine, e.g. `min_by(.foo)` finds the object with the smallest `foo` field. examples: - program: 'min' input: '[5,4,2,7]' output: ['2'] - program: 'max_by(.foo)' input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' output: ['{"foo":2, "bar":3}'] - title: "`unique`, `unique_by(path_exp)`" body: | The `unique` function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed. The `unique_by(path_exp)` function will keep only one element for each value obtained by applying the argument. Think of it as making an array by taking one element out of every group produced by `group`. examples: - program: 'unique' input: '[1,2,5,3,5,3,1,3]' output: ['[1,2,3,5]'] - program: 'unique_by(.foo)' input: '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' output: ['[{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]'] - program: 'unique_by(length)' input: '["chunky", "bacon", "kitten", "cicada", "asparagus"]' output: ['["bacon", "chunky", "asparagus"]'] - title: "`reverse`" body: | This function reverses an array. examples: - program: 'reverse' input: '[1,2,3,4]' output: ['[4,3,2,1]'] - title: "`contains(element)`" body: | The filter `contains(b)` will produce true if b is completely contained within the input. A string B is contained in a string A if B is a substring of A. An array B is contained in an array A if all elements in B are contained in any element in A. An object B is contained in object A if all of the values in B are contained in the value in A with the same key. All other types are assumed to be contained in each other if they are equal. examples: - program: 'contains("bar")' input: '"foobar"' output: ['true'] - program: 'contains(["baz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['true'] - program: 'contains(["bazzzzz", "bar"])' input: '["foobar", "foobaz", "blarp"]' output: ['false'] - program: 'contains({foo: 12, bar: [{barp: 12}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['true'] - program: 'contains({foo: 12, bar: [{barp: 15}]})' input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' output: ['false'] - title: "`indices(s)`" body: | Outputs an array containing the indices in `.` where `s` occurs. The input may be an array, in which case if `s` is an array then the indices output will be those where all elements in `.` match those of `s`. examples: - program: 'indices(", ")' input: '"a,b, cd, efg, hijk"' output: ['[3,7,12]'] - program: 'indices(1)' input: '[0,1,2,1,3,1,4]' output: ['[1,3,5]'] - program: 'indices([1,2])' input: '[0,1,2,3,1,4,2,5,1,2,6,7]' output: ['[1,8]'] - title: "`index(s)`, `rindex(s)`" body: | Outputs the index of the first (`index`) or last (`rindex`) occurrence of `s` in the input. examples: - program: 'index(", ")' input: '"a,b, cd, efg, hijk"' output: ['3'] - program: 'rindex(", ")' input: '"a,b, cd, efg, hijk"' output: ['12'] - title: "`inside`" body: | The filter `inside(b)` will produce true if the input is completely contained within b. It is, essentially, an inversed version of `contains`. examples: - program: 'inside("foobar")' input: '"bar"' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["baz", "bar"]' output: ['true'] - program: 'inside(["foobar", "foobaz", "blarp"])' input: '["bazzzzz", "bar"]' output: ['false'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 12}]}' output: ['true'] - program: 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})' input: '{"foo": 12, "bar": [{"barp": 15}]}' output: ['false'] - title: "`startswith(str)`" body: | Outputs `true` if . starts with the given string argument. examples: - program: '[.[]|startswith("foo")]' input: '["fo", "foo", "barfoo", "foobar", "barfoob"]' output: ['[false, true, false, true, false]'] - title: "`endswith(str)`" body: | Outputs `true` if . ends with the given string argument. examples: - program: '[.[]|endswith("foo")]' input: '["foobar", "barfoo"]' output: ['[false, true]'] - title: "`combinations`, `combinations(n)`" body: | Outputs all combinations of the elements of the arrays in the input array. If given an argument `n`, it outputs all combinations of `n` repetitions of the input array. examples: - program: 'combinations' input: '[[1,2], [3, 4]]' output: ['[1, 3]', '[1, 4]', '[2, 3]', '[2, 4]'] - program: 'combinations(2)' input: '[0, 1]' output: ['[0, 0]', '[0, 1]', '[1, 0]', '[1, 1]'] - title: "`ltrimstr(str)`" body: | Outputs its input with the given prefix string removed, if it starts with it. examples: - program: '[.[]|ltrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "afoo"]' output: ['["fo","","barfoo","bar","afoo"]'] - title: "`rtrimstr(str)`" body: | Outputs its input with the given suffix string removed, if it ends with it. examples: - program: '[.[]|rtrimstr("foo")]' input: '["fo", "foo", "barfoo", "foobar", "foob"]' output: ['["fo","","bar","foobar","foob"]'] - title: "`explode`" body: | Converts an input string into an array of the string's codepoint numbers. examples: - program: 'explode' input: '"foobar"' output: ['[102,111,111,98,97,114]'] - title: "`implode`" body: | The inverse of explode. examples: - program: 'implode' input: '[65, 66, 67]' output: ['"ABC"'] - title: "`split`" body: | Splits an input string on the separator argument. examples: - program: 'split(", ")' input: '"a, b,c,d, e, "' output: ['["a","b,c,d","e",""]'] - title: "`join(str)`" body: | Joins the array of elements given as input, using the argument as separator. It is the inverse of `split`: that is, running `split("foo") | join("foo")` over any input string returns said input string. examples: - program: 'join(", ")' input: '["a","b,c,d","e"]' output: ['"a, b,c,d, e"'] - title: "`ascii_downcase`, `ascii_upcase`" body: | Emit a copy of the input string with its alphabetic characters (a-z and A-Z) converted to the specified case. example: - program: 'ascii_upcase' input: '"useful but not for é"' output: '"USEFUL BUT NOT FOR é"' - title: "`while(cond; update)`" body: | The `while(cond; update)` function allows you to repeatedly apply an update to `.` until `cond` is false. Note that `while(cond; update)` is internally defined as a recursive jq function. Recursive calls within `while` will not consume additional memory if `update` produces at most one output for each input. See advanced topics below. examples: - program: '[while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: "`until(cond; next)`" body: | The `until(cond; next)` function allows you to repeatedly apply the expression `next`, initially to `.` then to its own output, until `cond` is true. For example, this can be used to implement a factorial function (see below). Note that `until(cond; next)` is internally defined as a recursive jq function. Recursive calls within `until()` will not consume additional memory if `next` produces at most one output for each input. See advanced topics below. examples: - program: '[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]' input: '4' output: ['24'] - title: "`recurse(f)`, `recurse`, `recurse(f; condition)`, `recurse_down`" body: | The `recurse(f)` function allows you to search through a recursive structure, and extract interesting data from all levels. Suppose your input represents a filesystem: {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} Now suppose you want to extract all of the filenames present. You need to retrieve `.name`, `.children[].name`, `.children[].children[].name`, and so on. You can do this with: recurse(.children[]) | .name When called without an argument, `recurse` is equivalent to `recurse(.[]?)`. `recurse(f)` is identical to `recurse(f; . != null)` and can be used without concerns about recursion depth. `recurse(f; condition)` is a generator which begins by emitting . and then emits in turn .|f, .|f|f, .|f|f|f, ... so long as the computed value satisfies the condition. For example, to generate all the integers, at least in principle, one could write `recurse(.+1; true)`. For legacy reasons, `recurse_down` exists as an alias to calling `recurse` without arguments. This alias is considered *deprecated* and will be removed in the next major release. The recursive calls in `recurse` will not consume additional memory whenever `f` produces at most a single output for each input. examples: - program: 'recurse(.foo[])' input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}' output: - '{"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}' - '{"foo":[]}' - '{"foo":[{"foo":[]}]}' - '{"foo":[]}' - program: 'recurse' input: '{"a":0,"b":[1]}' output: - '{"a":0,"b":[1]}' - '0' - '[1]' - '1' - program: 'recurse(. * .; . < 20)' input: 2 output: - 2 - 4 - 16 - title: "`..`" body: | Short-hand for `recurse` without arguments. This is intended to resemble the XPath `//` operator. Note that `..a` does not work; use `..|a` instead. In the example below we use `..|.a?` to find all the values of object keys "a" in any object found "below" `.`. examples: - program: '..|.a?' input: '[[{"a":1}]]' output: ['1'] - title: "`env`" body: | Outputs an object representing jq's environment. examples: - program: 'env.PAGER' input: 'null' output: ['"less"'] - title: "`transpose`" body: | Transpose a possibly jagged matrix (an array of arrays). Rows are padded with nulls so the result is always rectangular. examples: - program: 'transpose' input: '[[1], [2,3]]' output: ['[[1,2],[null,3]]'] - title: "`bsearch(x)`" body: | bsearch(x) conducts a binary search for x in the input array. If the input is sorted and contains x, then bsearch(x) will return its index in the array; otherwise, if the array is sorted, it will return (-1 - ix) where ix is an insertion point such that the array would still be sorted after the insertion of x at ix. If the array is not sorted, bsearch(x) will return an integer that is probably of no interest. examples: - program: 'bsearch(0)' input: '[0,1]' output: ['0'] - program: 'bsearch(0)' input: '[1,2,3]' output: ['-1'] - program: 'bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end' input: '[1,2,3]' output: ['[1,2,3,4]'] - title: "String interpolation - `\\(foo)`" body: | Inside a string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string. examples: - program: '"The input was \(.), which is one less than \(.+1)"' input: '42' output: ['"The input was 42, which is one less than 43"'] - title: "Convert to/from JSON" body: | The `tojson` and `fromjson` builtins dump values as JSON texts or parse JSON texts into values, respectively. The tojson builtin differs from tostring in that tostring returns strings unmodified, while tojson encodes strings as JSON strings. examples: - program: '[.[]|tostring]' input: '[1, "foo", ["foo"]]' output: ['["1","foo","[\"foo\"]"]'] - program: '[.[]|tojson]' input: '[1, "foo", ["foo"]]' output: ['["1","\"foo\"","[\"foo\"]"]'] - program: '[.[]|tojson|fromjson]' input: '[1, "foo", ["foo"]]' output: ['[1,"foo",["foo"]]'] - title: "Format strings and escaping" body: | The `@foo` syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth. `@foo` can be used as a filter on its own, the possible escapings are: * `@text`: Calls `tostring`, see that function for details. * `@json`: Serializes the input as JSON. * `@html`: Applies HTML/XML escaping, by mapping the characters `<>&'"` to their entity equivalents `<`, `>`, `&`, `'`, `"`. * `@uri`: Applies percent-encoding, by mapping all reserved URI characters to a `%XX` sequence. * `@csv`: The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition. * `@tsv`: The input must be an array, and it is rendered as TSV (tab-separated values). Each input array will be printed as a single line. Fields are separated by a single tab (ascii `0x09`). Input characters line-feed (ascii `0x0a`), carriage-return (ascii `0x0d`), tab (ascii `0x09`) and backslash (ascii `0x5c`) will be output as escape sequences `\n`, `\r`, `\t`, `\\` respectively. * `@sh`: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings. * `@base64`: The input is converted to base64 as specified by RFC 4648. This syntax can be combined with string interpolation in a useful way. You can follow a `@foo` token with a string literal. The contents of the string literal will *not* be escaped. However, all interpolations made inside that string literal will be escaped. For instance, @uri "https://www.google.com/search?q=\(.search)" will produce the following output for the input `{"search":"what is jq?"}`: "https://www.google.com/search?q=what%20is%20jq%3F" Note that the slashes, question mark, etc. in the URL are not escaped, as they were part of the string literal. examples: - program: '@html' input: '"This works if x < y"' output: ['"This works if x < y"'] # - program: '@html "Anonymous said: \(.)"' # input: '""' # output: ["Anonymous said: <script>alert("lol hax");</script>"] - program: '@sh "echo \(.)"' input: "\"O'Hara's Ale\"" output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""] - title: "Dates" body: | jq provides some basic date handling functionality, with some high-level and low-level builtins. In all cases these builtins deal exclusively with time in UTC. The `fromdateiso8601` builtin parses datetimes in the ISO 8601 format to a number of seconds since the Unix epoch (1970-01-01T00:00:00Z). The `todateiso8601` builtin does the inverse. The `fromdate` builtin parses datetime strings. Currently `fromdate` only supports ISO 8601 datetime strings, but in the future it will attempt to parse datetime strings in more formats. The `todate` builtin is an alias for `todateiso8601`. The `now` builtin outputs the current time, in seconds since the Unix epoch. Low-level jq interfaces to the C-library time functions are also provided: `strptime`, `strftime`, `mktime`, and `gmtime`. Refer to your host operating system's documentation for the format strings used by `strptime` and `strftime`. Note: these are not necessarily stable interfaces in jq, particularly as to their localization functionality. The `gmtime` builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of time as an array of numbers representing (in this order): the year, the month (zero-based), the day of the month, the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year -- all one-based unless otherwise stated. The `mktime` builtin consumes "broken down time" representations of time output by `gmtime` and `strptime`. The `strptime(fmt)` builtin parses input strings matching the `fmt` argument. The output is in the "broken down time" representation consumed by `gmtime` and output by `mktime`. The `strftime(fmt)` builtin formats a time with the given format. The format strings for `strptime` and `strftime` are described in typical C library documentation. The format string for ISO 8601 datetime is `"%Y-%m-%dT%H:%M:%SZ"`. jq may not support some or all of this date functionality on some systems. examples: - program: 'fromdate' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")' input: '"2015-03-05T23:51:47Z"' output: ['[2015,2,5,23,51,47,4,63]'] - program: 'strptime("%Y-%m-%dT%H:%M:%SZ")|mktime' input: '"2015-03-05T23:51:47Z"' output: ['1425599507'] - title: Conditionals and Comparisons entries: - title: "`==`, `!=`" body: | The expression 'a == b' will produce 'true' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and 'false' otherwise. In particular, strings are never considered equal to numbers. If you're coming from Javascript, jq's == is like Javascript's === - considering values equal only when they have the same type as well as the same value. != is "not equal", and 'a != b' returns the opposite value of 'a == b' examples: - program: '.[] == 1' input: '[1, 1.0, "1", "banana"]' output: ['true', 'true', 'false', 'false'] - title: if-then-else body: | `if A then B else C end` will act the same as `B` if `A` produces a value other than false or null, but act the same as `C` otherwise. Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you'll sometimes have to be more explicit about the condition you want: you can't test whether, e.g. a string is empty using `if .name then A else B end`, you'll need something more like `if (.name | length) > 0 then A else B end` instead. If the condition `A` produces multiple results, then `B` is evaluated once for each result that is not false or null, and `C` is evaluated once for each false or null. More cases can be added to an if using `elif A then B` syntax. examples: - program: |- if . == 0 then "zero" elif . == 1 then "one" else "many" end input: 2 output: ['"many"'] - title: "`>, >=, <=, <`" body: | The comparison operators `>`, `>=`, `<=`, `<` return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively). The ordering is the same as that described for `sort`, above. examples: - program: '. < 5' input: 2 output: ['true'] - title: and/or/not body: | jq supports the normal Boolean operators and/or/not. They have the same standard of truth as if expressions - false and null are considered "false values", and anything else is a "true value". If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input. `not` is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in `.foo and .bar | not`. These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default". If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below. examples: - program: '42 and "a string"' input: 'null' output: ['true'] - program: '(true, false) or false' input: 'null' output: ['true', 'false'] # - program: '(true, false) and (true, false)' # input: 'null' # output: ['true', 'false', 'false', 'false'] - program: '(true, true) and (true, false)' input: 'null' output: ['true', 'false', 'true', 'false'] - program: '[true, false | not]' input: 'null' output: ['[false, true]'] - title: Alternative operator - `//` body: | A filter of the form `a // b` produces the same results as `a`, if `a` produces results other than `false` and `null`. Otherwise, `a // b` produces the same results as `b`. This is useful for providing defaults: `.foo // 1` will evaluate to `1` if there's no `.foo` element in the input. It's similar to how `or` is sometimes used in Python (jq's `or` operator is reserved for strictly Boolean operations). examples: - program: '.foo // 42' input: '{"foo": 19}' output: [19] - program: '.foo // 42' input: '{}' output: [42] - title: try-catch body: | Errors can be caught by using `try EXP catch EXP`. The first expression is executed, and if it fails then the second is executed with the error message. The output of the handler, if any, is output as if it had been the output of the expression to try. The `try EXP` form uses `empty` as the exception handler. examples: - program: 'try .a catch ". is not an object"' input: 'true' output: ['". is not an object"'] - program: '[.[]|try .a]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - program: 'try error("some exception") catch .' input: 'true' output: ['"some exception"'] - title: Breaking out of control structures body: | A convenient use of try/catch is to break out of control structures like `reduce`, `foreach`, `while`, and so on. For example: # Repeat an expression until it raises "break" as an # error, then stop repeating without re-raising the error. # But if the error caught is not "break" then re-raise it. try repeat(exp) catch .=="break" then empty else error; jq has a syntax for named lexical labels to "break" or "go (back) to": label $out | ... break $out ... The `break $label_name` expression will cause the program to to act as though the nearest (to the left) `label $label_name` produced `empty`. The relationship between the `break` and corresponding `label` is lexical: the label has to be "visible" from the break. To break out of a `reduce`, for example: label $out | reduce .[] as $item (null; if .==false then break $out else ... end) The following jq program produces a syntax error: break $out because no label `$out` is visible. - title: "`?` operator" body: | The `?` operator, used as `EXP?`, is shorthand for `try EXP`. examples: - program: '[.[]|(.a)?]' input: '[{}, true, {"a":1}]' output: ['[null, 1]'] - title: Regular expressions (PCRE) body: | jq uses the Oniguruma regular expression library, as do php, ruby, TextMate, Sublime Text, etc, so the description here will focus on jq specifics. The jq regex filters are defined so that they can be used using one of these patterns: STRING | FILTER( REGEX ) STRING | FILTER( REGEX; FLAGS ) STRING | FILTER( [REGEX] ) STRING | FILTER( [REGEX, FLAGS] ) where: * STRING, REGEX and FLAGS are jq strings and subject to jq string interpolation; * REGEX, after string interpolation, should be a valid PCRE regex; * FILTER is one of `test`, `match`, or `capture`, as described below. FLAGS is a string consisting of one of more of the supported flags: * `g` - Global search (find all matches, not just the first) * `i` - Case insensitive search * `m` - Multi line mode ('.' will match newlines) * `n` - Ignore empty matches * `p` - Both s and m modes are enabled * `s` - Single line mode ('^' -> '\A', '$' -> '\Z') * `l` - Find longest possible matches * `x` - Extended regex format (ignore whitespace and comments) To match whitespace in an x pattern use an escape such as \s, e.g. * test( "a\\sb", "x" ). Note that certain flags may also be specified within REGEX, e.g. * jq -n '("test", "TEst", "teST", "TEST") | test( "(?i)te(?-i)st" )' evaluates to: true, true, false, false. entries: - title: "`test(val)`, `test(regex; flags)`" body: | Like `match`, but does not return match objects, only `true` or `false` for whether or not the regex matches the input. examples: - program: 'test("foo")' input: '"foo"' output: ['true'] - program: '.[] | test("a b c # spaces are ignored"; "ix")' input: '["xabcd", "ABC"]' output: ['true', 'true'] - title: "`match(val)`, `match(regex; flags)`" body: | **match** outputs an object for each match it finds. Matches have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of the match * `string` - the string that it matched * `captures` - an array of objects representing capturing groups. Capturing group objects have the following fields: * `offset` - offset in UTF-8 codepoints from the beginning of the input * `length` - length in UTF-8 codepoints of this capturing group * `string` - the string that was captured * `name` - the name of the capturing group (or `null` if it was unnamed) Capturing groups that did not match anything return an offset of -1 examples: - program: 'match("(abc)+"; "g")' input: '"abc abc"' output: - '{"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}' - '{"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]}' - program: 'match("foo")' input: '"foo bar foo"' output: ['{"offset": 0, "length": 3, "string": "foo", "captures": []}'] - program: 'match(["foo", "ig"])' input: '"foo bar FOO"' output: - '{"offset": 0, "length": 3, "string": "foo", "captures": []}' - '{"offset": 8, "length": 3, "string": "FOO", "captures": []}' - program: 'match("foo (?bar)? foo"; "ig")' input: '"foo bar foo foo foo"' output: - '{"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}' - '{"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}' - program: '[ match("."; "g")] | length' input: '"abc"' output: [3] - title: "`capture(val)`, `capture(regex; flags)`" body: | Collects the named captures in a JSON object, with the name of each capture as the key, and the matched string as the corresponding value. examples: - program: 'capture("(?[a-z]+)-(?[0-9]+)")' input: '"xyzzy-14"' output: ['{ "a": "xyzzy", "n": "14" }'] - title: "`scan(regex)`, `scan(regex; flags)`" body: | Emit a stream of the non-overlapping substrings of the input that match the regex in accordance with the flags, if any have been specified. If there is no match, the stream is empty. To capture all the matches for each input string, use the idiom `[ expr ]`, e.g. `[ scan(regex) ]`. example: - program: 'scan("c")' input: '"abcdefabc"' output: ['"c"', '"c"'] - program: 'scan("b")' input: ("", "") output: ['[]', '[]'] - title: "`split(regex; flags)`" body: | For backwards compatibility, `split` splits on a string, not a regex. example: - program: 'split(", *"; null)' input: '"ab,cd, ef"' output: ['"ab","cd","ef"'] - title: "`splits(regex)`, `splits(regex; flags)`" body: | These provide the same results as their `split` counterparts, but as a stream instead of an array. example: - program: 'splits(", *")' input: '("ab,cd", "ef, gh")' output: ['"ab"', '"cd"', '"ef"', '"gh"'] - title: "`sub(regex; tostring)` `sub(regex; string; flags)`" body: | Emit the string obtained by replacing the first match of regex in the input string with `tostring`, after interpolation. `tostring` should be a jq string, and may contain references to named captures. The named captures are, in effect, presented as a JSON object (as constructed by `capture`) to `tostring`, so a reference to a captured variable named "x" would take the form: "\(.x)". example: - program: 'sub("^[^a-z]*(?[a-z]*).*")' input: '"123abc456"' output: '"ZabcZabc"' - title: "`gsub(regex; string)`, `gsub(regex; string; flags)`" body: | `gsub` is like `sub` but all the non-overlapping occurrences of the regex are replaced by the string, after interpolation. example: - program: 'gsub("(?.)[^a]*"; "+\(.x)-")' input: '"Abcabc"' output: '"+A-+a-"' - title: Advanced features body: | Variables are an absolute necessity in most programming languages, but they're relegated to an "advanced feature" in jq. In most languages, variables are the only means of passing around data. If you calculate a value, and you want to use it more than once, you'll need to store it in a variable. To pass a value to another part of the program, you'll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data. It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq's standard library (many jq functions such as `map` and `find` are in fact written in jq). jq has reduction operators, which are very powerful but a bit tricky. Again, these are mostly used internally, to define some useful bits of jq's standard library. It may not be obvious at first, but jq is all about generators (yes, as often found in other languages). Some utilities are provided to help deal with generators. Some minimal I/O support (besides reading JSON from standard input, and writing JSON to standard output) is available. Finally, there is a module/library system. entries: - title: Variables body: | In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next. Many expressions, for instance `a + b`, pass their input to two distinct subexpressions (here `a` and `b` are both passed the same input), so variables aren't usually necessary in order to use a value twice. For instance, calculating the average value of an array of numbers requires a few variables in most languages - at least one to hold the array, perhaps one for each element or for a loop counter. In jq, it's simply `add / length` - the `add` expression is given the array and produces its sum, and the `length` expression is given the array and produces its length. So, there's generally a cleaner way to solve most problems in jq than defining variables. Still, sometimes they do make things easier, so jq lets you define variables using `expression as $variable`. All variable names start with `$`. Here's a slightly uglier version of the array-averaging example: length as $array_length | add / $array_length We'll need a more complicated problem to find a situation where using variables actually makes our lives easier. Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names. Our input looks like: {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} We want to produce the posts with the author field containing a real name, as in: {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"} We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: .realnames as $names | .posts[] | {title, author: $names[.author]} The expression `exp as $x | ...` means: for each value of expression `exp`, run the rest of the pipeline with the entire original input, and with `$x` set to that value. Thus `as` functions as something of a foreach loop. Just as `{foo}` is a handy way of writing `{foo: .foo}`, so `{$foo}` is a handy way of writing `{foo:$foo}`. Multiple variables may be declared using a single `as` expression by providing a pattern that matches the structure of the input (this is known as "destructuring"): . as {realnames: $names, posts: [$first, $second]} | ... The variable declarations in array patterns (e.g., `. as [$first, $second]`) bind to the elements of the array in from the element at index zero on up, in order. When there is no value at the index for an array pattern element, `null` is bound to that variable. Variables are scoped over the rest of the expression that defines them, so .realnames as $names | (.posts[] | {title, author: $names[.author]}) will work, but (.realnames as $names | .posts[]) | {title, author: $names[.author]} won't. For programming language theorists, it's more accurate to say that jq variables are lexically-scoped bindings. In particular there's no way to change the value of a binding; one can only setup a new binding with the same name, but which will not be visible where the old one was. examples: - program: '.bar as $x | .foo | . + $x' input: '{"foo":10, "bar":200}' output: ['210'] - program: '. as $i|[(.*2|. as $i| $i), $i]' input: '5' output: ['[10,5]'] - program: '. as [$a, $b, {c: $c}] | $a + $b + $c' input: '[2, 3, {"c": 4, "d": 5}]' output: ['9'] - program: '.[] as [$a, $b] | {a: $a, b: $b}' input: '[[0], [0, 1], [2, 1, 0]]' output: ['{"a":0,"b":null}', '{"a":0,"b":1}', '{"a":2,"b":1}'] - title: 'Defining Functions' body: | You can give a filter a name using "def" syntax: def increment: . + 1; From then on, `increment` is usable as a filter just like a builtin function (in fact, this is how some of the builtins are defined). A function may take arguments: def map(f): [.[] | f]; Arguments are passed as filters, not as values. The same argument may be referenced multiple times with different inputs (here `f` is run for each element of the input array). Arguments to a function work more like callbacks than like value arguments. This is important to understand. Consider: def foo(f): f|f; 5|foo(.*2) The result will be 20 because `f` is `.*2`, and during the first invocation of `f` `.` will be 5, and the second time it will be 10 (5 * 2), so the result will be 20. Function arguments are filters, and filters expect an input when invoked. If you want the value-argument behaviour for defining simple functions, you can just use a variable: def addvalue(f): f as $f | map(. + $f); Or use the short-hand: def addvalue($f): ...; With either definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. Multiple definitions using the same function name are allowed. Each re-definition replaces the previous one for the same number of function arguments, but only for references from functions (or main program) subsequent to the re-definition. examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' output: ['[[1,2,1], [10,20,10]]'] - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' input: '[[1,2],[10,20]]' output: ['[[1,2,1,2], [10,20,1,2]]'] - title: Reduce body: | The `reduce` syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer. As an example, we'll pass `[3,2,1]` to this expression: reduce .[] as $item (0; . + $item) For each result that `.[]` produces, `. + $item` is run to accumulate a running total, starting from 0. In this example, `.[]` produces the results 3, 2, and 1, so the effect is similar to running something like this: 0 | (3 as $item | . + $item) | (2 as $item | . + $item) | (1 as $item | . + $item) examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - title: "`limit(n; exp)`" body: | The `limit` function extracts up to `n` outputs from `exp`. examples: - program: '[limit(3;.[])]' input: '[0,1,2,3,4,5,6,7,8,9]' output: ['[0,1,2]'] - title: "`first(expr)`, `last(expr)`, `nth(n; expr)`" body: | The `first(expr)` and `last(expr)` functions extract the first and last values from `expr`, respectively. The `nth(n; expr)` function extracts the nth value output by `expr`. This can be defined as `def nth(n; expr): last(limit(n + 1; expr));`. Note that `nth(n; expr)` doesn't support negative values of `n`. examples: - program: '[first(range(.)), last(range(.)), nth(./2; range(.))]' input: '10' output: ['[0,9,5]'] - title: "`first`, `last`, `nth(n)`" body: | The `first` and `last` functions extract the first and last values from any array at `.`. The `nth(n)` function extracts the nth value of any array at `.`. examples: - program: '[range(.)]|[first, last, nth(5)]' input: '10' output: ['[0,9,5]'] - title: "`foreach`" body: | The `foreach` syntax is similar to `reduce`, but intended to allow the construction of `limit` and reducers that produce intermediate results (see example). The form is `foreach EXP as $var (INIT; UPDATE; EXTRACT)`. Like `reduce`, `INIT` is evaluated once to produce a state value, then each output of `EXP` is bound to `$var`, `UPDATE` is evaluated for each output of `EXP` with the current state and with `$var` visible. Each value output by `UPDATE` replaces the previous state. Finally, `EXTRACT` is evaluated for each new state to extract an output of `foreach`. This is mostly useful only for constructing `reduce`- and `limit`-like functions. But it is much more general, as it allows for partial reductions (see the example below). examples: - program: '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]] else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]' input: '[1,2,3,4,null,"a","b",null]' output: ['[[1,2,3,4],["a","b"]]'] - title: Recursion body: | As described above, `recurse` uses recursion, and any jq function can be recursive. The `while` builtin is also implemented in terms of recursion. Tail calls are optimized whenever the expression to the left of the recursive call outputs its last value. In practice this means that the expression to the left of the recursive call should not produce more than one output for each input. For example: def recurse(f): def r: ., (f | select(. != null) | r); r; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def repeat(exp): def _repeat: exp, _repeat; _repeat; - title: Generators and iterators body: | Some jq operators and functions are actually generators in that they can produce zero, one, or more values for each input, just as one might expect in other programming languages that have generators. For example, `.[]` generates all the values in its input (which must be an array or an object), `range(0; 10)` generates the integers between 0 and 10, and so on. Even the comma operator is a generator, generating first the values generated by the expression to the left of the comma, then for each of those, the values generate by the expression on the right of the comma. The `empty` builtin is the generator that produces zero outputs. The `empty` builtin backtracks to the preceding generator expression. All jq functions can be generators just by using builtin generators. It is also possible to define new generators using only recursion and the comma operator. If the recursive call(s) is(are) "in tail position" then the generator will be efficient. In the example below the recursive call by `_range` to itself is in tail position. The example shows off three advanced topics: tail recursion, generator construction, and sub-functions. examples: - program: '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)); range(0; 10; 3)' input: 'null' output: ['0', '3', '6', '9'] - program: 'def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; [while(.<100; .*2)]' input: '1' output: ['[1,2,4,8,16,32,64]'] - title: 'Math' body: | jq currently only has IEEE754 double-precision (64-bit) floating point number support. Besides simple arithmetic operators such as `+`, jq also has most standard math functions from the C math library. C math functions that take a single input argument (e.g., `sin()`) are available as zero-argument jq functions. C math functions that take two input arguments (e.g., `pow()`) are available as two-argument jq functions that ignore `.`. Availability of standard math functions depends on the availability of the corresponding math functions in your operating system and C math library. Unavailable math functions will be defined but will raise an error. - title: 'I/O' body: | At this time jq has minimal support for I/O, mostly in the form of control over when inputs are read. Two builtins functions are provided for this, `input` and `inputs`, that read from the same sources (e.g., `stdin`, files named on the command-line) as jq itself. These two builtins, and jq's own reading actions, can be interleaved with each other. One builtin provides minimal output capabilities, `debug`. (Recall that a jq program's output values are always output as JSON texts on `stdout`.) The `debug` builtin can have application-specific behavior, such as for executables that use the libjq C API but aren't the jq executable itself. entries: - title: "`input`" body: | Outputs one new input. - title: "`inputs`" body: | Outputs all remaining inputs, one by one. This is primarily useful for reductions over a program's inputs. - title: "`debug`" body: | Causes a debug message based on the input value to be produced. The jq executable wraps the input value with `["DEBUG:", ]` and prints that and a newline on stderr, compactly. This may change in the future. - title: "`input_filename`" body: | Returns the name of the file whose input is currently being filtered. Note that this will not work well unless jq is running in a UTF-8 locale. - title: "`input_line_number`" body: | Returns the line number of the input currently being filtered. - title: 'Streaming' body: | With the `--stream` option jq can parse input texts in a streaming fashion, allowing jq programs to start processing large JSON texts immediately rather than after the parse completes. If you have a single JSON text that is 1GB in size, streaming it will allow you to process it much more quickly. However, streaming isn't easy to deal with as the jq program will have `[, ]` (and a few other forms) as inputs. Several builtins are provided to make handling streams easier. The examples below use the streamed form of `[0,[1]]`, which is `[[0],0],[[1,0],1],[[1,0]],[[1]]`. Streaming forms include `[, ]` (to indicate any scalar value, empty array, or empty object), and `[]` (to indicate the end of an array or object). Future versions of jq run with `--stream` and `-seq` may output additional forms such as `["error message"]` when an input text fails to parse. entries: - title: "`truncate_stream(stream_expression)`" body: | Consumes a number as input and truncates the corresponding number of path elements from the left of the outputs of the given streaming expression. examples: - program: '[1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])]' input: '1' output: ['[[[0],2],[[0]]]'] - title: "`fromstream(stream_expression)`" body: | Outputs values corresponding to the stream expression's outputs. examples: - program: 'fromstream(1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))' input: 'null' output: ['[2]'] - title: "`tostream`" body: | The `tostream` builtin outputs the streamed form of its input. examples: - program: '. as $dot|fromstream($dot|tostream)|.==$dot' input: '[0,[1,{"a":1},{"b":2}]]' output: ['true'] - title: Assignment body: | Assignment works a little differently in jq than in most programming languages. jq doesn't distinguish between references to and copies of something - two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object". If an object has two fields which are arrays, `.foo` and `.bar`, and you append something to `.foo`, then `.bar` will not get bigger. Even if you've just set `.bar = .foo`. If you're used to programming in languages like Python, Java, Ruby, Javascript, etc. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance, it doesn't actually do that, but that's the general idea). All the assignment operators in jq have path expressions on the left-hand side. entries: - title: "`=`" body: | The filter `.foo = 1` will take as input an object and produce as output an object with the "foo" field set to 1. There is no notion of "modifying" or "changing" something in jq - all jq values are immutable. For instance, .foo = .bar | .foo.baz = 1 will not have the side-effect of setting .bar.baz to be set to 1, as the similar-looking program in Javascript, Python, Ruby or other languages would. Unlike these languages (but like Haskell and some other functional languages), there is no notion of two arrays or objects being "the same array" or "the same object". They can be equal, or not equal, but if we change one of them in no circumstances will the other change behind our backs. This means that it's impossible to build circular values in jq (such as an array whose first element is itself). This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON. Note that the left-hand side of '=' refers to a value in `.`. Thus `$var.foo = 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo = 1` instead. If the right-hand side of '=' produces multiple values, then for each such value jq will set the paths on the left-hand side to the value and then it will output the modified `.`. For example, `(.a,.b)=range(2)` outputs `{"a":0,"b":0}`, then `{"a":1,"b":1}`. The "update" assignment forms (see below) do not do this. Note too that `.a,.b=0` does not set `.a` and `.b`, but `(.a,.b)=0` sets both. - title: "`|=`" body: | As well as the assignment operator '=', jq provides the "update" operator '|=', which takes a filter on the right-hand side and works out the new value for the property of `.` being assigned to by running the old value through this expression. For instance, .foo |= .+1 will build an object with the "foo" field set to the input's "foo" plus 1. This example should show the difference between '=' and '|=': Provide input '{"a": {"b": 10}, "b": 20}' to the programs: .a = .b .a |= .b The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20}. The latter will set the "a" field of the input to the "a" field's "b" field, producing {"a": 10}. The left-hand side can be any general path expression; see `path()`. Note that the left-hand side of '|=' refers to a value in `.`. Thus `$var.foo |= . + 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | .foo |= . + 1` instead. If the right-hand side outputs multiple values, only the last one will be used. examples: - program: '(..|select(type=="boolean")) |= if . then 1 else 0 end' input: '[true,false,[5,true,[true,[false]],false]]' output: ['[1,0,[5,1,[1,[0]],0]]'] - title: "`+=`, `-=`, `*=`, `/=`, `%=`, `//=`" body: | jq has a few operators of the form `a op= b`, which are all equivalent to `a |= . op b`. So, `+= 1` can be used to increment values. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] - title: Complex assignments body: | Lots more things are allowed on the left-hand side of a jq assignment than in most languages. We've already seen simple field accesses on the left hand side, and it's no surprise that array accesses work just as well: .posts[0].title = "JQ Manual" What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: .posts[].comments |= . + ["this is great"] That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts). When jq encounters an assignment like 'a = b', it records the "path" taken to select a part of the input document while executing a. This path is then used to find which part of the input to change while executing the assignment. Any filter may be used on the left-hand side of an equals - whichever paths it selects from the input will be where the assignment is performed. This is a very powerful operation. Suppose we wanted to add a comment to blog posts, using the same "blog" input above. This time, we only want to comment on the posts written by "stedolan". We can find those posts using the "select" function described earlier: .posts[] | select(.author == "stedolan") The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: (.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."] - title: Modules body: | jq has a library/module system. Modules are files whose names end in `.jq`. Modules imported by a program are searched for in a default search path (see below). The `import` and `include` directives allow the importer to alter this path. Paths in the a search path are subject to various substitutions. For paths starting with "~/", the user's home directory is substituted for "~". For paths starting with "$ORIGIN/", the path of the jq executable is substituted for "$ORIGIN". For paths starting with "./" or paths that are ".", the path of the including file is substituted for ".". For top-level programs given on the command-line, the current directory is used. Import directives can optionally specify a search path to which the default is appended. The default search path is the search path given to the `-L` command-line option, else `["~/.jq", "$ORIGIN/../lib/jq", "$ORIGIN/../lib"]`. Null and empty string path elements terminate search path processing. A dependency with relative path "foo/bar" would be searched for in "foo/bar.jq" and "foo/bar/bar.jq" in the given search path. This is intended to allow modules to be placed in a directory along with, for example, version control files, README files, and so on, but also to allow for single-file modules. Consecutive components with the same name are not allowed to avoid ambiguities (e.g., "foo/foo"). For example, with `-L$HOME/.jq` a module `foo` can be found in `$HOME/.jq/foo.jq` and `$HOME/.jq/foo/foo.jq`. If "$HOME/.jq" is a file, it is sourced into the main program. entries: - title: "`import RelativePathString as NAME [];`" body: | Imports a module found at the given path relative to a directory in a search path. A ".jq" suffix will be added to the relative path string. The module's symbols are prefixed with "NAME::". The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`include RelativePathString [];`" body: | Imports a module found at the given path relative to a directory in a search path as if it were included in place. A ".jq" suffix will be added to the relative path string. The module's symbols are imported into the caller's namespace as if the module's content had been included directly. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. - title: "`import RelativePathString as $NAME [];`" body: | Imports a JSON file found at the given path relative to a directory in a search path. A ".json" suffix will be added to the relative path string. The file's data will be available as `$NAME::NAME`. The optional metadata must be a constant jq expression. It should be an object with keys like "homepage" and so on. At this time jq only uses the "search" key/value of the metadata. The metadata is also made available to users via the `modulemeta` builtin. The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top-level search path. - title: "`module ;`" body: | This directive is entirely optional. It's not required for proper operation. It serves only the purpose of providing metadata that can be read with the `modulemeta` builtin. The metadata must be a constant jq expression. It should be an object with keys like "homepage". At this time jq doesn't use this metadata, but it is made available to users via the `modulemeta` builtin. - title: "`modulemeta`" body: | Takes a module name as input and outputs the module's metadata as an object, with the module's imports (including metadata) as an array value for the "deps" key. Programs can use this to query a module's metadata, which they could then use to, for example, search for, download, and install missing dependencies. jq-jq-1.6/docs/README.md0000600000175000017500000000170013366726451014122 0ustar czchenczchenDocumentation ============= The jq website, manpages and some of the tests are generated from this directory. The directory holds a [Bonsai](http://tinytree.info) website, and the manual is a YAML file in `content/3.manual`. To build the documentation (including building the jq manpage), you'll need a working Ruby setup. The easiest way to get one is to install RVM and Ruby 1.9.3 like so: \curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3 After that finishes installing, you'll need to make sure RVM is on your path by doing `source $HOME/.rvm/scripts/rvm`, or just opening a new shell. See for more info on RVM. Once RVM is installed, you can install all the dependencies for jq's documentation build by running this from the `docs` directory: bundle install When bundle manages to install the dependencies, rerun `./configure` in the jq root directory and then the Makefile will be able to generate the jq manpage. jq-jq-1.6/docs/public/0000700000175000017500000000000013366726451014121 5ustar czchenczchenjq-jq-1.6/docs/public/jq.png0000600000175000017500000002000313366726451015236 0ustar czchenczchenPNG  IHDR^RosBIT|d pHYs]ztEXtSoftwarewww.inkscape.org<IDATxyc1Í ɚ-Ѡ_Y:#Q(-J*&FETJBݶ;*KrRPٳATdsߙfu\9>x\x{~>83) 2=myLL"Rof q,5/J#_:ڤ@Djlׇ˝|oõD51o{Kx](51偟0"3@Dj6 6`w,i)50V{+0U+ t-Ny]eX88WZB D$/ 8"([NDYe< CF+ [GGL.BG D$E^]4Hݣ  f3f:Pw0,eeWȈ:a>i5+G0 P1%a,]4Hl)`:Q Gi 5M.`.@C DzE0G ͡"R1w x(aԹIͨĸ'aȈĸ.a1i5gF0 %̞X$\D!͡Hw8/]4V 2ҒI9K#kk 3{9p 03i5yҶ) R٥9˪8Jqw?2i5CJi5ʧ_s`j/3[7tkYF<^6tvJI_ ,ij2Z,)7g9_ߧ:̾~w?. WZ@ DfI)] fq49_Uwُ+WaR88|kZf68 ur/8GZN DH)l?KO9?6뷎>yQ`w-"cn9>0G3Ϧl׽ה.,mkwnO 90mt7yH?雹RJ+(n\c 火G[ l lB p V# Zݯ{p `  e=m~gTj RZ@ű/8uz/' [X!1g-"R5J)-|8 H 5 X: ܀L5J)M 85 WD@ {dyz@ H7w@C"j RX7 h’0Rf꟱z{"V ;3{>\_+I_??q?WY?tHJic` 73{%je{_]o/jtKc:9٢VDx2GJp-ppxdUlSN cf;BcvkJfv qԧy@Y1.5m<'Yf6鄔 U}Ny3g1%y nwǣ `f+8H=Ws&:z[U| }5yYl3;؃4(œcfǛ٬g'QfHF}y"8 "v兰boʳiV{l~N5]XՏlՇ-Lٟ/󶡙^ *>Z8̾ HF4 {>ڦ>)H.dzv> Hm*}<8,ORHѵHKlq@,fJ }{{d\l/2<N "Ɩ7g@osΗJw}X))@a`R@n}hc˘;wD!p)ʭzx$3[QizdCƯ78`ZdNj 8orGzl5_%(f{+j@nSF2~:΢ - |1x g_Dy?kʩlcKACF{_kF#c?|txrtO m;/Hfu_PRJ[Ds>( wlKG"^3ly`Zr?Xwޏ ٖhZ4'2#{ 3Wϋj+.^1_\df|mV R2 r[6.,v+3{)p=s!OtMrj}b]pdt"V.Uڏ|: ئϺW(+廫^LyVrwJǘR:t̀;)59lvX8pφWJ-ᨁ4Hod圯 ȕQOt^~F&ZQi}yՇT-"] jrV@"8W@rΗJ7?\ss똝Hshd-:;g/"fRi7R4Tab&}/ ̟/w@.직G㫔R85 ~pr@t-lX5u5]4[r2]a;`l9~]4A+ݵc`Rc)D;i"q; Au[oJGj2A׸AuAtj 5Rڐ ȕ40v@+dzm'5yMPJi bğsk@t[T\wR>Q'lN0ŀu ywF֠R3)sAM}5S9!j /P@Va64#@j$pd9KrEV ucG"@eo`r@V%eӓj 5RZ7gۆj `\lHQ+4"@j 45]˜قĝޖ`ܯksgpX<5>K)[3rE+̖ a,ikd4ĂrtĆRi'䊌?D#8TlU`Jtj #ܣrO˹kf5>HE-.@>]H٢5I)m l}L\~x(,bhx/R,,, <Lu,Shd0w=3[#-Ls!o rL+ҿ?9?8R+r`rrE*u SEd#=RZ2Ôh.9_1_[)SW;X3|kŹgf9pq` N ef); CGSC^ \Gv\ m )RnSÙgr¤VnVq ى{Ѯ6rх [D2y>DO)6kP^RpE#Ez]lfI)mFy@>H?9hSݜsnn@76ʳ:8#Fbfa8=~;e4e%)cQ4]o|=!kfEٻi )?qo)%0s\RJy9 rEreH]`f_:> Dכ3zG?qؓyCZNc.1clAقfv,p(PswS~tJ)}Q ~+"U"z'3fPf6 }P4lXL_^g&fFK' Jfv_5%hʮuuARZ<]-lFٱ&PULn6o&cmf|?bw>N)U4v vw8+yXr36l~-lf[!f;zY$M܅:kΡUfZ<ڞzwۡ|2 ):7y j\&>u@.]E!5X1ySm>fԊ \]iBtPz#-"uS 58.o+!51rV] sWԖ2`Pj`pOtP>,Շ*@!1#ۯ9끡)[%ȼIWP5>Dݯ̫묬N*aܝs#*껨y@ٮo旼8&!U6b_;ԧDCZ1^Y*Uر+j Mjs(sk3WK9T篘=3 X {rS^ΜF`=mηTG xV@75oȚLGM~Ѡ 㷁ٍRu<\ؔG䶎m ?6qwwO'HJ5l^bf/8Я5Pf!.ΗdlݭhdBfӜ[BM򃱜v'2/5) V/?61zCerD"[X5۱u"_e(S~yj V cRZ ؒr|-sΏVlf.p.,VtFw< F"[X"@ h/y H&WvSpmt '`'MܕASZqgmz4 B@v}~ǁB@z!f; Pr'kB[@"` @s]tԞ_A9Zj.UB@6`S7ѵB@1! ?%Yf~_t!mj (lO>wtǢQF8ZΣ)gz]N'@wx?] ln-ݟ@8Im Y`]w$?D_@UlIS[#]/Pʈs*X[ DZ̦P X)88gD#cfG j:&w?O DZ&[Sb+OS]ȸٞ׀Eq#e'U#mCl9l)O({gi -lL%g7QVɗ@lxSˀI}7a\6Q1"*e2wJøLZ` kIENDB`jq-jq-1.6/docs/public/css/0000700000175000017500000000000013366726451014711 5ustar czchenczchenjq-jq-1.6/docs/public/css/base.scss0000600000175000017500000000623313366726451016526 0ustar czchenczchen@charset "utf-8"; body { padding-top: 80px; } .container { max-width: 970px; } /* index.liquid *******************************************/ #blurb { padding-top: 40px; p { font-size: 1.9em; } .btn-group { margin: 4px; } } #multiblurb { line-height: 1.7; text-align: center; font-size: 12pt; code { border: 0; font-size: 12pt; } } #news { font-size: 12pt; .date { font-style: italic; } } /* default.liquid *****************************************/ .tutorial-example { position: relative; margin-bottom: 10px; pre { margin-bottom: 0px; } a { position: absolute; top: 0px; right: 0px; padding: 15px 8px; color: #777777; font-weight: bold; line-height: 10px; font-size: 12px; border-left: 1px solid #DDDDDD; display: block; } .accordion-body pre { margin: 0 4px; border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; } } @media print { .tutorial-example a { display: none; } } /* manual.liquid ******************************************/ section { padding-top: 24px; } h3 code { border: 0; font-size: 20px; } @media(max-width: 991px){ #navcolumn { /* Put nav column above manual content */ position: relative !important; margin-bottom: 60px; } } @media(min-width: 992px) { #manualcontent { /* Put nav column left of manual content */ padding-left: 280px; } } .nav-pills { li a { padding: 8px 12px; } margin-bottom: 20px; } .manual-example table { border-top: 1px solid #E5E5E5; td { white-space: pre-wrap; font-family: Monaco, Menlo, Consolas, "Courier New", monospace; } td.jqprogram { font-weight: bold; } th { text-align: right; padding-right: 10px; } } @media print { #navcolumn { display: none !important; } .manual-example { display: block !important; height: auto !important; } .jqplay-btn { display: none !important; } } /* shared/_footer.liquid **********************************/ footer { background-color: #F5F5F5; padding: 20px 0; margin-top: 40px; color: #999999; text-align: center; p { margin: 8px 0; } } /* typeahead **********************************************/ .twitter-typeahead { width: 100%; } .tt-menu { width: 100%; background-color: #fff; padding: 8px 0; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); box-shadow: 0 5px 10px rgba(0,0,0,.2); } .tt-suggestion { padding: 3px 20px; &:hover { cursor: pointer; color: #fff; background-color: #446e9b; } &.tt-cursor { color: #fff; background-color: #446e9b; } p { margin: 0; } } jq-jq-1.6/docs/public/robots.txt0000600000175000017500000000002613366726451016172 0ustar czchenczchenUser-agent: * Allow: /jq-jq-1.6/docs/public/js/0000700000175000017500000000000013366726451014535 5ustar czchenczchenjq-jq-1.6/docs/public/js/manual-search.js0000600000175000017500000000304313366726451017615 0ustar czchenczchenvar section_names = function(q) { if (!q) { return []; } var matches = []; q = q.toLowerCase(); $.each(section_map, function(k, v) { if (k.toLowerCase().indexOf(q) != -1) { matches.push(k); } }); matches.sort(function(a, b) { // shortest to longest return a.length - b.length; }); return matches; } var section_names_cb = function(q, cb) { cb(section_names(q)); } var go_to_section = function() { query = $('#searchbox').val(); results = section_names(query); if (results.length == 0) { return; } result = results[0]; location.hash = '#' + section_map[result]; if (result != query) { $('#searchbox').val(result); } } $(function(){ $('#searchbox').typeahead( {hint: false, highlight: true, minLength: 1}, {name: "contents", source: section_names_cb, limit: 6} ).on('typeahead:selected', function(e, data) { go_to_section(); }); $('#searchbox').change(go_to_section); }); // add "Run" button to execute examples on jqplay.org $(function() { $.each($('.manual-example table'), function(index, value) { $value = $(value) var j = $value.find('tr:nth-child(2) td:first').text(); var q = $value.find('.jqprogram').text().replace(/^jq /, '').replace(/^'(.+)'$/, '$1'); var url = 'https://jqplay.org/jq?q=' + encodeURIComponent(q) +'&j=' + encodeURIComponent(j) var $last_tr = $value.find('tr:last'); $last_tr.after('Run'); }); }); jq-jq-1.6/docs/public/.htaccess0000600000175000017500000000133713366726451015725 0ustar czchenczchenDirectoryIndex index.html FileETag All # Compress all static assets # compress content with type html, text, and css AddOutputFilterByType DEFLATE text/css text/html text/javascript application/javascript application/x-javascript text/js text/plain text/xml # properly handle requests coming from behind proxies Header append Vary User-Agent # Cache, aggressively ExpiresActive On ExpiresDefault "access plus 10 days" ExpiresDefault "access plus 10 years" # Mime-types AddType application/vnd.ms-fontobject .eot AddType font/ttf .ttf AddType font/otf .otf jq-jq-1.6/docs/Rakefile.manual0000600000175000017500000000235713366726451015575 0ustar czchenczchenrequire 'ronn' require 'tempfile' require 'yaml' def load_manual YAML::load(File.open("content/3.manual/manual.yml")) end task :manpage do Tempfile.open "manpage" do |f| manual = load_manual f.puts manual['manpage_intro'] f.puts manual['body'] manual['sections'].each do |section| f.puts "## #{section['title'].upcase}\n" f.puts section['body'] f.puts "" (section['entries'] || []).each do |entry| f.puts "### #{entry['title']}\n" f.puts entry['body'] f.puts "" (entry['examples'] || []).each do |example| f.puts " jq '#{example['program']}'" f.puts " #{example['input']}" f.puts " => #{example['output'].join(", ")}" f.puts end end f.puts "" end f.puts manual['manpage_epilogue'] f.close puts Ronn::Document.new(f.path).convert('roff').gsub(/<\/?code>/,"") end end task :mantests do load_manual['sections'].each do |section| (section['entries'] || []).each do |entry| (entry['examples'] || []).each do |example| puts example['program'].gsub("\n", " ") puts example['input'] example['output'].each do |s| puts s end puts end end end end jq-jq-1.6/docs/Gemfile0000600000175000017500000000017513366726451014143 0ustar czchenczchensource "https://rubygems.org" gem "rake" gem "bonsai" gem "maruku" gem "ronn" gem "mustache", "<1.0" # to support ruby <2.0 jq-jq-1.6/docs/Rakefile.website0000600000175000017500000000275313366726451015762 0ustar czchenczchenrequire 'bonsai' require 'json' require 'liquid' require 'maruku' module ExtraFilters def markdownify(input) Maruku.new(input).to_html end def search_id(input) input.gsub(/`/, '') end def section_id(input) input.gsub(/[^a-zA-Z0-9_]/, '') end def entry_id(input) input.gsub(/[ `]/, '') end def no_paragraph(input) input.gsub('

', '').gsub('

', '') end def json(input) input.to_json end def unique(input) @n = (@n || 0) + 1 input + @n.to_s end end Liquid::Template.register_filter(ExtraFilters) begin `java 2>&1` rescue class Bonsai::Exporter def self.compress_assets Bonsai.log "java not found! Not compressing javascript or stylesheets" end end end task :build do Bonsai.root_dir = Dir.pwd Bonsai::Exporter.publish! end task :serve do begin Bonsai.log "Press Control+C to quit" require 'rack' require 'sinatra' require 'watch' require 'launchy' Bonsai.root_dir = Dir.pwd server = fork { app = Rack::Builder.app { map "/jq" do use Bonsai::StaticPassThrough, :root => Bonsai.root_dir + "/output", :urls => ["/"] end run Bonsai::DevelopmentServer } Rack::Handler.default.run(app, :Port => 5000) do Launchy.open("http://localhost:5000/jq/") end } Watch.new("{content,templates,public}/**/*") { Bonsai::Exporter.process! } rescue Interrupt Process.kill("QUIT", server) Process.wait(server) exit end end jq-jq-1.6/docs/site.yml0000600000175000017500000000044113366726451014333 0ustar czchenczchen# The key value pairs found below are available within the templates. :url: https://stedolan.github.io/jq # This line is modified by the Makefile. To change the version number, # edit the Autoconf version number at the top of configure.ac jq_version: "1.4-2-g15c4a7f-dirty" root: '/jq' jq-jq-1.6/NEWS0000600000175000017500000000445013366726451012417 0ustar czchenczchen Release history * jq version 1.5 was released on Sat Aug 15 2015 * jq version 1.4 was released on Mon Jun 9 2014 * jq version 1.3 was released on Sun May 19 2013 * jq version 1.2 was released on Thu Dec 20 2012 * jq version 1.1 was released on Sun Oct 21 2012 * jq version 1.0 was released on Sun Oct 21 2012 New features in 1.5 since 1.4: - regular expressions (with Oniguruma) - a library/module system - many new builtins - datetime builtins - math builtins - regexp-related builtins - stream-related builtins (e.g., all/1, any/1) - minimal I/O builtins (`inputs`, `debug`) - new syntactic features, including: - destructuring (`. as [$first, $second] | ...`) - try/catch, generalized `?` operator, and label/break - `foreach` - multiple definitions of a function with different numbers of arguments - command-line arguments - --join-lines / -j for raw output - --argjson and --slurpfile - --tab and --indent - --stream (streaming JSON parser) - --seq (RFC7464 JSON text sequence) - --run-tests improvements - optimizations: - tail-call optimization - reduce and foreach no longer leak a reference to . New features in 1.4 since 1.3: - command-line arguments - jq --arg-file variable file - jq --unbuffered - jq -e / --exit-status (set exit status based on outputs) - jq -S / --sort-keys (now jq no longer sorts object keys by default - syntax - .. -> like // in XPath (recursive traversal) - question mark (e.g., .a?) to suppress errors - ."foo" syntax (equivalent to .["foo"]) - better error handling for .foo - added % operator (modulo) - allow negation without requiring extra parenthesis - more function arguments (up to six) - filters: - any, all - iterables, arrays, objects, scalars, nulls, booleans, numbers, strings, values - string built-ins: - split - join (join an array of strings with a given separator string) - ltrimstr, rtrimstr - startswith, endswith - explode, implode - fromjson, tojson - index, rindex, indices - math functions - floor, sqrt, cbrt, etetera (depends on what's available from libm) - libjq -- a C API interface to jq's JSON representation and for running jq programs from C applications jq-jq-1.6/build/0000700000175000017500000000000013366726451013012 5ustar czchenczchenjq-jq-1.6/jq.1.prebuilt0000600000175000017500000030501213366726451014237 0ustar czchenczchen.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "JQ" "1" "December 2017" "" "" . .SH "NAME" \fBjq\fR \- Command\-line JSON processor . .SH "SYNOPSIS" \fBjq\fR [\fIoptions\fR\.\.\.] \fIfilter\fR [\fIfiles\fR\.\.\.] . .P \fBjq\fR can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents\. For instance, running the command \fBjq \'map(\.price) | add\'\fR will take an array of JSON objects as input and return the sum of their "price" fields\. . .P \fBjq\fR can accept text input as well, but by default, \fBjq\fR reads a stream of JSON entities (including numbers and other literals) from \fBstdin\fR\. Whitespace is only needed to separate entities such as 1 and 2, and true and false\. One or more \fIfiles\fR may be specified, in which case \fBjq\fR will read input from those instead\. . .P The \fIoptions\fR are described in the \fIINVOKING JQ\fR section; they mostly concern input and output formatting\. The \fIfilter\fR is written in the jq language and specifies how to transform the input file or document\. . .SH "FILTERS" A jq program is a "filter": it takes an input, and produces an output\. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks\. . .P Filters can be combined in various ways \- you can pipe the output of one filter into another filter, or collect the output of a filter into an array\. . .P Some filters produce multiple results, for instance there\'s one that produces all the elements of its input array\. Piping that filter into a second runs the second filter for each element of the array\. Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq\. . .P It\'s important to remember that every filter has an input and an output\. Even literals like "hello" or 42 are filters \- they take an input but always produce the same literal as output\. Operations that combine two filters, like addition, generally feed the same input to both and combine the results\. So, you can implement an averaging filter as \fBadd / length\fR \- feeding the input array both to the \fBadd\fR filter and the \fBlength\fR filter and then performing the division\. . .P But that\'s getting ahead of ourselves\. :) Let\'s start with something simpler: . .SH "INVOKING JQ" jq filters run on a stream of JSON data\. The input to jq is parsed as a sequence of whitespace\-separated JSON values which are passed through the provided filter one at a time\. The output(s) of the filter are written to standard out, again as a sequence of whitespace\-separated JSON data\. . .P Note: it is important to mind the shell\'s quoting rules\. As a general rule it\'s best to always quote (with single\-quote characters) the jq program, as too many characters with special meaning to jq are also shell meta\-characters\. For example, \fBjq "foo"\fR will fail on most Unix shells because that will be the same as \fBjq foo\fR, which will generally fail because \fBfoo is not defined\fR\. When using the Windows command shell (cmd\.exe) it\'s best to use double quotes around your jq program when given on the command\-line (instead of the \fB\-f program\-file\fR option), but then double\-quotes in the jq program need backslash escaping\. . .P You can affect how jq reads and writes its input and output using some command\-line options: . .IP "\(bu" 4 \fB\-\-version\fR: . .IP Output the jq version and exit with zero\. . .IP "\(bu" 4 \fB\-\-seq\fR: . .IP Use the \fBapplication/json\-seq\fR MIME type scheme for separating JSON texts in jq\'s input and output\. This means that an ASCII RS (record separator) character is printed before each value on output and an ASCII LF (line feed) is printed after every output\. Input JSON texts that fail to parse are ignored (but warned about), discarding all subsequent input until the next RS\. This mode also parses the output of jq without the \fB\-\-seq\fR option\. . .IP "\(bu" 4 \fB\-\-stream\fR: . .IP Parse the input in streaming fashion, outputing arrays of path and leaf values (scalars and empty arrays or empty objects)\. For example, \fB"a"\fR becomes \fB[[],"a"]\fR, and \fB[[],"a",["b"]]\fR becomes \fB[[0],[]]\fR, \fB[[1],"a"]\fR, and \fB[[1,0],"b"]\fR\. . .IP This is useful for processing very large inputs\. Use this in conjunction with filtering and the \fBreduce\fR and \fBforeach\fR syntax to reduce large inputs incrementally\. . .IP "\(bu" 4 \fB\-\-slurp\fR/\fB\-s\fR: . .IP Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once\. . .IP "\(bu" 4 \fB\-\-raw\-input\fR/\fB\-R\fR: . .IP Don\'t parse the input as JSON\. Instead, each line of text is passed to the filter as a string\. If combined with \fB\-\-slurp\fR, then the entire input is passed to the filter as a single long string\. . .IP "\(bu" 4 \fB\-\-null\-input\fR/\fB\-n\fR: . .IP Don\'t read any input at all! Instead, the filter is run once using \fBnull\fR as the input\. This is useful when using jq as a simple calculator or to construct JSON data from scratch\. . .IP "\(bu" 4 \fB\-\-compact\-output\fR / \fB\-c\fR: . .IP By default, jq pretty\-prints JSON output\. Using this option will result in more compact output by instead putting each JSON object on a single line\. . .IP "\(bu" 4 \fB\-\-tab\fR: . .IP Use a tab for each indentation level instead of two spaces\. . .IP "\(bu" 4 \fB\-\-indent n\fR: . .IP Use the given number of spaces (no more than 8) for indentation\. . .IP "\(bu" 4 \fB\-\-color\-output\fR / \fB\-C\fR and \fB\-\-monochrome\-output\fR / \fB\-M\fR: . .IP By default, jq outputs colored JSON if writing to a terminal\. You can force it to produce color even if writing to a pipe or a file using \fB\-C\fR, and disable color with \fB\-M\fR\. . .IP Colors can be configured with the \fBJQ_COLORS\fR environment variable (see below)\. . .IP "\(bu" 4 \fB\-\-ascii\-output\fR / \fB\-a\fR: . .IP jq usually outputs non\-ASCII Unicode codepoints as UTF\-8, even if the input specified them as escape sequences (like "\eu03bc")\. Using this option, you can force jq to produce pure ASCII output with every non\-ASCII character replaced with the equivalent escape sequence\. . .IP "\(bu" 4 \fB\-\-unbuffered\fR . .IP Flush the output after each JSON object is printed (useful if you\'re piping a slow data source into jq and piping jq\'s output elsewhere)\. . .IP "\(bu" 4 \fB\-\-sort\-keys\fR / \fB\-S\fR: . .IP Output the fields of each object with the keys in sorted order\. . .IP "\(bu" 4 \fB\-\-raw\-output\fR / \fB\-r\fR: . .IP With this option, if the filter\'s result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes\. This can be useful for making jq filters talk to non\-JSON\-based systems\. . .IP "\(bu" 4 \fB\-\-join\-output\fR / \fB\-j\fR: . .IP Like \fB\-r\fR but jq won\'t print a newline after each output\. . .IP "\(bu" 4 \fB\-f filename\fR / \fB\-\-from\-file filename\fR: . .IP Read filter from the file rather than from a command line, like awk\'s \-f option\. You can also use \'#\' to make comments\. . .IP "\(bu" 4 \fB\-Ldirectory\fR / \fB\-L directory\fR: . .IP Prepend \fBdirectory\fR to the search list for modules\. If this option is used then no builtin search list is used\. See the section on modules below\. . .IP "\(bu" 4 \fB\-e\fR / \fB\-\-exit\-status\fR: . .IP Sets the exit status of jq to 0 if the last output values was neither \fBfalse\fR nor \fBnull\fR, 1 if the last output value was either \fBfalse\fR or \fBnull\fR, or 4 if no valid result was ever produced\. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran\. . .IP Another way to set the exit status is with the \fBhalt_error\fR builtin function\. . .IP "\(bu" 4 \fB\-\-arg name value\fR: . .IP This option passes a value to the jq program as a predefined variable\. If you run jq with \fB\-\-arg foo bar\fR, then \fB$foo\fR is available in the program and has the value \fB"bar"\fR\. Note that \fBvalue\fR will be treated as a string, so \fB\-\-arg foo 123\fR will bind \fB$foo\fR to \fB"123"\fR\. . .IP Named arguments are also available to the jq program as \fB$ARGS\.named\fR\. . .IP "\(bu" 4 \fB\-\-argjson name JSON\-text\fR: . .IP This option passes a JSON\-encoded value to the jq program as a predefined variable\. If you run jq with \fB\-\-argjson foo 123\fR, then \fB$foo\fR is available in the program and has the value \fB123\fR\. . .IP "\(bu" 4 \fB\-\-slurpfile variable\-name filename\fR: . .IP This option reads all the JSON texts in the named file and binds an array of the parsed JSON values to the given global variable\. If you run jq with \fB\-\-argfile foo bar\fR, then \fB$foo\fR is available in the program and has an array whose elements correspond to the texts in the file named \fBbar\fR\. . .IP "\(bu" 4 \fB\-\-argfile variable\-name filename\fR: . .IP Do not use\. Use \fB\-\-slurpfile\fR instead\. . .IP (This option is like \fB\-\-slurpfile\fR, but when the file has just one text, then that is used, else an array of texts is used as in \fB\-\-slurpfile\fR\.) . .IP "\(bu" 4 \fB\-\-args\fR: . .IP Remaining arguments are positional string arguments\. These are available to the jq program as \fB$ARGS\.positional[]\fR\. . .IP "\(bu" 4 \fB\-\-jsonargs\fR: . .IP Remaining arguments are positional JSON text arguments\. These are available to the jq program as \fB$ARGS\.positional[]\fR\. . .IP "\(bu" 4 \fB\-\-run\-tests [filename]\fR: . .IP Runs the tests in the given file or standard input\. This must be the last option given and does not honor all preceding options\. The input consists of comment lines, empty lines, and program lines followed by one input line, as many lines of output as are expected (one per output), and a terminating empty line\. Compilation failure tests start with a line containing only "%%FAIL", then a line containing the program to compile, then a line containing an error message to compare to the actual\. . .IP Be warned that this option can change backwards\-incompatibly\. . .IP "" 0 . .SH "BASIC FILTERS" . .SS "Identity: \." The absolute simplest filter is \fB\.\fR \. This is a filter that takes its input and produces it unchanged as output\. That is, this is the identity operator\. . .P Since jq by default pretty\-prints all output, this trivial program can be a useful way of formatting JSON output from, say, \fBcurl\fR\. . .IP "" 4 . .nf jq \'\.\' "Hello, world!" => "Hello, world!" . .fi . .IP "" 0 . .SS "Object Identifier\-Index: \.foo, \.foo\.bar" The simplest \fIuseful\fR filter is \fB\.foo\fR\. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key "foo", or null if there\'s none present\. . .P A filter of the form \fB\.foo\.bar\fR is equivalent to \fB\.foo|\.bar\fR\. . .P This syntax only works for simple, identifier\-like keys, that is, keys that are all made of alphanumeric characters and underscore, and which do not start with a digit\. . .P If the key contains special characters, you need to surround it with double quotes like this: \fB\."foo$"\fR, or else \fB\.["foo$"]\fR\. . .P For example \fB\.["foo::bar"]\fR and \fB\.["foo\.bar"]\fR work while \fB\.foo::bar\fR does not, and \fB\.foo\.bar\fR means \fB\.["foo"]\.["bar"]\fR\. . .IP "" 4 . .nf jq \'\.foo\' {"foo": 42, "bar": "less interesting data"} => 42 jq \'\.foo\' {"notfoo": true, "alsonotfoo": false} => null jq \'\.["foo"]\' {"foo": 42} => 42 . .fi . .IP "" 0 . .SS "Optional Object Identifier\-Index: \.foo?" Just like \fB\.foo\fR, but does not output even an error when \fB\.\fR is not an array or an object\. . .IP "" 4 . .nf jq \'\.foo?\' {"foo": 42, "bar": "less interesting data"} => 42 jq \'\.foo?\' {"notfoo": true, "alsonotfoo": false} => null jq \'\.["foo"]?\' {"foo": 42} => 42 jq \'[\.foo?]\' [1,2] => [] . .fi . .IP "" 0 . .SS "Generic Object Index: \.[]" You can also look up fields of an object using syntax like \fB\.["foo"]\fR (\.foo above is a shorthand version of this, but only for identifier\-like strings)\. . .SS "Array Index: \.[2]" When the index value is an integer, \fB\.[]\fR can index arrays\. Arrays are zero\-based, so \fB\.[2]\fR returns the third element\. . .P Negative indices are allowed, with \-1 referring to the last element, \-2 referring to the next to last element, and so on\. . .IP "" 4 . .nf jq \'\.[0]\' [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => {"name":"JSON", "good":true} jq \'\.[2]\' [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => null jq \'\.[\-2]\' [1,2,3] => 2 . .fi . .IP "" 0 . .SS "Array/String Slice: \.[10:15]" The \fB\.[10:15]\fR syntax can be used to return a subarray of an array or substring of a string\. The array returned by \fB\.[10:15]\fR will be of length 5, containing the elements from index 10 (inclusive) to index 15 (exclusive)\. Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array)\. . .IP "" 4 . .nf jq \'\.[2:4]\' ["a","b","c","d","e"] => ["c", "d"] jq \'\.[2:4]\' "abcdefghi" => "cd" jq \'\.[:3]\' ["a","b","c","d","e"] => ["a", "b", "c"] jq \'\.[\-2:]\' ["a","b","c","d","e"] => ["d", "e"] . .fi . .IP "" 0 . .SS "Array/Object Value Iterator: \.[]" If you use the \fB\.[index]\fR syntax, but omit the index entirely, it will return \fIall\fR of the elements of an array\. Running \fB\.[]\fR with the input \fB[1,2,3]\fR will produce the numbers as three separate results, rather than as a single array\. . .P You can also use this on an object, and it will return all the values of the object\. . .IP "" 4 . .nf jq \'\.[]\' [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => {"name":"JSON", "good":true}, {"name":"XML", "good":false} jq \'\.[]\' [] => jq \'\.[]\' {"a": 1, "b": 1} => 1, 1 . .fi . .IP "" 0 . .SS "\.[]?" Like \fB\.[]\fR, but no errors will be output if \. is not an array or object\. . .SS "Comma: ," If two filters are separated by a comma, then the same input will be fed into both and the two filters\' output value streams will be concatenated in order: first, all of the outputs produced by the left expression, and then all of the outputs produced by the right\. For instance, filter \fB\.foo, \.bar\fR, produces both the "foo" fields and "bar" fields as separate outputs\. . .IP "" 4 . .nf jq \'\.foo, \.bar\' {"foo": 42, "bar": "something else", "baz": true} => 42, "something else" jq \'\.user, \.projects[]\' {"user":"stedolan", "projects": ["jq", "wikiflow"]} => "stedolan", "jq", "wikiflow" jq \'\.[4,2]\' ["a","b","c","d","e"] => "e", "c" . .fi . .IP "" 0 . .SS "Pipe: |" The | operator combines two filters by feeding the output(s) of the one on the left into the input of the one on the right\. It\'s pretty much the same as the Unix shell\'s pipe, if you\'re used to that\. . .P If the one on the left produces multiple results, the one on the right will be run for each of those results\. So, the expression \fB\.[] | \.foo\fR retrieves the "foo" field of each element of the input array\. . .P Note that \fB\.a\.b\.c\fR is the same as \fB\.a | \.b | \.c\fR\. . .P Note too that \fB\.\fR is the input value at the particular stage in a "pipeline", specifically: where the \fB\.\fR expression appears\. Thus \fB\.a | \. | \.b\fR is the same as \fB\.a\.b\fR, as the \fB\.\fR in the middle refers to whatever value \fB\.a\fR produced\. . .IP "" 4 . .nf jq \'\.[] | \.name\' [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => "JSON", "XML" . .fi . .IP "" 0 . .SS "Parenthesis" Parenthesis work as a grouping operator just as in any typical programming language\. . .IP "" 4 . .nf jq \'(\. + 2) * 5\' 1 => 15 . .fi . .IP "" 0 . .SH "TYPES AND VALUES" jq supports the same set of datatypes as JSON \- numbers, strings, booleans, arrays, objects (which in JSON\-speak are hashes with only string keys), and "null"\. . .P Booleans, null, strings and numbers are written the same way as in javascript\. Just like everything else in jq, these simple values take an input and produce an output \- \fB42\fR is a valid jq expression that takes an input, ignores it, and returns 42 instead\. . .SS "Array construction: []" As in JSON, \fB[]\fR is used to construct arrays, as in \fB[1,2,3]\fR\. The elements of the arrays can be any jq expression, including a pipeline\. All of the results produced by all of the expressions are collected into one big array\. You can use it to construct an array out of a known quantity of values (as in \fB[\.foo, \.bar, \.baz]\fR) or to "collect" all the results of a filter into an array (as in \fB[\.items[]\.name]\fR) . .P Once you understand the "," operator, you can look at jq\'s array syntax in a different light: the expression \fB[1,2,3]\fR is not using a built\-in syntax for comma\-separated arrays, but is instead applying the \fB[]\fR operator (collect results) to the expression 1,2,3 (which produces three different results)\. . .P If you have a filter \fBX\fR that produces four results, then the expression \fB[X]\fR will produce a single result, an array of four elements\. . .IP "" 4 . .nf jq \'[\.user, \.projects[]]\' {"user":"stedolan", "projects": ["jq", "wikiflow"]} => ["stedolan", "jq", "wikiflow"] jq \'[ \.[] | \. * 2]\' [1, 2, 3] => [2, 4, 6] . .fi . .IP "" 0 . .SS "Object Construction: {}" Like JSON, \fB{}\fR is for constructing objects (aka dictionaries or hashes), as in: \fB{"a": 42, "b": 17}\fR\. . .P If the keys are "identifier\-like", then the quotes can be left off, as in \fB{a:42, b:17}\fR\. Keys generated by expressions need to be parenthesized, e\.g\., \fB{("a"+"b"):59}\fR\. . .P The value can be any expression (although you may need to wrap it in parentheses if it\'s a complicated one), which gets applied to the {} expression\'s input (remember, all filters have an input and an output)\. . .IP "" 4 . .nf {foo: \.bar} . .fi . .IP "" 0 . .P will produce the JSON object \fB{"foo": 42}\fR if given the JSON object \fB{"bar":42, "baz":43}\fR as its input\. You can use this to select particular fields of an object: if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write . .IP "" 4 . .nf {user: \.user, title: \.title} . .fi . .IP "" 0 . .P Because that is so common, there\'s a shortcut syntax for it: \fB{user, title}\fR\. . .P If one of the expressions produces multiple results, multiple dictionaries will be produced\. If the input\'s . .IP "" 4 . .nf {"user":"stedolan","titles":["JQ Primer", "More JQ"]} . .fi . .IP "" 0 . .P then the expression . .IP "" 4 . .nf {user, title: \.titles[]} . .fi . .IP "" 0 . .P will produce two outputs: . .IP "" 4 . .nf {"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"} . .fi . .IP "" 0 . .P Putting parentheses around the key means it will be evaluated as an expression\. With the same input as above, . .IP "" 4 . .nf {(\.user): \.titles} . .fi . .IP "" 0 . .P produces . .IP "" 4 . .nf {"stedolan": ["JQ Primer", "More JQ"]} jq \'{user, title: \.titles[]}\' {"user":"stedolan","titles":["JQ Primer", "More JQ"]} => {"user":"stedolan", "title": "JQ Primer"}, {"user":"stedolan", "title": "More JQ"} jq \'{(\.user): \.titles}\' {"user":"stedolan","titles":["JQ Primer", "More JQ"]} => {"stedolan": ["JQ Primer", "More JQ"]} . .fi . .IP "" 0 . .SS "Recursive Descent: \.\." Recursively descends \fB\.\fR, producing every value\. This is the same as the zero\-argument \fBrecurse\fR builtin (see below)\. This is intended to resemble the XPath \fB//\fR operator\. Note that \fB\.\.a\fR does not work; use \fB\.\.|\.a\fR instead\. In the example below we use \fB\.\.|\.a?\fR to find all the values of object keys "a" in any object found "below" \fB\.\fR\. . .P This is particularly useful in conjunction with \fBpath(EXP)\fR (also see below) and the \fB?\fR operator\. . .IP "" 4 . .nf jq \'\.\.|\.a?\' [[{"a":1}]] => 1 . .fi . .IP "" 0 . .SH "BUILTIN OPERATORS AND FUNCTIONS" Some jq operator (for instance, \fB+\fR) do different things depending on the type of their arguments (arrays, numbers, etc\.)\. However, jq never does implicit type conversions\. If you try to add a string to an object you\'ll get an error message and no result\. . .SS "Addition: +" The operator \fB+\fR takes two filters, applies them both to the same input, and adds the results together\. What "adding" means depends on the types involved: . .IP "\(bu" 4 \fBNumbers\fR are added by normal arithmetic\. . .IP "\(bu" 4 \fBArrays\fR are added by being concatenated into a larger array\. . .IP "\(bu" 4 \fBStrings\fR are added by being joined into a larger string\. . .IP "\(bu" 4 \fBObjects\fR are added by merging, that is, inserting all the key\-value pairs from both objects into a single combined object\. If both objects contain a value for the same key, the object on the right of the \fB+\fR wins\. (For recursive merge use the \fB*\fR operator\.) . .IP "" 0 . .P \fBnull\fR can be added to any value, and returns the other value unchanged\. . .IP "" 4 . .nf jq \'\.a + 1\' {"a": 7} => 8 jq \'\.a + \.b\' {"a": [1,2], "b": [3,4]} => [1,2,3,4] jq \'\.a + null\' {"a": 1} => 1 jq \'\.a + 1\' {} => 1 jq \'{a: 1} + {b: 2} + {c: 3} + {a: 42}\' null => {"a": 42, "b": 2, "c": 3} . .fi . .IP "" 0 . .SS "Subtraction: \-" As well as normal arithmetic subtraction on numbers, the \fB\-\fR operator can be used on arrays to remove all occurrences of the second array\'s elements from the first array\. . .IP "" 4 . .nf jq \'4 \- \.a\' {"a":3} => 1 jq \'\. \- ["xml", "yaml"]\' ["xml", "yaml", "json"] => ["json"] . .fi . .IP "" 0 . .SS "Multiplication, division, modulo: *, /, and %" These infix operators behave as expected when given two numbers\. Division by zero raises an error\. \fBx % y\fR computes x modulo y\. . .P Multiplying a string by a number produces the concatenation of that string that many times\. \fB"x" * 0\fR produces \fBnull\fR\. . .P Dividing a string by another splits the first using the second as separators\. . .P Multiplying two objects will merge them recursively: this works like addition but if both objects contain a value for the same key, and the values are objects, the two are merged with the same strategy\. . .IP "" 4 . .nf jq \'10 / \. * 3\' 5 => 6 jq \'\. / ", "\' "a, b,c,d, e" => ["a","b,c,d","e"] jq \'{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}\' null => {"k": {"a": 0, "b": 2, "c": 3}} jq \'\.[] | (1 / \.)?\' [1,0,\-1] => 1, \-1 . .fi . .IP "" 0 . .SS "length" The builtin function \fBlength\fR gets the length of various different types of value: . .IP "\(bu" 4 The length of a \fBstring\fR is the number of Unicode codepoints it contains (which will be the same as its JSON\-encoded length in bytes if it\'s pure ASCII)\. . .IP "\(bu" 4 The length of an \fBarray\fR is the number of elements\. . .IP "\(bu" 4 The length of an \fBobject\fR is the number of key\-value pairs\. . .IP "\(bu" 4 The length of \fBnull\fR is zero\. . .IP jq \'\.[] | length\' [[1,2], "string", {"a":2}, null] => 2, 6, 1, 0 . .IP "" 0 . .SS "utf8bytelength" The builtin function \fButf8bytelength\fR outputs the number of bytes used to encode a string in UTF\-8\. . .IP "" 4 . .nf jq \'utf8bytelength\' "\eu03bc" => 2 . .fi . .IP "" 0 . .SS "keys, keys_unsorted" The builtin function \fBkeys\fR, when given an object, returns its keys in an array\. . .P The keys are sorted "alphabetically", by unicode codepoint order\. This is not an order that makes particular sense in any particular language, but you can count on it being the same for any two objects with the same set of keys, regardless of locale settings\. . .P When \fBkeys\fR is given an array, it returns the valid indices for that array: the integers from 0 to length\-1\. . .P The \fBkeys_unsorted\fR function is just like \fBkeys\fR, but if the input is an object then the keys will not be sorted, instead the keys will roughly be in insertion order\. . .IP "" 4 . .nf jq \'keys\' {"abc": 1, "abcd": 2, "Foo": 3} => ["Foo", "abc", "abcd"] jq \'keys\' [42,3,35] => [0,1,2] . .fi . .IP "" 0 . .SS "has(key)" The builtin function \fBhas\fR returns whether the input object has the given key, or the input array has an element at the given index\. . .P \fBhas($key)\fR has the same effect as checking whether \fB$key\fR is a member of the array returned by \fBkeys\fR, although \fBhas\fR will be faster\. . .IP "" 4 . .nf jq \'map(has("foo"))\' [{"foo": 42}, {}] => [true, false] jq \'map(has(2))\' [[0,1], ["a","b","c"]] => [false, true] . .fi . .IP "" 0 . .SS "in" The builtin function \fBin\fR returns whether or not the input key is in the given object, or the input index corresponds to an element in the given array\. It is, essentially, an inversed version of \fBhas\fR\. . .IP "" 4 . .nf jq \'\.[] | in({"foo": 42})\' ["foo", "bar"] => true, false jq \'map(in([0,1]))\' [2, 0] => [false, true] . .fi . .IP "" 0 . .SS "map(x), map_values(x)" For any filter \fBx\fR, \fBmap(x)\fR will run that filter for each element of the input array, and return the outputs in a new array\. \fBmap(\.+1)\fR will increment each element of an array of numbers\. . .P Similarly, \fBmap_values(x)\fR will run that filter for each element, but it will return an object when an object is passed\. . .P \fBmap(x)\fR is equivalent to \fB[\.[] | x]\fR\. In fact, this is how it\'s defined\. Similarly, \fBmap_values(x)\fR is defined as \fB\.[] |= x\fR\. . .IP "" 4 . .nf jq \'map(\.+1)\' [1,2,3] => [2,3,4] jq \'map_values(\.+1)\' {"a": 1, "b": 2, "c": 3} => {"a": 2, "b": 3, "c": 4} . .fi . .IP "" 0 . .SS "path(path_expression)" Outputs array representations of the given path expression in \fB\.\fR\. The outputs are arrays of strings (object keys) and/or numbers (array indices)\. . .P Path expressions are jq expressions like \fB\.a\fR, but also \fB\.[]\fR\. There are two types of path expressions: ones that can match exactly, and ones that cannot\. For example, \fB\.a\.b\.c\fR is an exact match path expression, while \fB\.a[]\.b\fR is not\. . .P \fBpath(exact_path_expression)\fR will produce the array representation of the path expression even if it does not exist in \fB\.\fR, if \fB\.\fR is \fBnull\fR or an array or an object\. . .P \fBpath(pattern)\fR will produce array representations of the paths matching \fBpattern\fR if the paths exist in \fB\.\fR\. . .P Note that the path expressions are not different from normal expressions\. The expression \fBpath(\.\.|select(type=="boolean"))\fR outputs all the paths to boolean values in \fB\.\fR, and only those paths\. . .IP "" 4 . .nf jq \'path(\.a[0]\.b)\' null => ["a",0,"b"] jq \'[path(\.\.)]\' {"a":[{"b":1}]} => [[],["a"],["a",0],["a",0,"b"]] . .fi . .IP "" 0 . .SS "del(path_expression)" The builtin function \fBdel\fR removes a key and its corresponding value from an object\. . .IP "" 4 . .nf jq \'del(\.foo)\' {"foo": 42, "bar": 9001, "baz": 42} => {"bar": 9001, "baz": 42} jq \'del(\.[1, 2])\' ["foo", "bar", "baz"] => ["foo"] . .fi . .IP "" 0 . .SS "getpath(PATHS)" The builtin function \fBgetpath\fR outputs the values in \fB\.\fR found at each path in \fBPATHS\fR\. . .IP "" 4 . .nf jq \'getpath(["a","b"])\' null => null jq \'[getpath(["a","b"], ["a","c"])]\' {"a":{"b":0, "c":1}} => [0, 1] . .fi . .IP "" 0 . .SS "setpath(PATHS; VALUE)" The builtin function \fBsetpath\fR sets the \fBPATHS\fR in \fB\.\fR to \fBVALUE\fR\. . .IP "" 4 . .nf jq \'setpath(["a","b"]; 1)\' null => {"a": {"b": 1}} jq \'setpath(["a","b"]; 1)\' {"a":{"b":0}} => {"a": {"b": 1}} jq \'setpath([0,"a"]; 1)\' null => [{"a":1}] . .fi . .IP "" 0 . .SS "delpaths(PATHS)" The builtin function \fBdelpaths\fR sets the \fBPATHS\fR in \fB\.\fR\. \fBPATHS\fR must be an array of paths, where each path is an array of strings and numbers\. . .IP "" 4 . .nf jq \'delpaths([["a","b"]])\' {"a":{"b":1},"x":{"y":2}} => {"a":{},"x":{"y":2}} . .fi . .IP "" 0 . .SS "to_entries, from_entries, with_entries" These functions convert between an object and an array of key\-value pairs\. If \fBto_entries\fR is passed an object, then for each \fBk: v\fR entry in the input, the output array includes \fB{"key": k, "value": v}\fR\. . .P \fBfrom_entries\fR does the opposite conversion, and \fBwith_entries(foo)\fR is a shorthand for \fBto_entries | map(foo) | from_entries\fR, useful for doing some operation to all keys and values of an object\. \fBfrom_entries\fR accepts key, Key, name, Name, value and Value as keys\. . .IP "" 4 . .nf jq \'to_entries\' {"a": 1, "b": 2} => [{"key":"a", "value":1}, {"key":"b", "value":2}] jq \'from_entries\' [{"key":"a", "value":1}, {"key":"b", "value":2}] => {"a": 1, "b": 2} jq \'with_entries(\.key |= "KEY_" + \.)\' {"a": 1, "b": 2} => {"KEY_a": 1, "KEY_b": 2} . .fi . .IP "" 0 . .SS "select(boolean_expression)" The function \fBselect(foo)\fR produces its input unchanged if \fBfoo\fR returns true for that input, and produces no output otherwise\. . .P It\'s useful for filtering lists: \fB[1,2,3] | map(select(\. >= 2))\fR will give you \fB[2,3]\fR\. . .IP "" 4 . .nf jq \'map(select(\. >= 2))\' [1,5,3,0,7] => [5,3,7] jq \'\.[] | select(\.id == "second")\' [{"id": "first", "val": 1}, {"id": "second", "val": 2}] => {"id": "second", "val": 2} . .fi . .IP "" 0 . .SS "arrays, objects, iterables, booleans, numbers, normals, finites, strings, nulls, values, scalars" These built\-ins select only inputs that are arrays, objects, iterables (arrays or objects), booleans, numbers, normal numbers, finite numbers, strings, null, non\-null values, and non\-iterables, respectively\. . .IP "" 4 . .nf jq \'\.[]|numbers\' [[],{},1,"foo",null,true,false] => 1 . .fi . .IP "" 0 . .SS "empty" \fBempty\fR returns no results\. None at all\. Not even \fBnull\fR\. . .P It\'s useful on occasion\. You\'ll know if you need it :) . .IP "" 4 . .nf jq \'1, empty, 2\' null => 1, 2 jq \'[1,2,empty,3]\' null => [1,2,3] . .fi . .IP "" 0 . .SS "error(message)" Produces an error, just like \fB\.a\fR applied to values other than null and objects would, but with the given message as the error\'s value\. Errors can be caught with try/catch; see below\. . .SS "halt" Stops the jq program with no further outputs\. jq will exit with exit status \fB0\fR\. . .SS "halt_error, halt_error(exit_code)" Stops the jq program with no further outputs\. The input will be printed on \fBstderr\fR as raw output (i\.e\., strings will not have double quotes) with no decoration, not even a newline\. . .P The given \fBexit_code\fR (defaulting to \fB5\fR) will be jq\'s exit status\. . .P For example, \fB"Error: somthing went wrong\en"|halt_error(1)\fR\. . .SS "$__loc__" Produces an object with a "file" key and a "line" key, with the filename and line number where \fB$__loc__\fR occurs, as values\. . .IP "" 4 . .nf jq \'try error("\e($__loc__)") catch \.\' null => "{\e"file\e":\e"\e",\e"line\e":1}" . .fi . .IP "" 0 . .SS "paths, paths(node_filter), leaf_paths" \fBpaths\fR outputs the paths to all the elements in its input (except it does not output the empty list, representing \. itself)\. . .P \fBpaths(f)\fR outputs the paths to any values for which \fBf\fR is true\. That is, \fBpaths(numbers)\fR outputs the paths to all numeric values\. . .P \fBleaf_paths\fR is an alias of \fBpaths(scalars)\fR; \fBleaf_paths\fR is \fIdeprecated\fR and will be removed in the next major release\. . .IP "" 4 . .nf jq \'[paths]\' [1,[[],{"a":2}]] => [[0],[1],[1,0],[1,1],[1,1,"a"]] jq \'[paths(scalars)]\' [1,[[],{"a":2}]] => [[0],[1,1,"a"]] . .fi . .IP "" 0 . .SS "add" The filter \fBadd\fR takes as input an array, and produces as output the elements of the array added together\. This might mean summed, concatenated or merged depending on the types of the elements of the input array \- the rules are the same as those for the \fB+\fR operator (described above)\. . .P If the input is an empty array, \fBadd\fR returns \fBnull\fR\. . .IP "" 4 . .nf jq \'add\' ["a","b","c"] => "abc" jq \'add\' [1, 2, 3] => 6 jq \'add\' [] => null . .fi . .IP "" 0 . .SS "any, any(condition), any(generator; condition)" The filter \fBany\fR takes as input an array of boolean values, and produces \fBtrue\fR as output if any of the elements of the array are \fBtrue\fR\. . .P If the input is an empty array, \fBany\fR returns \fBfalse\fR\. . .P The \fBany(condition)\fR form applies the given condition to the elements of the input array\. . .P The \fBany(generator; condition)\fR form applies the given condition to all the outputs of the given generator\. . .IP "" 4 . .nf jq \'any\' [true, false] => true jq \'any\' [false, false] => false jq \'any\' [] => false . .fi . .IP "" 0 . .SS "all, all(condition), all(generator; condition)" The filter \fBall\fR takes as input an array of boolean values, and produces \fBtrue\fR as output if all of the elements of the array are \fBtrue\fR\. . .P The \fBall(condition)\fR form applies the given condition to the elements of the input array\. . .P The \fBall(generator; condition)\fR form applies the given condition to all the outputs of the given generator\. . .P If the input is an empty array, \fBall\fR returns \fBtrue\fR\. . .IP "" 4 . .nf jq \'all\' [true, false] => false jq \'all\' [true, true] => true jq \'all\' [] => true . .fi . .IP "" 0 . .SS "flatten, flatten(depth)" The filter \fBflatten\fR takes as input an array of nested arrays, and produces a flat array in which all arrays inside the original array have been recursively replaced by their values\. You can pass an argument to it to specify how many levels of nesting to flatten\. . .P \fBflatten(2)\fR is like \fBflatten\fR, but going only up to two levels deep\. . .IP "" 4 . .nf jq \'flatten\' [1, [2], [[3]]] => [1, 2, 3] jq \'flatten(1)\' [1, [2], [[3]]] => [1, 2, [3]] jq \'flatten\' [[]] => [] jq \'flatten\' [{"foo": "bar"}, [{"foo": "baz"}]] => [{"foo": "bar"}, {"foo": "baz"}] . .fi . .IP "" 0 . .SS "range(upto), range(from;upto) range(from;upto;by)" The \fBrange\fR function produces a range of numbers\. \fBrange(4;10)\fR produces 6 numbers, from 4 (inclusive) to 10 (exclusive)\. The numbers are produced as separate outputs\. Use \fB[range(4;10)]\fR to get a range as an array\. . .P The one argument form generates numbers from 0 to the given number, with an increment of 1\. . .P The two argument form generates numbers from \fBfrom\fR to \fBupto\fR with an increment of 1\. . .P The three argument form generates numbers \fBfrom\fR to \fBupto\fR with an increment of \fBby\fR\. . .IP "" 4 . .nf jq \'range(2;4)\' null => 2, 3 jq \'[range(2;4)]\' null => [2,3] jq \'[range(4)]\' null => [0,1,2,3] jq \'[range(0;10;3)]\' null => [0,3,6,9] jq \'[range(0;10;\-1)]\' null => [] jq \'[range(0;\-5;\-1)]\' null => [0,\-1,\-2,\-3,\-4] . .fi . .IP "" 0 . .SS "floor" The \fBfloor\fR function returns the floor of its numeric input\. . .IP "" 4 . .nf jq \'floor\' 3\.14159 => 3 . .fi . .IP "" 0 . .SS "sqrt" The \fBsqrt\fR function returns the square root of its numeric input\. . .IP "" 4 . .nf jq \'sqrt\' 9 => 3 . .fi . .IP "" 0 . .SS "tonumber" The \fBtonumber\fR function parses its input as a number\. It will convert correctly\-formatted strings to their numeric equivalent, leave numbers alone, and give an error on all other input\. . .IP "" 4 . .nf jq \'\.[] | tonumber\' [1, "1"] => 1, 1 . .fi . .IP "" 0 . .SS "tostring" The \fBtostring\fR function prints its input as a string\. Strings are left unchanged, and all other values are JSON\-encoded\. . .IP "" 4 . .nf jq \'\.[] | tostring\' [1, "1", [1]] => "1", "1", "[1]" . .fi . .IP "" 0 . .SS "type" The \fBtype\fR function returns the type of its argument as a string, which is one of null, boolean, number, string, array or object\. . .IP "" 4 . .nf jq \'map(type)\' [0, false, [], {}, null, "hello"] => ["number", "boolean", "array", "object", "null", "string"] . .fi . .IP "" 0 . .SS "infinite, nan, isinfinite, isnan, isfinite, isnormal" Some arithmetic operations can yield infinities and "not a number" (NaN) values\. The \fBisinfinite\fR builtin returns \fBtrue\fR if its input is infinite\. The \fBisnan\fR builtin returns \fBtrue\fR if its input is a NaN\. The \fBinfinite\fR builtin returns a positive infinite value\. The \fBnan\fR builtin returns a NaN\. The \fBisnormal\fR builtin returns true if its input is a normal number\. . .P Note that division by zero raises an error\. . .P Currently most arithmetic operations operating on infinities, NaNs, and sub\-normals do not raise errors\. . .IP "" 4 . .nf jq \'\.[] | (infinite * \.) < 0\' [\-1, 1] => true, false jq \'infinite, nan | type\' null => "number", "number" . .fi . .IP "" 0 . .SS "sort, sort_by(path_expression)" The \fBsort\fR functions sorts its input, which must be an array\. Values are sorted in the following order: . .IP "\(bu" 4 \fBnull\fR . .IP "\(bu" 4 \fBfalse\fR . .IP "\(bu" 4 \fBtrue\fR . .IP "\(bu" 4 numbers . .IP "\(bu" 4 strings, in alphabetical order (by unicode codepoint value) . .IP "\(bu" 4 arrays, in lexical order . .IP "\(bu" 4 objects . .IP "" 0 . .P The ordering for objects is a little complex: first they\'re compared by comparing their sets of keys (as arrays in sorted order), and if their keys are equal then the values are compared key by key\. . .P \fBsort\fR may be used to sort by a particular field of an object, or by applying any jq filter\. . .P \fBsort_by(foo)\fR compares two elements by comparing the result of \fBfoo\fR on each element\. . .IP "" 4 . .nf jq \'sort\' [8,3,null,6] => [null,3,6,8] jq \'sort_by(\.foo)\' [{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}] => [{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}] . .fi . .IP "" 0 . .SS "group_by(path_expression)" \fBgroup_by(\.foo)\fR takes as input an array, groups the elements having the same \fB\.foo\fR field into separate arrays, and produces all of these arrays as elements of a larger array, sorted by the value of the \fB\.foo\fR field\. . .P Any jq expression, not just a field access, may be used in place of \fB\.foo\fR\. The sorting order is the same as described in the \fBsort\fR function above\. . .IP "" 4 . .nf jq \'group_by(\.foo)\' [{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}] => [[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]] . .fi . .IP "" 0 . .SS "min, max, min_by(path_exp), max_by(path_exp)" Find the minimum or maximum element of the input array\. . .P The \fBmin_by(path_exp)\fR and \fBmax_by(path_exp)\fR functions allow you to specify a particular field or property to examine, e\.g\. \fBmin_by(\.foo)\fR finds the object with the smallest \fBfoo\fR field\. . .IP "" 4 . .nf jq \'min\' [5,4,2,7] => 2 jq \'max_by(\.foo)\' [{"foo":1, "bar":14}, {"foo":2, "bar":3}] => {"foo":2, "bar":3} . .fi . .IP "" 0 . .SS "unique, unique_by(path_exp)" The \fBunique\fR function takes as input an array and produces an array of the same elements, in sorted order, with duplicates removed\. . .P The \fBunique_by(path_exp)\fR function will keep only one element for each value obtained by applying the argument\. Think of it as making an array by taking one element out of every group produced by \fBgroup\fR\. . .IP "" 4 . .nf jq \'unique\' [1,2,5,3,5,3,1,3] => [1,2,3,5] jq \'unique_by(\.foo)\' [{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}] => [{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}] jq \'unique_by(length)\' ["chunky", "bacon", "kitten", "cicada", "asparagus"] => ["bacon", "chunky", "asparagus"] . .fi . .IP "" 0 . .SS "reverse" This function reverses an array\. . .IP "" 4 . .nf jq \'reverse\' [1,2,3,4] => [4,3,2,1] . .fi . .IP "" 0 . .SS "contains(element)" The filter \fBcontains(b)\fR will produce true if b is completely contained within the input\. A string B is contained in a string A if B is a substring of A\. An array B is contained in an array A if all elements in B are contained in any element in A\. An object B is contained in object A if all of the values in B are contained in the value in A with the same key\. All other types are assumed to be contained in each other if they are equal\. . .IP "" 4 . .nf jq \'contains("bar")\' "foobar" => true jq \'contains(["baz", "bar"])\' ["foobar", "foobaz", "blarp"] => true jq \'contains(["bazzzzz", "bar"])\' ["foobar", "foobaz", "blarp"] => false jq \'contains({foo: 12, bar: [{barp: 12}]})\' {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]} => true jq \'contains({foo: 12, bar: [{barp: 15}]})\' {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]} => false . .fi . .IP "" 0 . .SS "indices(s)" Outputs an array containing the indices in \fB\.\fR where \fBs\fR occurs\. The input may be an array, in which case if \fBs\fR is an array then the indices output will be those where all elements in \fB\.\fR match those of \fBs\fR\. . .IP "" 4 . .nf jq \'indices(", ")\' "a,b, cd, efg, hijk" => [3,7,12] jq \'indices(1)\' [0,1,2,1,3,1,4] => [1,3,5] jq \'indices([1,2])\' [0,1,2,3,1,4,2,5,1,2,6,7] => [1,8] . .fi . .IP "" 0 . .SS "index(s), rindex(s)" Outputs the index of the first (\fBindex\fR) or last (\fBrindex\fR) occurrence of \fBs\fR in the input\. . .IP "" 4 . .nf jq \'index(", ")\' "a,b, cd, efg, hijk" => 3 jq \'rindex(", ")\' "a,b, cd, efg, hijk" => 12 . .fi . .IP "" 0 . .SS "inside" The filter \fBinside(b)\fR will produce true if the input is completely contained within b\. It is, essentially, an inversed version of \fBcontains\fR\. . .IP "" 4 . .nf jq \'inside("foobar")\' "bar" => true jq \'inside(["foobar", "foobaz", "blarp"])\' ["baz", "bar"] => true jq \'inside(["foobar", "foobaz", "blarp"])\' ["bazzzzz", "bar"] => false jq \'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})\' {"foo": 12, "bar": [{"barp": 12}]} => true jq \'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})\' {"foo": 12, "bar": [{"barp": 15}]} => false . .fi . .IP "" 0 . .SS "startswith(str)" Outputs \fBtrue\fR if \. starts with the given string argument\. . .IP "" 4 . .nf jq \'[\.[]|startswith("foo")]\' ["fo", "foo", "barfoo", "foobar", "barfoob"] => [false, true, false, true, false] . .fi . .IP "" 0 . .SS "endswith(str)" Outputs \fBtrue\fR if \. ends with the given string argument\. . .IP "" 4 . .nf jq \'[\.[]|endswith("foo")]\' ["foobar", "barfoo"] => [false, true] . .fi . .IP "" 0 . .SS "combinations, combinations(n)" Outputs all combinations of the elements of the arrays in the input array\. If given an argument \fBn\fR, it outputs all combinations of \fBn\fR repetitions of the input array\. . .IP "" 4 . .nf jq \'combinations\' [[1,2], [3, 4]] => [1, 3], [1, 4], [2, 3], [2, 4] jq \'combinations(2)\' [0, 1] => [0, 0], [0, 1], [1, 0], [1, 1] . .fi . .IP "" 0 . .SS "ltrimstr(str)" Outputs its input with the given prefix string removed, if it starts with it\. . .IP "" 4 . .nf jq \'[\.[]|ltrimstr("foo")]\' ["fo", "foo", "barfoo", "foobar", "afoo"] => ["fo","","barfoo","bar","afoo"] . .fi . .IP "" 0 . .SS "rtrimstr(str)" Outputs its input with the given suffix string removed, if it ends with it\. . .IP "" 4 . .nf jq \'[\.[]|rtrimstr("foo")]\' ["fo", "foo", "barfoo", "foobar", "foob"] => ["fo","","bar","foobar","foob"] . .fi . .IP "" 0 . .SS "explode" Converts an input string into an array of the string\'s codepoint numbers\. . .IP "" 4 . .nf jq \'explode\' "foobar" => [102,111,111,98,97,114] . .fi . .IP "" 0 . .SS "implode" The inverse of explode\. . .IP "" 4 . .nf jq \'implode\' [65, 66, 67] => "ABC" . .fi . .IP "" 0 . .SS "split(str)" Splits an input string on the separator argument\. . .IP "" 4 . .nf jq \'split(", ")\' "a, b,c,d, e, " => ["a","b,c,d","e",""] . .fi . .IP "" 0 . .SS "join(str)" Joins the array of elements given as input, using the argument as separator\. It is the inverse of \fBsplit\fR: that is, running \fBsplit("foo") | join("foo")\fR over any input string returns said input string\. . .P Numbers and booleans in the input are converted to strings\. Null values are treated as empty strings\. Arrays and objects in the input are not supported\. . .IP "" 4 . .nf jq \'join(", ")\' ["a","b,c,d","e"] => "a, b,c,d, e" jq \'join(" ")\' ["a",1,2\.3,true,null,false] => "a 1 2\.3 true false" . .fi . .IP "" 0 . .SS "ascii_downcase, ascii_upcase" Emit a copy of the input string with its alphabetic characters (a\-z and A\-Z) converted to the specified case\. . .SS "while(cond; update)" The \fBwhile(cond; update)\fR function allows you to repeatedly apply an update to \fB\.\fR until \fBcond\fR is false\. . .P Note that \fBwhile(cond; update)\fR is internally defined as a recursive jq function\. Recursive calls within \fBwhile\fR will not consume additional memory if \fBupdate\fR produces at most one output for each input\. See advanced topics below\. . .IP "" 4 . .nf jq \'[while(\.<100; \.*2)]\' 1 => [1,2,4,8,16,32,64] . .fi . .IP "" 0 . .SS "until(cond; next)" The \fBuntil(cond; next)\fR function allows you to repeatedly apply the expression \fBnext\fR, initially to \fB\.\fR then to its own output, until \fBcond\fR is true\. For example, this can be used to implement a factorial function (see below)\. . .P Note that \fBuntil(cond; next)\fR is internally defined as a recursive jq function\. Recursive calls within \fBuntil()\fR will not consume additional memory if \fBnext\fR produces at most one output for each input\. See advanced topics below\. . .IP "" 4 . .nf jq \'[\.,1]|until(\.[0] < 1; [\.[0] \- 1, \.[1] * \.[0]])|\.[1]\' 4 => 24 . .fi . .IP "" 0 . .SS "recurse(f), recurse, recurse(f; condition), recurse_down" The \fBrecurse(f)\fR function allows you to search through a recursive structure, and extract interesting data from all levels\. Suppose your input represents a filesystem: . .IP "" 4 . .nf {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} . .fi . .IP "" 0 . .P Now suppose you want to extract all of the filenames present\. You need to retrieve \fB\.name\fR, \fB\.children[]\.name\fR, \fB\.children[]\.children[]\.name\fR, and so on\. You can do this with: . .IP "" 4 . .nf recurse(\.children[]) | \.name . .fi . .IP "" 0 . .P When called without an argument, \fBrecurse\fR is equivalent to \fBrecurse(\.[]?)\fR\. . .P \fBrecurse(f)\fR is identical to \fBrecurse(f; \. != null)\fR and can be used without concerns about recursion depth\. . .P \fBrecurse(f; condition)\fR is a generator which begins by emitting \. and then emits in turn \.|f, \.|f|f, \.|f|f|f, \.\.\. so long as the computed value satisfies the condition\. For example, to generate all the integers, at least in principle, one could write \fBrecurse(\.+1; true)\fR\. . .P For legacy reasons, \fBrecurse_down\fR exists as an alias to calling \fBrecurse\fR without arguments\. This alias is considered \fIdeprecated\fR and will be removed in the next major release\. . .P The recursive calls in \fBrecurse\fR will not consume additional memory whenever \fBf\fR produces at most a single output for each input\. . .IP "" 4 . .nf jq \'recurse(\.foo[])\' {"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]} => {"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}, {"foo":[]}, {"foo":[{"foo":[]}]}, {"foo":[]} jq \'recurse\' {"a":0,"b":[1]} => {"a":0,"b":[1]}, 0, [1], 1 jq \'recurse(\. * \.; \. < 20)\' 2 => 2, 4, 16 . .fi . .IP "" 0 . .SS "walk(f)" The \fBwalk(f)\fR function applies f recursively to every component of the input entity\. When an array is encountered, f is first applied to its elements and then to the array itself; when an object is encountered, f is first applied to all the values and then to the object\. In practice, f will usually test the type of its input, as illustrated in the following examples\. The first example highlights the usefulness of processing the elements of an array of arrays before processing the array itself\. The second example shows how all the keys of all the objects within the input can be considered for alteration\. . .IP "" 4 . .nf jq \'walk(if type == "array" then sort else \. end)\' [[4, 1, 7], [8, 5, 2], [3, 6, 9]] => [[1,4,7],[2,5,8],[3,6,9]] jq \'walk( if type == "object" then with_entries( \.key |= sub( "^_+"; "") ) else \. end )\' [ { "_a": { "__b": 2 } } ] => [{"a":{"b":2}}] . .fi . .IP "" 0 . .SS "$ENV, env" \fB$ENV\fR is an object representing the environment variables as set when the jq program started\. . .P \fBenv\fR outputs an object representing jq\'s current environment\. . .P At the moment there is no builtin for setting environment variables\. . .IP "" 4 . .nf jq \'$ENV\.PAGER\' null => "less" jq \'env\.PAGER\' null => "less" . .fi . .IP "" 0 . .SS "transpose" Transpose a possibly jagged matrix (an array of arrays)\. Rows are padded with nulls so the result is always rectangular\. . .IP "" 4 . .nf jq \'transpose\' [[1], [2,3]] => [[1,2],[null,3]] . .fi . .IP "" 0 . .SS "bsearch(x)" bsearch(x) conducts a binary search for x in the input array\. If the input is sorted and contains x, then bsearch(x) will return its index in the array; otherwise, if the array is sorted, it will return (\-1 \- ix) where ix is an insertion point such that the array would still be sorted after the insertion of x at ix\. If the array is not sorted, bsearch(x) will return an integer that is probably of no interest\. . .IP "" 4 . .nf jq \'bsearch(0)\' [0,1] => 0 jq \'bsearch(0)\' [1,2,3] => \-1 jq \'bsearch(4) as $ix | if $ix < 0 then \.[\-(1+$ix)] = 4 else \. end\' [1,2,3] => [1,2,3,4] . .fi . .IP "" 0 . .SS "String interpolation \- \e(foo)" Inside a string, you can put an expression inside parens after a backslash\. Whatever the expression returns will be interpolated into the string\. . .IP "" 4 . .nf jq \'"The input was \e(\.), which is one less than \e(\.+1)"\' 42 => "The input was 42, which is one less than 43" . .fi . .IP "" 0 . .SS "Convert to/from JSON" The \fBtojson\fR and \fBfromjson\fR builtins dump values as JSON texts or parse JSON texts into values, respectively\. The tojson builtin differs from tostring in that tostring returns strings unmodified, while tojson encodes strings as JSON strings\. . .IP "" 4 . .nf jq \'[\.[]|tostring]\' [1, "foo", ["foo"]] => ["1","foo","[\e"foo\e"]"] jq \'[\.[]|tojson]\' [1, "foo", ["foo"]] => ["1","\e"foo\e"","[\e"foo\e"]"] jq \'[\.[]|tojson|fromjson]\' [1, "foo", ["foo"]] => [1,"foo",["foo"]] . .fi . .IP "" 0 . .SS "Format strings and escaping" The \fB@foo\fR syntax is used to format and escape strings, which is useful for building URLs, documents in a language like HTML or XML, and so forth\. \fB@foo\fR can be used as a filter on its own, the possible escapings are: . .TP \fB@text\fR: . .IP Calls \fBtostring\fR, see that function for details\. . .TP \fB@json\fR: . .IP Serializes the input as JSON\. . .TP \fB@html\fR: . .IP Applies HTML/XML escaping, by mapping the characters \fB<>&\'"\fR to their entity equivalents \fB<\fR, \fB>\fR, \fB&\fR, \fB'\fR, \fB"\fR\. . .TP \fB@uri\fR: . .IP Applies percent\-encoding, by mapping all reserved URI characters to a \fB%XX\fR sequence\. . .TP \fB@csv\fR: . .IP The input must be an array, and it is rendered as CSV with double quotes for strings, and quotes escaped by repetition\. . .TP \fB@tsv\fR: . .IP The input must be an array, and it is rendered as TSV (tab\-separated values)\. Each input array will be printed as a single line\. Fields are separated by a single tab (ascii \fB0x09\fR)\. Input characters line\-feed (ascii \fB0x0a\fR), carriage\-return (ascii \fB0x0d\fR), tab (ascii \fB0x09\fR) and backslash (ascii \fB0x5c\fR) will be output as escape sequences \fB\en\fR, \fB\er\fR, \fB\et\fR, \fB\e\e\fR respectively\. . .TP \fB@sh\fR: . .IP The input is escaped suitable for use in a command\-line for a POSIX shell\. If the input is an array, the output will be a series of space\-separated strings\. . .TP \fB@base64\fR: . .IP The input is converted to base64 as specified by RFC 4648\. . .TP \fB@base64d\fR: . .IP The inverse of \fB@base64\fR, input is decoded as specified by RFC 4648\. Note: If the decoded string is not UTF\-8, the results are undefined\. . .P This syntax can be combined with string interpolation in a useful way\. You can follow a \fB@foo\fR token with a string literal\. The contents of the string literal will \fInot\fR be escaped\. However, all interpolations made inside that string literal will be escaped\. For instance, . .IP "" 4 . .nf @uri "https://www\.google\.com/search?q=\e(\.search)" . .fi . .IP "" 0 . .P will produce the following output for the input \fB{"search":"what is jq?"}\fR: . .IP "" 4 . .nf "https://www\.google\.com/search?q=what%20is%20jq%3F" . .fi . .IP "" 0 . .P Note that the slashes, question mark, etc\. in the URL are not escaped, as they were part of the string literal\. . .IP "" 4 . .nf jq \'@html\' "This works if x < y" => "This works if x < y" jq \'@sh "echo \e(\.)"\' "O\'Hara\'s Ale" => "echo \'O\'\e\e\'\'Hara\'\e\e\'\'s Ale\'" jq \'@base64\' "This is a message" => "VGhpcyBpcyBhIG1lc3NhZ2U=" jq \'@base64d\' "VGhpcyBpcyBhIG1lc3NhZ2U=" => "This is a message" . .fi . .IP "" 0 . .SS "Dates" jq provides some basic date handling functionality, with some high\-level and low\-level builtins\. In all cases these builtins deal exclusively with time in UTC\. . .P The \fBfromdateiso8601\fR builtin parses datetimes in the ISO 8601 format to a number of seconds since the Unix epoch (1970\-01\-01T00:00:00Z)\. The \fBtodateiso8601\fR builtin does the inverse\. . .P The \fBfromdate\fR builtin parses datetime strings\. Currently \fBfromdate\fR only supports ISO 8601 datetime strings, but in the future it will attempt to parse datetime strings in more formats\. . .P The \fBtodate\fR builtin is an alias for \fBtodateiso8601\fR\. . .P The \fBnow\fR builtin outputs the current time, in seconds since the Unix epoch\. . .P Low\-level jq interfaces to the C\-library time functions are also provided: \fBstrptime\fR, \fBstrftime\fR, \fBstrflocaltime\fR, \fBmktime\fR, \fBgmtime\fR, and \fBlocaltime\fR\. Refer to your host operating system\'s documentation for the format strings used by \fBstrptime\fR and \fBstrftime\fR\. Note: these are not necessarily stable interfaces in jq, particularly as to their localization functionality\. . .P The \fBgmtime\fR builtin consumes a number of seconds since the Unix epoch and outputs a "broken down time" representation of Greenwhich Meridian time as an array of numbers representing (in this order): the year, the month (zero\-based), the day of the month (one\-based), the hour of the day, the minute of the hour, the second of the minute, the day of the week, and the day of the year \-\- all one\-based unless otherwise stated\. The day of the week number may be wrong on some systems for dates before March 1st 1900, or after December 31 2099\. . .P The \fBlocaltime\fR builtin works like the \fBgmtime\fR builtin, but using the local timezone setting\. . .P The \fBmktime\fR builtin consumes "broken down time" representations of time output by \fBgmtime\fR and \fBstrptime\fR\. . .P The \fBstrptime(fmt)\fR builtin parses input strings matching the \fBfmt\fR argument\. The output is in the "broken down time" representation consumed by \fBgmtime\fR and output by \fBmktime\fR\. . .P The \fBstrftime(fmt)\fR builtin formats a time (GMT) with the given format\. The \fBstrflocaltime\fR does the same, but using the local timezone setting\. . .P The format strings for \fBstrptime\fR and \fBstrftime\fR are described in typical C library documentation\. The format string for ISO 8601 datetime is \fB"%Y\-%m\-%dT%H:%M:%SZ"\fR\. . .P jq may not support some or all of this date functionality on some systems\. In particular, the \fB%u\fR and \fB%j\fR specifiers for \fBstrptime(fmt)\fR are not supported on macOS\. . .IP "" 4 . .nf jq \'fromdate\' "2015\-03\-05T23:51:47Z" => 1425599507 jq \'strptime("%Y\-%m\-%dT%H:%M:%SZ")\' "2015\-03\-05T23:51:47Z" => [2015,2,5,23,51,47,4,63] jq \'strptime("%Y\-%m\-%dT%H:%M:%SZ")|mktime\' "2015\-03\-05T23:51:47Z" => 1425599507 . .fi . .IP "" 0 . .SS "SQL\-Style Operators" jq provides a few SQL\-style operators\. . .TP INDEX(stream; index_expression): . .IP This builtin produces an object whose keys are computed by the given index expression applied to each value from the given stream\. . .TP JOIN($idx; stream; idx_expr; join_expr): . .IP This builtin joins the values from the given stream to the given index\. The index\'s keys are computed by applying the given index expression to each value from the given stream\. An array of the value in the stream and the corresponding value from the index is fed to the given join expression to produce each result\. . .TP JOIN($idx; stream; idx_expr): . .IP Same as \fBJOIN($idx; stream; idx_expr; \.)\fR\. . .TP JOIN($idx; idx_expr): . .IP This builtin joins the input \fB\.\fR to the given index, applying the given index expression to \fB\.\fR to compute the index key\. The join operation is as described above\. . .TP IN(s): . .IP This builtin outputs \fBtrue\fR if \fB\.\fR appears in the given stream, otherwise it outputs \fBfalse\fR\. . .TP IN(source; s): . .IP This builtin outputs \fBtrue\fR if any value in the source stream appears in the second stream, otherwise it outputs \fBfalse\fR\. . .SS "builtins" Returns a list of all builtin functions in the format \fBname/arity\fR\. Since functions with the same name but different arities are considered separate functions, \fBall/0\fR, \fBall/1\fR, and \fBall/2\fR would all be present in the list\. . .SH "CONDITIONALS AND COMPARISONS" . .SS "==, !=" The expression \'a == b\' will produce \'true\' if the result of a and b are equal (that is, if they represent equivalent JSON documents) and \'false\' otherwise\. In particular, strings are never considered equal to numbers\. If you\'re coming from Javascript, jq\'s == is like Javascript\'s === \- considering values equal only when they have the same type as well as the same value\. . .P != is "not equal", and \'a != b\' returns the opposite value of \'a == b\' . .IP "" 4 . .nf jq \'\.[] == 1\' [1, 1\.0, "1", "banana"] => true, true, false, false . .fi . .IP "" 0 . .SS "if\-then\-else" \fBif A then B else C end\fR will act the same as \fBB\fR if \fBA\fR produces a value other than false or null, but act the same as \fBC\fR otherwise\. . .P Checking for false or null is a simpler notion of "truthiness" than is found in Javascript or Python, but it means that you\'ll sometimes have to be more explicit about the condition you want: you can\'t test whether, e\.g\. a string is empty using \fBif \.name then A else B end\fR, you\'ll need something more like \fBif (\.name | length) > 0 then A else B end\fR instead\. . .P If the condition \fBA\fR produces multiple results, then \fBB\fR is evaluated once for each result that is not false or null, and \fBC\fR is evaluated once for each false or null\. . .P More cases can be added to an if using \fBelif A then B\fR syntax\. . .IP "" 4 . .nf jq \'if \. == 0 then . .fi . .IP "" 0 . .P "zero" elif \. == 1 then "one" else "many" end\' 2 => "many" . .SS ">, >=, <=, <" The comparison operators \fB>\fR, \fB>=\fR, \fB<=\fR, \fB<\fR return whether their left argument is greater than, greater than or equal to, less than or equal to or less than their right argument (respectively)\. . .P The ordering is the same as that described for \fBsort\fR, above\. . .IP "" 4 . .nf jq \'\. < 5\' 2 => true . .fi . .IP "" 0 . .SS "and/or/not" jq supports the normal Boolean operators and/or/not\. They have the same standard of truth as if expressions \- false and null are considered "false values", and anything else is a "true value"\. . .P If an operand of one of these operators produces multiple results, the operator itself will produce a result for each input\. . .P \fBnot\fR is in fact a builtin function rather than an operator, so it is called as a filter to which things can be piped rather than with special syntax, as in \fB\.foo and \.bar | not\fR\. . .P These three only produce the values "true" and "false", and so are only useful for genuine Boolean operations, rather than the common Perl/Python/Ruby idiom of "value_that_may_be_null or default"\. If you want to use this form of "or", picking between two values rather than evaluating a condition, see the "//" operator below\. . .IP "" 4 . .nf jq \'42 and "a string"\' null => true jq \'(true, false) or false\' null => true, false jq \'(true, true) and (true, false)\' null => true, false, true, false jq \'[true, false | not]\' null => [false, true] . .fi . .IP "" 0 . .SS "Alternative operator: //" A filter of the form \fBa // b\fR produces the same results as \fBa\fR, if \fBa\fR produces results other than \fBfalse\fR and \fBnull\fR\. Otherwise, \fBa // b\fR produces the same results as \fBb\fR\. . .P This is useful for providing defaults: \fB\.foo // 1\fR will evaluate to \fB1\fR if there\'s no \fB\.foo\fR element in the input\. It\'s similar to how \fBor\fR is sometimes used in Python (jq\'s \fBor\fR operator is reserved for strictly Boolean operations)\. . .IP "" 4 . .nf jq \'\.foo // 42\' {"foo": 19} => 19 jq \'\.foo // 42\' {} => 42 . .fi . .IP "" 0 . .SS "try\-catch" Errors can be caught by using \fBtry EXP catch EXP\fR\. The first expression is executed, and if it fails then the second is executed with the error message\. The output of the handler, if any, is output as if it had been the output of the expression to try\. . .P The \fBtry EXP\fR form uses \fBempty\fR as the exception handler\. . .IP "" 4 . .nf jq \'try \.a catch "\. is not an object"\' true => "\. is not an object" jq \'[\.[]|try \.a]\' [{}, true, {"a":1}] => [null, 1] jq \'try error("some exception") catch \.\' true => "some exception" . .fi . .IP "" 0 . .SS "Breaking out of control structures" A convenient use of try/catch is to break out of control structures like \fBreduce\fR, \fBforeach\fR, \fBwhile\fR, and so on\. . .P For example: . .IP "" 4 . .nf # Repeat an expression until it raises "break" as an # error, then stop repeating without re\-raising the error\. # But if the error caught is not "break" then re\-raise it\. try repeat(exp) catch \.=="break" then empty else error; . .fi . .IP "" 0 . .P jq has a syntax for named lexical labels to "break" or "go (back) to": . .IP "" 4 . .nf label $out | \.\.\. break $out \.\.\. . .fi . .IP "" 0 . .P The \fBbreak $label_name\fR expression will cause the program to to act as though the nearest (to the left) \fBlabel $label_name\fR produced \fBempty\fR\. . .P The relationship between the \fBbreak\fR and corresponding \fBlabel\fR is lexical: the label has to be "visible" from the break\. . .P To break out of a \fBreduce\fR, for example: . .IP "" 4 . .nf label $out | reduce \.[] as $item (null; if \.==false then break $out else \.\.\. end) . .fi . .IP "" 0 . .P The following jq program produces a syntax error: . .IP "" 4 . .nf break $out . .fi . .IP "" 0 . .P because no label \fB$out\fR is visible\. . .SS "Error Suppression / Optional Operator: ?" The \fB?\fR operator, used as \fBEXP?\fR, is shorthand for \fBtry EXP\fR\. . .IP "" 4 . .nf jq \'[\.[]|(\.a)?]\' [{}, true, {"a":1}] => [null, 1] . .fi . .IP "" 0 . .SH "REGULAR EXPRESSIONS (PCRE)" jq uses the Oniguruma regular expression library, as do php, ruby, TextMate, Sublime Text, etc, so the description here will focus on jq specifics\. . .P The jq regex filters are defined so that they can be used using one of these patterns: . .IP "" 4 . .nf STRING | FILTER( REGEX ) STRING | FILTER( REGEX; FLAGS ) STRING | FILTER( [REGEX] ) STRING | FILTER( [REGEX, FLAGS] ) . .fi . .IP "" 0 . .P where: * STRING, REGEX and FLAGS are jq strings and subject to jq string interpolation; * REGEX, after string interpolation, should be a valid PCRE regex; * FILTER is one of \fBtest\fR, \fBmatch\fR, or \fBcapture\fR, as described below\. . .P FLAGS is a string consisting of one of more of the supported flags: . .IP "\(bu" 4 \fBg\fR \- Global search (find all matches, not just the first) . .IP "\(bu" 4 \fBi\fR \- Case insensitive search . .IP "\(bu" 4 \fBm\fR \- Multi line mode (\'\.\' will match newlines) . .IP "\(bu" 4 \fBn\fR \- Ignore empty matches . .IP "\(bu" 4 \fBp\fR \- Both s and m modes are enabled . .IP "\(bu" 4 \fBs\fR \- Single line mode (\'^\' \-> \'\eA\', \'$\' \-> \'\eZ\') . .IP "\(bu" 4 \fBl\fR \- Find longest possible matches . .IP "\(bu" 4 \fBx\fR \- Extended regex format (ignore whitespace and comments) . .IP "" 0 . .P To match whitespace in an x pattern use an escape such as \es, e\.g\. . .IP "\(bu" 4 test( "a\esb", "x" )\. . .IP "" 0 . .P Note that certain flags may also be specified within REGEX, e\.g\. . .IP "\(bu" 4 jq \-n \'("test", "TEst", "teST", "TEST") | test( "(?i)te(?\-i)st" )\' . .IP "" 0 . .P evaluates to: true, true, false, false\. . .SS "test(val), test(regex; flags)" Like \fBmatch\fR, but does not return match objects, only \fBtrue\fR or \fBfalse\fR for whether or not the regex matches the input\. . .IP "" 4 . .nf jq \'test("foo")\' "foo" => true jq \'\.[] | test("a b c # spaces are ignored"; "ix")\' ["xabcd", "ABC"] => true, true . .fi . .IP "" 0 . .SS "match(val), match(regex; flags)" \fBmatch\fR outputs an object for each match it finds\. Matches have the following fields: . .IP "\(bu" 4 \fBoffset\fR \- offset in UTF\-8 codepoints from the beginning of the input . .IP "\(bu" 4 \fBlength\fR \- length in UTF\-8 codepoints of the match . .IP "\(bu" 4 \fBstring\fR \- the string that it matched . .IP "\(bu" 4 \fBcaptures\fR \- an array of objects representing capturing groups\. . .IP "" 0 . .P Capturing group objects have the following fields: . .IP "\(bu" 4 \fBoffset\fR \- offset in UTF\-8 codepoints from the beginning of the input . .IP "\(bu" 4 \fBlength\fR \- length in UTF\-8 codepoints of this capturing group . .IP "\(bu" 4 \fBstring\fR \- the string that was captured . .IP "\(bu" 4 \fBname\fR \- the name of the capturing group (or \fBnull\fR if it was unnamed) . .IP "" 0 . .P Capturing groups that did not match anything return an offset of \-1 . .IP "" 4 . .nf jq \'match("(abc)+"; "g")\' "abc abc" => {"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}, {"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]} jq \'match("foo")\' "foo bar foo" => {"offset": 0, "length": 3, "string": "foo", "captures": []} jq \'match(["foo", "ig"])\' "foo bar FOO" => {"offset": 0, "length": 3, "string": "foo", "captures": []}, {"offset": 8, "length": 3, "string": "FOO", "captures": []} jq \'match("foo (?bar)? foo"; "ig")\' "foo bar foo foo foo" => {"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}, {"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": \-1, "length": 0, "string": null, "name": "bar123"}]} jq \'[ match("\."; "g")] | length\' "abc" => 3 . .fi . .IP "" 0 . .SS "capture(val), capture(regex; flags)" Collects the named captures in a JSON object, with the name of each capture as the key, and the matched string as the corresponding value\. . .IP "" 4 . .nf jq \'capture("(?[a\-z]+)\-(?[0\-9]+)")\' "xyzzy\-14" => { "a": "xyzzy", "n": "14" } . .fi . .IP "" 0 . .SS "scan(regex), scan(regex; flags)" Emit a stream of the non\-overlapping substrings of the input that match the regex in accordance with the flags, if any have been specified\. If there is no match, the stream is empty\. To capture all the matches for each input string, use the idiom \fB[ expr ]\fR, e\.g\. \fB[ scan(regex) ]\fR\. . .SS "split(regex; flags)" For backwards compatibility, \fBsplit\fR splits on a string, not a regex\. . .SS "splits(regex), splits(regex; flags)" These provide the same results as their \fBsplit\fR counterparts, but as a stream instead of an array\. . .SS "sub(regex; tostring) sub(regex; string; flags)" Emit the string obtained by replacing the first match of regex in the input string with \fBtostring\fR, after interpolation\. \fBtostring\fR should be a jq string, and may contain references to named captures\. The named captures are, in effect, presented as a JSON object (as constructed by \fBcapture\fR) to \fBtostring\fR, so a reference to a captured variable named "x" would take the form: "(\.x)"\. . .SS "gsub(regex; string), gsub(regex; string; flags)" \fBgsub\fR is like \fBsub\fR but all the non\-overlapping occurrences of the regex are replaced by the string, after interpolation\. . .SH "ADVANCED FEATURES" Variables are an absolute necessity in most programming languages, but they\'re relegated to an "advanced feature" in jq\. . .P In most languages, variables are the only means of passing around data\. If you calculate a value, and you want to use it more than once, you\'ll need to store it in a variable\. To pass a value to another part of the program, you\'ll need that part of the program to define a variable (as a function parameter, object member, or whatever) in which to place the data\. . .P It is also possible to define functions in jq, although this is is a feature whose biggest use is defining jq\'s standard library (many jq functions such as \fBmap\fR and \fBfind\fR are in fact written in jq)\. . .P jq has reduction operators, which are very powerful but a bit tricky\. Again, these are mostly used internally, to define some useful bits of jq\'s standard library\. . .P It may not be obvious at first, but jq is all about generators (yes, as often found in other languages)\. Some utilities are provided to help deal with generators\. . .P Some minimal I/O support (besides reading JSON from standard input, and writing JSON to standard output) is available\. . .P Finally, there is a module/library system\. . .SS "Variable / Symbolic Binding Operator: \.\.\. as $identifier | \.\.\." In jq, all filters have an input and an output, so manual plumbing is not necessary to pass a value from one part of a program to the next\. Many expressions, for instance \fBa + b\fR, pass their input to two distinct subexpressions (here \fBa\fR and \fBb\fR are both passed the same input), so variables aren\'t usually necessary in order to use a value twice\. . .P For instance, calculating the average value of an array of numbers requires a few variables in most languages \- at least one to hold the array, perhaps one for each element or for a loop counter\. In jq, it\'s simply \fBadd / length\fR \- the \fBadd\fR expression is given the array and produces its sum, and the \fBlength\fR expression is given the array and produces its length\. . .P So, there\'s generally a cleaner way to solve most problems in jq than defining variables\. Still, sometimes they do make things easier, so jq lets you define variables using \fBexpression as $variable\fR\. All variable names start with \fB$\fR\. Here\'s a slightly uglier version of the array\-averaging example: . .IP "" 4 . .nf length as $array_length | add / $array_length . .fi . .IP "" 0 . .P We\'ll need a more complicated problem to find a situation where using variables actually makes our lives easier\. . .P Suppose we have an array of blog posts, with "author" and "title" fields, and another object which is used to map author usernames to real names\. Our input looks like: . .IP "" 4 . .nf {"posts": [{"title": "Frist psot", "author": "anon"}, {"title": "A well\-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}} . .fi . .IP "" 0 . .P We want to produce the posts with the author field containing a real name, as in: . .IP "" 4 . .nf {"title": "Frist psot", "author": "Anonymous Coward"} {"title": "A well\-written article", "author": "Person McPherson"} . .fi . .IP "" 0 . .P We use a variable, $names, to store the realnames object, so that we can refer to it later when looking up author usernames: . .IP "" 4 . .nf \&\.realnames as $names | \.posts[] | {title, author: $names[\.author]} . .fi . .IP "" 0 . .P The expression \fBexp as $x | \.\.\.\fR means: for each value of expression \fBexp\fR, run the rest of the pipeline with the entire original input, and with \fB$x\fR set to that value\. Thus \fBas\fR functions as something of a foreach loop\. . .P Just as \fB{foo}\fR is a handy way of writing \fB{foo: \.foo}\fR, so \fB{$foo}\fR is a handy way of writing \fB{foo:$foo}\fR\. . .P Multiple variables may be declared using a single \fBas\fR expression by providing a pattern that matches the structure of the input (this is known as "destructuring"): . .IP "" 4 . .nf \&\. as {realnames: $names, posts: [$first, $second]} | \.\.\. . .fi . .IP "" 0 . .P The variable declarations in array patterns (e\.g\., \fB\. as [$first, $second]\fR) bind to the elements of the array in from the element at index zero on up, in order\. When there is no value at the index for an array pattern element, \fBnull\fR is bound to that variable\. . .P Variables are scoped over the rest of the expression that defines them, so . .IP "" 4 . .nf \&\.realnames as $names | (\.posts[] | {title, author: $names[\.author]}) . .fi . .IP "" 0 . .P will work, but . .IP "" 4 . .nf (\.realnames as $names | \.posts[]) | {title, author: $names[\.author]} . .fi . .IP "" 0 . .P won\'t\. . .P For programming language theorists, it\'s more accurate to say that jq variables are lexically\-scoped bindings\. In particular there\'s no way to change the value of a binding; one can only setup a new binding with the same name, but which will not be visible where the old one was\. . .IP "" 4 . .nf jq \'\.bar as $x | \.foo | \. + $x\' {"foo":10, "bar":200} => 210 jq \'\. as $i|[(\.*2|\. as $i| $i), $i]\' 5 => [10,5] jq \'\. as [$a, $b, {c: $c}] | $a + $b + $c\' [2, 3, {"c": 4, "d": 5}] => 9 jq \'\.[] as [$a, $b] | {a: $a, b: $b}\' [[0], [0, 1], [2, 1, 0]] => {"a":0,"b":null}, {"a":0,"b":1}, {"a":2,"b":1} . .fi . .IP "" 0 . .SS "Defining Functions" You can give a filter a name using "def" syntax: . .IP "" 4 . .nf def increment: \. + 1; . .fi . .IP "" 0 . .P From then on, \fBincrement\fR is usable as a filter just like a builtin function (in fact, this is how many of the builtins are defined)\. A function may take arguments: . .IP "" 4 . .nf def map(f): [\.[] | f]; . .fi . .IP "" 0 . .P Arguments are passed as \fIfilters\fR (functions with no arguments), \fInot\fR as values\. The same argument may be referenced multiple times with different inputs (here \fBf\fR is run for each element of the input array)\. Arguments to a function work more like callbacks than like value arguments\. This is important to understand\. Consider: . .IP "" 4 . .nf def foo(f): f|f; 5|foo(\.*2) . .fi . .IP "" 0 . .P The result will be 20 because \fBf\fR is \fB\.*2\fR, and during the first invocation of \fBf\fR \fB\.\fR will be 5, and the second time it will be 10 (5 * 2), so the result will be 20\. Function arguments are filters, and filters expect an input when invoked\. . .P If you want the value\-argument behaviour for defining simple functions, you can just use a variable: . .IP "" 4 . .nf def addvalue(f): f as $f | map(\. + $f); . .fi . .IP "" 0 . .P Or use the short\-hand: . .IP "" 4 . .nf def addvalue($f): \.\.\.; . .fi . .IP "" 0 . .P With either definition, \fBaddvalue(\.foo)\fR will add the current input\'s \fB\.foo\fR field to each element of the array\. Do note that calling \fBaddvalue(\.[])\fR will cause the \fBmap(\. + $f)\fR part to be evaluated once per value in the value of \fB\.\fR at the call site\. . .P Multiple definitions using the same function name are allowed\. Each re\-definition replaces the previous one for the same number of function arguments, but only for references from functions (or main program) subsequent to the re\-definition\. See also the section below on scoping\. . .IP "" 4 . .nf jq \'def addvalue(f): \. + [f]; map(addvalue(\.[0]))\' [[1,2],[10,20]] => [[1,2,1], [10,20,10]] jq \'def addvalue(f): f as $x | map(\. + $x); addvalue(\.[0])\' [[1,2],[10,20]] => [[1,2,1,2], [10,20,1,2]] . .fi . .IP "" 0 . .SS "Scoping" There are two types of symbols in jq: value bindings (a\.k\.a\., "variables"), and functions\. Both are scoped lexically, with expressions being able to refer only to symbols that have been defined "to the left" of them\. The only exception to this rule is that functions can refer to themselves so as to be able to create recursive functions\. . .P For example, in the following expression there is a binding which is visible "to the right" of it, \fB\.\.\. | \.*3 as $times_three | [\. + $times_three] | \.\.\.\fR, but not "to the left"\. Consider this expression now, \fB\.\.\. | (\.*3 as $times_three | [\.+ $times_three]) | \.\.\.\fR: here the binding \fB$times_three\fR is \fInot\fR visible past the closing parenthesis\. . .SS "Reduce" The \fBreduce\fR syntax in jq allows you to combine all of the results of an expression by accumulating them into a single answer\. As an example, we\'ll pass \fB[3,2,1]\fR to this expression: . .IP "" 4 . .nf reduce \.[] as $item (0; \. + $item) . .fi . .IP "" 0 . .P For each result that \fB\.[]\fR produces, \fB\. + $item\fR is run to accumulate a running total, starting from 0\. In this example, \fB\.[]\fR produces the results 3, 2, and 1, so the effect is similar to running something like this: . .IP "" 4 . .nf 0 | (3 as $item | \. + $item) | (2 as $item | \. + $item) | (1 as $item | \. + $item) jq \'reduce \.[] as $item (0; \. + $item)\' [10,2,5,3] => 20 . .fi . .IP "" 0 . .SS "isempty(exp)" Returns true if \fBexp\fR produces no outputs, false otherwise\. . .IP "" 4 . .nf jq \'isempty(empty)\' null => true . .fi . .IP "" 0 . .SS "limit(n; exp)" The \fBlimit\fR function extracts up to \fBn\fR outputs from \fBexp\fR\. . .IP "" 4 . .nf jq \'[limit(3;\.[])]\' [0,1,2,3,4,5,6,7,8,9] => [0,1,2] . .fi . .IP "" 0 . .SS "first(expr), last(expr), nth(n; expr)" The \fBfirst(expr)\fR and \fBlast(expr)\fR functions extract the first and last values from \fBexpr\fR, respectively\. . .P The \fBnth(n; expr)\fR function extracts the nth value output by \fBexpr\fR\. This can be defined as \fBdef nth(n; expr): last(limit(n + 1; expr));\fR\. Note that \fBnth(n; expr)\fR doesn\'t support negative values of \fBn\fR\. . .IP "" 4 . .nf jq \'[first(range(\.)), last(range(\.)), nth(\./2; range(\.))]\' 10 => [0,9,5] . .fi . .IP "" 0 . .SS "first, last, nth(n)" The \fBfirst\fR and \fBlast\fR functions extract the first and last values from any array at \fB\.\fR\. . .P The \fBnth(n)\fR function extracts the nth value of any array at \fB\.\fR\. . .IP "" 4 . .nf jq \'[range(\.)]|[first, last, nth(5)]\' 10 => [0,9,5] . .fi . .IP "" 0 . .SS "foreach" The \fBforeach\fR syntax is similar to \fBreduce\fR, but intended to allow the construction of \fBlimit\fR and reducers that produce intermediate results (see example)\. . .P The form is \fBforeach EXP as $var (INIT; UPDATE; EXTRACT)\fR\. Like \fBreduce\fR, \fBINIT\fR is evaluated once to produce a state value, then each output of \fBEXP\fR is bound to \fB$var\fR, \fBUPDATE\fR is evaluated for each output of \fBEXP\fR with the current state and with \fB$var\fR visible\. Each value output by \fBUPDATE\fR replaces the previous state\. Finally, \fBEXTRACT\fR is evaluated for each new state to extract an output of \fBforeach\fR\. . .P This is mostly useful only for constructing \fBreduce\fR\- and \fBlimit\fR\-like functions\. But it is much more general, as it allows for partial reductions (see the example below)\. . .IP "" 4 . .nf jq \'[foreach \.[] as $item ([[],[]]; if $item == null then [[],\.[0]] else [(\.[0] + [$item]),[]] end; if $item == null then \.[1] else empty end)]\' [1,2,3,4,null,"a","b",null] => [[1,2,3,4],["a","b"]] . .fi . .IP "" 0 . .SS "Recursion" As described above, \fBrecurse\fR uses recursion, and any jq function can be recursive\. The \fBwhile\fR builtin is also implemented in terms of recursion\. . .P Tail calls are optimized whenever the expression to the left of the recursive call outputs its last value\. In practice this means that the expression to the left of the recursive call should not produce more than one output for each input\. . .P For example: . .IP "" 4 . .nf def recurse(f): def r: \., (f | select(\. != null) | r); r; def while(cond; update): def _while: if cond then \., (update | _while) else empty end; _while; def repeat(exp): def _repeat: exp, _repeat; _repeat; . .fi . .IP "" 0 . .SS "Generators and iterators" Some jq operators and functions are actually generators in that they can produce zero, one, or more values for each input, just as one might expect in other programming languages that have generators\. For example, \fB\.[]\fR generates all the values in its input (which must be an array or an object), \fBrange(0; 10)\fR generates the integers between 0 and 10, and so on\. . .P Even the comma operator is a generator, generating first the values generated by the expression to the left of the comma, then for each of those, the values generate by the expression on the right of the comma\. . .P The \fBempty\fR builtin is the generator that produces zero outputs\. The \fBempty\fR builtin backtracks to the preceding generator expression\. . .P All jq functions can be generators just by using builtin generators\. It is also possible to define new generators using only recursion and the comma operator\. If the recursive call(s) is(are) "in tail position" then the generator will be efficient\. In the example below the recursive call by \fB_range\fR to itself is in tail position\. The example shows off three advanced topics: tail recursion, generator construction, and sub\-functions\. . .IP "" 4 . .nf jq \'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)); range(0; 10; 3)\' null => 0, 3, 6, 9 jq \'def while(cond; update): def _while: if cond then \., (update | _while) else empty end; _while; [while(\.<100; \.*2)]\' 1 => [1,2,4,8,16,32,64] . .fi . .IP "" 0 . .SH "MATH" jq currently only has IEEE754 double\-precision (64\-bit) floating point number support\. . .P Besides simple arithmetic operators such as \fB+\fR, jq also has most standard math functions from the C math library\. C math functions that take a single input argument (e\.g\., \fBsin()\fR) are available as zero\-argument jq functions\. C math functions that take two input arguments (e\.g\., \fBpow()\fR) are available as two\-argument jq functions that ignore \fB\.\fR\. C math functions that take three input arguments are available as three\-argument jq functions that ignore \fB\.\fR\. . .P Availability of standard math functions depends on the availability of the corresponding math functions in your operating system and C math library\. Unavailable math functions will be defined but will raise an error\. . .P One\-input C math functions: \fBacos\fR \fBacosh\fR \fBasin\fR \fBasinh\fR \fBatan\fR \fBatanh\fR \fBcbrt\fR \fBceil\fR \fBcos\fR \fBcosh\fR \fBerf\fR \fBerfc\fR \fBexp\fR \fBexp10\fR \fBexp2\fR \fBexpm1\fR \fBfabs\fR \fBfloor\fR \fBgamma\fR \fBj0\fR \fBj1\fR \fBlgamma\fR \fBlog\fR \fBlog10\fR \fBlog1p\fR \fBlog2\fR \fBlogb\fR \fBnearbyint\fR \fBpow10\fR \fBrint\fR \fBround\fR \fBsignificand\fR \fBsin\fR \fBsinh\fR \fBsqrt\fR \fBtan\fR \fBtanh\fR \fBtgamma\fR \fBtrunc\fR \fBy0\fR \fBy1\fR\. . .P Two\-input C math functions: \fBatan2\fR \fBcopysign\fR \fBdrem\fR \fBfdim\fR \fBfmax\fR \fBfmin\fR \fBfmod\fR \fBfrexp\fR \fBhypot\fR \fBjn\fR \fBldexp\fR \fBmodf\fR \fBnextafter\fR \fBnexttoward\fR \fBpow\fR \fBremainder\fR \fBscalb\fR \fBscalbln\fR \fByn\fR\. . .P Three\-input C math functions: \fBfma\fR\. . .P See your system\'s manual for more information on each of these\. . .SH "I/O" At this time jq has minimal support for I/O, mostly in the form of control over when inputs are read\. Two builtins functions are provided for this, \fBinput\fR and \fBinputs\fR, that read from the same sources (e\.g\., \fBstdin\fR, files named on the command\-line) as jq itself\. These two builtins, and jq\'s own reading actions, can be interleaved with each other\. . .P Two builtins provide minimal output capabilities, \fBdebug\fR, and \fBstderr\fR\. (Recall that a jq program\'s output values are always output as JSON texts on \fBstdout\fR\.) The \fBdebug\fR builtin can have application\-specific behavior, such as for executables that use the libjq C API but aren\'t the jq executable itself\. The \fBstderr\fR builtin outputs its input in raw mode to stder with no additional decoration, not even a newline\. . .P Most jq builtins are referentially transparent, and yield constant and repeatable value streams when applied to constant inputs\. This is not true of I/O builtins\. . .SS "input" Outputs one new input\. . .SS "inputs" Outputs all remaining inputs, one by one\. . .P This is primarily useful for reductions over a program\'s inputs\. . .SS "debug" Causes a debug message based on the input value to be produced\. The jq executable wraps the input value with \fB["DEBUG:", ]\fR and prints that and a newline on stderr, compactly\. This may change in the future\. . .SS "stderr" Prints its input in raw and compact mode to stderr with no additional decoration, not even a newline\. . .SS "input_filename" Returns the name of the file whose input is currently being filtered\. Note that this will not work well unless jq is running in a UTF\-8 locale\. . .SS "input_line_number" Returns the line number of the input currently being filtered\. . .SH "STREAMING" With the \fB\-\-stream\fR option jq can parse input texts in a streaming fashion, allowing jq programs to start processing large JSON texts immediately rather than after the parse completes\. If you have a single JSON text that is 1GB in size, streaming it will allow you to process it much more quickly\. . .P However, streaming isn\'t easy to deal with as the jq program will have \fB[, ]\fR (and a few other forms) as inputs\. . .P Several builtins are provided to make handling streams easier\. . .P The examples below use the streamed form of \fB[0,[1]]\fR, which is \fB[[0],0],[[1,0],1],[[1,0]],[[1]]\fR\. . .P Streaming forms include \fB[, ]\fR (to indicate any scalar value, empty array, or empty object), and \fB[]\fR (to indicate the end of an array or object)\. Future versions of jq run with \fB\-\-stream\fR and \fB\-seq\fR may output additional forms such as \fB["error message"]\fR when an input text fails to parse\. . .SS "truncate_stream(stream_expression)" Consumes a number as input and truncates the corresponding number of path elements from the left of the outputs of the given streaming expression\. . .IP "" 4 . .nf jq \'[1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])]\' 1 => [[[0],2],[[0]]] . .fi . .IP "" 0 . .SS "fromstream(stream_expression)" Outputs values corresponding to the stream expression\'s outputs\. . .IP "" 4 . .nf jq \'fromstream(1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))\' null => [2] . .fi . .IP "" 0 . .SS "tostream" The \fBtostream\fR builtin outputs the streamed form of its input\. . .IP "" 4 . .nf jq \'\. as $dot|fromstream($dot|tostream)|\.==$dot\' [0,[1,{"a":1},{"b":2}]] => true . .fi . .IP "" 0 . .SH "ASSIGNMENT" Assignment works a little differently in jq than in most programming languages\. jq doesn\'t distinguish between references to and copies of something \- two objects or arrays are either equal or not equal, without any further notion of being "the same object" or "not the same object"\. . .P If an object has two fields which are arrays, \fB\.foo\fR and \fB\.bar\fR, and you append something to \fB\.foo\fR, then \fB\.bar\fR will not get bigger, even if you\'ve previously set \fB\.bar = \.foo\fR\. If you\'re used to programming in languages like Python, Java, Ruby, Javascript, etc\. then you can think of it as though jq does a full deep copy of every object before it does the assignment (for performance it doesn\'t actually do that, but that\'s the general idea)\. . .P This means that it\'s impossible to build circular values in jq (such as an array whose first element is itself)\. This is quite intentional, and ensures that anything a jq program can produce can be represented in JSON\. . .P All the assignment operators in jq have path expressions on the left\-hand side (LHS)\. The right\-hand side (RHS) procides values to set to the paths named by the LHS path expressions\. . .P Values in jq are always immutable\. Internally, assignment works by using a reduction to compute new, replacement values for \fB\.\fR that have had all the desired assignments applied to \fB\.\fR, then outputting the modified value\. This might be made clear by this example: \fB{a:{b:{c:1}}} | (\.a\.b|=3), \.\fR\. This will output \fB{"a":{"b":3}}\fR and \fB{"a":{"b":{"c":1}}}\fR because the last sub\-expression, \fB\.\fR, sees the original value, not the modified value\. . .P Most users will want to use modification assignment operators, such as \fB|=\fR or \fB+=\fR, rather than \fB=\fR\. . .P Note that the LHS of assignment operators refers to a value in \fB\.\fR\. Thus \fB$var\.foo = 1\fR won\'t work as expected (\fB$var\.foo\fR is not a valid or useful path expression in \fB\.\fR); use \fB$var | \.foo = 1\fR instead\. . .P Note too that \fB\.a,\.b=0\fR does not set \fB\.a\fR and \fB\.b\fR, but \fB(\.a,\.b)=0\fR sets both\. . .SS "Update\-assignment: |=" This is the "update" operator \'|=\'\. It takes a filter on the right\-hand side and works out the new value for the property of \fB\.\fR being assigned to by running the old value through this expression\. For instance, (\.foo, \.bar) |= \.+1 will build an object with the "foo" field set to the input\'s "foo" plus 1, and the "bar" field set to the input\'s "bar" plus 1\. . .P The left\-hand side can be any general path expression; see \fBpath()\fR\. . .P Note that the left\-hand side of \'|=\' refers to a value in \fB\.\fR\. Thus \fB$var\.foo |= \. + 1\fR won\'t work as expected (\fB$var\.foo\fR is not a valid or useful path expression in \fB\.\fR); use \fB$var | \.foo |= \. + 1\fR instead\. . .P If the right\-hand side outputs no values (i\.e\., \fBempty\fR), then the left\-hand side path will be deleted, as with \fBdel(path)\fR\. . .P If the right\-hand side outputs multiple values, only the first one will be used (COMPATIBILITY NOTE: in jq 1\.5 and earlier releases, it used to be that only the last one was used)\. . .IP "" 4 . .nf jq \'(\.\.|select(type=="boolean")) |= if \. then 1 else 0 end\' [true,false,[5,true,[true,[false]],false]] => [1,0,[5,1,[1,[0]],0]] . .fi . .IP "" 0 . .SS "Arithmetic update\-assignment: +=, \-=, *=, /=, %=, //=" jq has a few operators of the form \fBa op= b\fR, which are all equivalent to \fBa |= \. op b\fR\. So, \fB+= 1\fR can be used to increment values, being the same as \fB|= \. + 1\fR\. . .IP "" 4 . .nf jq \'\.foo += 1\' {"foo": 42} => {"foo": 43} . .fi . .IP "" 0 . .SS "Plain assignment: =" This is the plain assignment operator\. Unlike the others, the input to the right\-hand\-side (RHS) is the same as the input to the left\-hand\-side (LHS) rather than the value at the LHS path, and all values output by the RHS will be used (as shown below)\. . .P If the RHS of \'=\' produces multiple values, then for each such value jq will set the paths on the left\-hand side to the value and then it will output the modified \fB\.\fR\. For example, \fB(\.a,\.b)=range(2)\fR outputs \fB{"a":0,"b":0}\fR, then \fB{"a":1,"b":1}\fR\. The "update" assignment forms (see above) do not do this\. . .P This example should show the difference between \'=\' and \'|=\': . .P Provide input \'{"a": {"b": 10}, "b": 20}\' to the programs: . .P \&\.a = \.b . .P \&\.a |= \.b . .P The former will set the "a" field of the input to the "b" field of the input, and produce the output {"a": 20, "b": 20}\. The latter will set the "a" field of the input to the "a" field\'s "b" field, producing {"a": 10, "b": 20}\. . .P Another example of the difference between \'=\' and \'|=\': . .P null|(\.a,\.b)=range(3) . .P outputs \'{"a":0,"b":0}\', \'{"a":1,"b":1}\', and \'{"a":2,"b":2}\', while . .P null|(\.a,\.b)|=range(3) . .P outputs just \'{"a":0,"b":0}\'\. . .SS "Complex assignments" Lots more things are allowed on the left\-hand side of a jq assignment than in most languages\. We\'ve already seen simple field accesses on the left hand side, and it\'s no surprise that array accesses work just as well: . .IP "" 4 . .nf \&\.posts[0]\.title = "JQ Manual" . .fi . .IP "" 0 . .P What may come as a surprise is that the expression on the left may produce multiple results, referring to different points in the input document: . .IP "" 4 . .nf \&\.posts[]\.comments |= \. + ["this is great"] . .fi . .IP "" 0 . .P That example appends the string "this is great" to the "comments" array of each post in the input (where the input is an object with a field "posts" which is an array of posts)\. . .P When jq encounters an assignment like \'a = b\', it records the "path" taken to select a part of the input document while executing a\. This path is then used to find which part of the input to change while executing the assignment\. Any filter may be used on the left\-hand side of an equals \- whichever paths it selects from the input will be where the assignment is performed\. . .P This is a very powerful operation\. Suppose we wanted to add a comment to blog posts, using the same "blog" input above\. This time, we only want to comment on the posts written by "stedolan"\. We can find those posts using the "select" function described earlier: . .IP "" 4 . .nf \&\.posts[] | select(\.author == "stedolan") . .fi . .IP "" 0 . .P The paths provided by this operation point to each of the posts that "stedolan" wrote, and we can comment on each of them in the same way that we did before: . .IP "" 4 . .nf (\.posts[] | select(\.author == "stedolan") | \.comments) |= \. + ["terrible\."] . .fi . .IP "" 0 . .SH "MODULES" jq has a library/module system\. Modules are files whose names end in \fB\.jq\fR\. . .P Modules imported by a program are searched for in a default search path (see below)\. The \fBimport\fR and \fBinclude\fR directives allow the importer to alter this path\. . .P Paths in the a search path are subject to various substitutions\. . .P For paths starting with "~/", the user\'s home directory is substituted for "~"\. . .P For paths starting with "$ORIGIN/", the path of the jq executable is substituted for "$ORIGIN"\. . .P For paths starting with "\./" or paths that are "\.", the path of the including file is substituted for "\."\. For top\-level programs given on the command\-line, the current directory is used\. . .P Import directives can optionally specify a search path to which the default is appended\. . .P The default search path is the search path given to the \fB\-L\fR command\-line option, else \fB["~/\.jq", "$ORIGIN/\.\./lib/jq", "$ORIGIN/\.\./lib"]\fR\. . .P Null and empty string path elements terminate search path processing\. . .P A dependency with relative path "foo/bar" would be searched for in "foo/bar\.jq" and "foo/bar/bar\.jq" in the given search path\. This is intended to allow modules to be placed in a directory along with, for example, version control files, README files, and so on, but also to allow for single\-file modules\. . .P Consecutive components with the same name are not allowed to avoid ambiguities (e\.g\., "foo/foo")\. . .P For example, with \fB\-L$HOME/\.jq\fR a module \fBfoo\fR can be found in \fB$HOME/\.jq/foo\.jq\fR and \fB$HOME/\.jq/foo/foo\.jq\fR\. . .P If "$HOME/\.jq" is a file, it is sourced into the main program\. . .SS "import RelativePathString as NAME [];" Imports a module found at the given path relative to a directory in a search path\. A "\.jq" suffix will be added to the relative path string\. The module\'s symbols are prefixed with "NAME::"\. . .P The optional metadata must be a constant jq expression\. It should be an object with keys like "homepage" and so on\. At this time jq only uses the "search" key/value of the metadata\. The metadata is also made available to users via the \fBmodulemeta\fR builtin\. . .P The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top\-level search path\. . .SS "include RelativePathString [];" Imports a module found at the given path relative to a directory in a search path as if it were included in place\. A "\.jq" suffix will be added to the relative path string\. The module\'s symbols are imported into the caller\'s namespace as if the module\'s content had been included directly\. . .P The optional metadata must be a constant jq expression\. It should be an object with keys like "homepage" and so on\. At this time jq only uses the "search" key/value of the metadata\. The metadata is also made available to users via the \fBmodulemeta\fR builtin\. . .SS "import RelativePathString as $NAME [];" Imports a JSON file found at the given path relative to a directory in a search path\. A "\.json" suffix will be added to the relative path string\. The file\'s data will be available as \fB$NAME::NAME\fR\. . .P The optional metadata must be a constant jq expression\. It should be an object with keys like "homepage" and so on\. At this time jq only uses the "search" key/value of the metadata\. The metadata is also made available to users via the \fBmodulemeta\fR builtin\. . .P The "search" key in the metadata, if present, should have a string or array value (array of strings); this is the search path to be prefixed to the top\-level search path\. . .SS "module ;" This directive is entirely optional\. It\'s not required for proper operation\. It serves only the purpose of providing metadata that can be read with the \fBmodulemeta\fR builtin\. . .P The metadata must be a constant jq expression\. It should be an object with keys like "homepage"\. At this time jq doesn\'t use this metadata, but it is made available to users via the \fBmodulemeta\fR builtin\. . .SS "modulemeta" Takes a module name as input and outputs the module\'s metadata as an object, with the module\'s imports (including metadata) as an array value for the "deps" key\. . .P Programs can use this to query a module\'s metadata, which they could then use to, for example, search for, download, and install missing dependencies\. . .SH "COLORS" To configure alternative colors just set the \fBJQ_COLORS\fR environment variable to colon\-delimited list of partial terminal escape sequences like \fB"1;31"\fR, in this order: . .IP "\(bu" 4 color for \fBnull\fR . .IP "\(bu" 4 color for \fBfalse\fR . .IP "\(bu" 4 color for \fBtrue\fR . .IP "\(bu" 4 color for numbers . .IP "\(bu" 4 color for strings . .IP "\(bu" 4 color for arrays . .IP "\(bu" 4 color for objects . .IP "" 0 . .P The default color scheme is the same as setting \fB"JQ_COLORS=1;30:0;39:0;39:0;39:0;32:1;39:1;39"\fR\. . .P This is not a manual for VT100/ANSI escapes\. However, each of these color specifications should consist of two numbers separated by a semi\-colon, where the first number is one of these: . .IP "\(bu" 4 1 (bright) . .IP "\(bu" 4 2 (dim) . .IP "\(bu" 4 4 (underscore) . .IP "\(bu" 4 5 (blink) . .IP "\(bu" 4 7 (reverse) . .IP "\(bu" 4 8 (hidden) . .IP "" 0 . .P and the second is one of these: . .IP "\(bu" 4 30 (black) . .IP "\(bu" 4 31 (red) . .IP "\(bu" 4 32 (green) . .IP "\(bu" 4 33 (yellow) . .IP "\(bu" 4 34 (blue) . .IP "\(bu" 4 35 (magenta) . .IP "\(bu" 4 36 (cyan) . .IP "\(bu" 4 37 (white) . .IP "" 0 . .SH "BUGS" Presumably\. Report them or discuss them at: . .IP "" 4 . .nf https://github\.com/stedolan/jq/issues . .fi . .IP "" 0 . .SH "AUTHOR" Stephen Dolan \fB\fR jq-jq-1.6/compile-ios.sh0000700000175000017500000000524713366726451014502 0ustar czchenczchen#!/bin/bash # old values OLD_CFLAGS=$CFLAGS OLD_LDFLAGS=$LDFLAGS ORIG_PWD=`pwd` # For parallelism in make NJOBS="-j`sysctl -n hw.ncpu || echo 1`" # Get oniguruma rm -rf $PWD/build/ios oniguruma-5.9.6 echo "Downloading oniguruma 5.9.6" curl -L https://github.com/kkos/oniguruma/archive/v5.9.6.tar.gz | tar xz cd oniguruma-5.9.6 # So, we need to remake the configure scripts so that the arm64 architecture # exists in config.sub. In order to keep autoreconf from failing, create # NEWS and ChangeLog. touch NEWS ChangeLog autoreconf -fi >/dev/null 2>&1 CC=`xcrun -f clang` cd $ORIG_PWD autoreconf -fi for arch in i386 x86_64 armv7 armv7s arm64; do # Some of the architectures are a bit different... if [[ "$arch" = "i386" || "$arch" = "x86_64" ]] then SYSROOT=`xcrun -f --sdk iphonesimulator --show-sdk-path` else SYSROOT=`xcrun -f --sdk iphoneos --show-sdk-path` fi if [[ "$arch" = "arm64" ]] then HOST="aarch64-apple-darwin" else HOST="$arch-apple-darwin" fi CFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_CFLAGS" LDFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_LDFLAGS" # Build oniguruma for this architecture cd oniguruma-5.9.6 CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static --host=$HOST --prefix=$ORIG_PWD/build/ios/$arch STATUS=$? if [ $STATUS -ne 0 ] then echo "Failed to configure oniguruma for architecture $arch. Check `pwd`/config.log for details." cd $PWD exit $STATUS fi make clean make $NJBOS install STATUS=$? if [ $STATUS -ne 0 ] then echo "Failed to make oniguruma for architecture $arch." cd $PWD exit $STATUS fi # Build jq for this architecture cd $ORIG_PWD CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static -host=$HOST --prefix=$ORIG_PWD/build/ios/$arch --with-oniguruma=$ORIG_PWD/build/ios/$arch STATUS=$? if [ $STATUS -ne 0 ] then echo "Failed to configure jq for architecture $arch" exit $STATUS fi make clean make $NJOBS install STATUS=$? if [ $STATUS -ne 0 ] then echo "Failed to make jq for architecture $arch" exit $STATUS fi done # lipo together the different architectures into a universal 'fat' file lipo -create -output $ORIG_PWD/build/ios/libonig.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libonig.a lipo -create -output $ORIG_PWD/build/ios/libjq.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libjq.a # copy the products into the destination directory and clean up the single-architecture files. cp $ORIG_PWD/build/ios/i386/include/*.h $ORIG_PWD/build/ios/ rm -rf $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64} echo "Products are in $ORIG_PWD/build/ios"