Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e73951f3d1 | ||
|
|
fdf843c08b | ||
|
|
cf145ec65e | ||
|
|
4889844d0c | ||
|
|
dc7857c585 | ||
|
|
d65dbf89c9 | ||
|
|
ed2b3e26fb | ||
|
|
46436bd870 | ||
|
|
1dbe9317bc | ||
|
|
ef4f3a54fe | ||
|
|
9af03c2562 | ||
|
|
c10e5f4e73 | ||
|
|
01f676041d | ||
|
|
6856ab8a69 | ||
|
|
8212750fe9 | ||
|
|
35b6b949e3 | ||
|
|
d1ea3ab89d | ||
|
|
ae625d0de7 | ||
|
|
5fb82d1018 | ||
|
|
f9d1b94496 | ||
|
|
854ff5643a | ||
|
|
072ebb911b | ||
|
|
d46f5d29fa | ||
|
|
69aa0003af | ||
|
|
41a093c1f2 | ||
|
|
022a418894 | ||
|
|
d056668f4f | ||
|
|
917334d18a | ||
|
|
7a83f1c416 | ||
|
|
6f0a9521e1 | ||
|
|
aee5c3ecb0 | ||
|
|
da0d575649 | ||
|
|
74d2216da2 | ||
|
|
769314a8a2 | ||
|
|
831be04027 | ||
|
|
1a1adf73e8 | ||
|
|
68f41097b9 | ||
|
|
4402c6d806 | ||
|
|
2aa8a43c5b | ||
|
|
219bb9ecb7 | ||
|
|
88fd2ee4f2 | ||
|
|
6c3683dfe9 | ||
|
|
36e495da1e | ||
|
|
15faf9dc3b | ||
|
|
59302fd208 | ||
|
|
24202d1ecf | ||
|
|
336f4e27e6 | ||
|
|
fe29d3d3fa | ||
|
|
3d33412e9a | ||
|
|
fb9196021e | ||
|
|
b23fae9ee3 | ||
|
|
8b41415b07 | ||
|
|
1fa55a3fae | ||
|
|
a45c937f80 | ||
|
|
6ed3adb091 | ||
|
|
106fdf5114 | ||
|
|
99f170adc3 | ||
|
|
f60d83c15f | ||
|
|
36a47bae56 | ||
|
|
dc8528af0f | ||
|
|
e12d0ffd1e | ||
|
|
a71138a43a | ||
|
|
cb976b9a50 | ||
|
|
060eb39bf9 | ||
|
|
ab363e9e50 | ||
|
|
41a8ae7557 | ||
|
|
1956abbedf | ||
|
|
2003a0440f | ||
|
|
a0274cc46e | ||
|
|
3b384740b9 | ||
|
|
4d40a47eab | ||
|
|
18e828f17c | ||
|
|
0966230ba8 | ||
|
|
669f4ab4c3 | ||
|
|
3455714410 | ||
|
|
32ea50d336 | ||
|
|
5ac6a460fa | ||
|
|
f147b7fae5 | ||
|
|
474386a1d9 | ||
|
|
7fd2c2d08b | ||
|
|
5412f3f1c1 | ||
|
|
014b45b4a9 | ||
|
|
30ea390b77 | ||
|
|
00f244385b | ||
|
|
7b671b95d2 | ||
|
|
1535f23455 | ||
|
|
4ba0c8baef | ||
|
|
a7dd3ab793 | ||
|
|
f6f7d4d973 | ||
|
|
f4ae5c883b | ||
|
|
74a14f5de7 | ||
|
|
fee21c959a | ||
|
|
4b48054f41 | ||
|
|
17a319d120 | ||
|
|
3a1dab5396 | ||
|
|
dac081e183 | ||
|
|
489fcdc089 | ||
|
|
3ba7c97b2a | ||
|
|
54635000c5 | ||
|
|
426edff09c | ||
|
|
b37ce20c2a | ||
|
|
cac14a531d | ||
|
|
ae7a042876 | ||
|
|
eb165459aa | ||
|
|
04bc2ef7cf | ||
|
|
09104ce531 | ||
|
|
9fa2173825 | ||
|
|
346da34432 | ||
|
|
fa316ac430 | ||
|
|
77936a594d | ||
|
|
4e07eac356 | ||
|
|
d018e43ddc | ||
|
|
8cee66e609 | ||
|
|
3c4248192d | ||
|
|
255f8ef5be | ||
|
|
20b32db5af | ||
|
|
b634722c8a | ||
|
|
c9e8581f45 | ||
|
|
da129f6664 | ||
|
|
884e6c7d8b | ||
|
|
5aadaa79eb | ||
|
|
cf562961b7 | ||
|
|
63bed9bdf1 | ||
|
|
5989dbdfcf | ||
|
|
db19a11399 | ||
|
|
6c734cb609 | ||
|
|
ef41d1cb57 | ||
|
|
08b4a34a8e | ||
|
|
1fc5656762 | ||
|
|
91cfc34ae4 | ||
|
|
a0b942a7f5 | ||
|
|
6a401c8262 | ||
|
|
d235f32bc9 | ||
|
|
bdbfa7e32c | ||
|
|
78976b3504 | ||
|
|
8d4748c8df | ||
|
|
da723d3e11 | ||
|
|
0feeb7d0d4 | ||
|
|
9e53f36a74 | ||
|
|
7e7c08da47 | ||
|
|
2f6b89ef70 | ||
|
|
4e3024cb61 | ||
|
|
c427bf6a94 | ||
|
|
dae2422fd1 | ||
|
|
f98e3e6c26 | ||
|
|
fe99532f8b | ||
|
|
41a4968c90 | ||
|
|
1cdaabf2dd | ||
|
|
82d8253c19 | ||
|
|
c1717d6e12 | ||
|
|
f0d1c39b5b | ||
|
|
111aea3f0a | ||
|
|
d253414783 | ||
|
|
8b1b503609 | ||
|
|
ff48bd6ec5 | ||
|
|
54b9c9bdb2 | ||
|
|
3dcdc582ea | ||
|
|
7c4171d414 | ||
|
|
1d839ff9df | ||
|
|
3403d07912 |
No files matched your search
+10
@@ -39,3 +39,13 @@ Makefile.in
|
||||
version.h
|
||||
.remake-version-h
|
||||
config.cache
|
||||
*.rpm
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
|
||||
cscope.in.out
|
||||
cscope.out
|
||||
cscope.po.out
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
language: c
|
||||
|
||||
before_script:
|
||||
- sudo apt-get install -qq valgrind
|
||||
- bundle install --gemfile=docs/Gemfile
|
||||
- autoreconf -i
|
||||
|
||||
script:
|
||||
- ./setup.sh
|
||||
- ./configure && make
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -4,3 +4,4 @@ Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
Contributions by
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
Nicolas Williams <nico@cryptonector.com> - library-fication, autoconf stuff, misc
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
### C source files to be built and distributed.
|
||||
|
||||
LIBJQ_INCS = jq_parser.h builtin.h bytecode.h compile.h exec_stack.h \
|
||||
jv_alloc.h jv_dtoa.h jv_parse.h jv_unicode.h locfile.h \
|
||||
libm.h jv_alloc.h jv_dtoa.h jv_unicode.h locfile.h \
|
||||
opcode_list.h parser.y jv_utf8_tables.h lexer.l
|
||||
|
||||
LIBJQ_SRC = locfile.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
@@ -110,9 +110,9 @@ DOC_FILES = docs/content docs/public docs/templates docs/site.yml \
|
||||
# setup is only used by distribution developers, not package developers.
|
||||
# Still, as a matter of allowing patching, its not a bad idea to distribute
|
||||
# the developer setup script in the tarball.
|
||||
EXTRA_DIST = $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
|
||||
gen_utf8_tables.py jq.spec $(DOC_FILES) scripts/version \
|
||||
parser.h parser.c lexer.h lexer.c libjq.map
|
||||
EXTRA_DIST = $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) jq.spec \
|
||||
$(DOC_FILES) scripts/version parser.h parser.c lexer.h \
|
||||
lexer.c
|
||||
|
||||
# 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.
|
||||
@@ -120,11 +120,11 @@ docdir = ${datadir}/doc/${PACKAGE}
|
||||
dist_doc_DATA = README.md COPYING AUTHORS README
|
||||
|
||||
RELEASE ?= 1
|
||||
rpm: jq
|
||||
rpm: dist jq.spec
|
||||
@echo "Packaging jq as an RPM ..."
|
||||
mkdir -p rpm/SOURCES rpm/BUILD rpm/BUILDROOT rpm/RPMS rpm/SPECS
|
||||
cp jq-$$(cat VERSION).tar.gz rpm/SOURCES/
|
||||
rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $$(cat VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$$(cat VERSION).tar.gz
|
||||
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
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ jq is a command-line JSON processor.
|
||||
If you want to learn to use jq, read the documentation at
|
||||
[http://stedolan.github.io/jq](http://stedolan.github.io/jq). This
|
||||
documentation is generated from the docs/ folder of this repository.
|
||||
You can also try it online at [http://jqplay.herokuapp.com](http://jqplay.herokuapp.com).
|
||||
|
||||
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
|
||||
@@ -16,7 +17,8 @@ bison installed. To build, run:
|
||||
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make -j8
|
||||
make check
|
||||
|
||||
After make finishes, you'll be able to use `./jq`. You can also
|
||||
install it using:
|
||||
@@ -26,3 +28,8 @@ install it using:
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -49,6 +50,18 @@ static jv f_plus(jv input, jv a, jv b) {
|
||||
}
|
||||
}
|
||||
|
||||
#define LIBM_DD(name) \
|
||||
static jv f_ ## name(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; \
|
||||
}
|
||||
#include "libm.h"
|
||||
#undef LIBM_DD
|
||||
|
||||
static jv f_negate(jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "cannot be negated");
|
||||
@@ -58,6 +71,64 @@ static jv f_negate(jv input) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jv f_startswith(jv a, jv b) {
|
||||
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(jv a, jv b) {
|
||||
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(jv input, jv left) {
|
||||
if (jv_get_kind(f_startswith(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(jv input, jv right) {
|
||||
if (jv_get_kind(f_endswith(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(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
@@ -85,9 +156,34 @@ static jv f_minus(jv input, jv a, jv b) {
|
||||
}
|
||||
|
||||
static jv f_multiply(jv input, jv a, jv b) {
|
||||
jv_kind ak = jv_get_kind(a);
|
||||
jv_kind bk = jv_get_kind(b);
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
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");
|
||||
}
|
||||
@@ -97,6 +193,17 @@ static jv f_divide(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_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(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((intmax_t)jv_number_value(a) % (intmax_t)jv_number_value(b));
|
||||
} else {
|
||||
return type_error2(a, b, "cannot be divided");
|
||||
}
|
||||
@@ -152,6 +259,19 @@ static jv f_contains(jv a, jv b) {
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_dump(jv input) {
|
||||
return jv_dump_string(input, 0);
|
||||
}
|
||||
|
||||
static jv f_json_parse(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(jv input) {
|
||||
if (jv_get_kind(input) == JV_KIND_NUMBER) {
|
||||
return input;
|
||||
@@ -173,6 +293,8 @@ static jv f_length(jv input) {
|
||||
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);
|
||||
@@ -453,15 +575,29 @@ static jv f_error(jv input, jv msg) {
|
||||
return jv_invalid_with_msg(msg);
|
||||
}
|
||||
|
||||
#define LIBM_DD(name) \
|
||||
{(cfunction_ptr)f_ ## name, "_" #name, 1},
|
||||
|
||||
static const struct cfunction function_list[] = {
|
||||
#include "libm.h"
|
||||
{(cfunction_ptr)f_plus, "_plus", 3},
|
||||
{(cfunction_ptr)f_negate, "_negate", 1},
|
||||
{(cfunction_ptr)f_minus, "_minus", 3},
|
||||
{(cfunction_ptr)f_multiply, "_multiply", 3},
|
||||
{(cfunction_ptr)f_divide, "_divide", 3},
|
||||
{(cfunction_ptr)f_mod, "_mod", 3},
|
||||
{(cfunction_ptr)f_dump, "tojson", 1},
|
||||
{(cfunction_ptr)f_json_parse, "fromjson", 1},
|
||||
{(cfunction_ptr)f_tonumber, "tonumber", 1},
|
||||
{(cfunction_ptr)f_tostring, "tostring", 1},
|
||||
{(cfunction_ptr)f_keys, "keys", 1},
|
||||
{(cfunction_ptr)f_startswith, "startswith", 2},
|
||||
{(cfunction_ptr)f_endswith, "endswith", 2},
|
||||
{(cfunction_ptr)f_ltrimstr, "ltrimstr", 2},
|
||||
{(cfunction_ptr)f_rtrimstr, "rtrimstr", 2},
|
||||
{(cfunction_ptr)jv_string_split, "split", 2},
|
||||
{(cfunction_ptr)jv_string_explode, "explode", 1},
|
||||
{(cfunction_ptr)jv_string_implode, "implode", 1},
|
||||
{(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking
|
||||
{(cfunction_ptr)jv_getpath, "getpath", 2},
|
||||
{(cfunction_ptr)jv_delpaths, "delpaths", 2},
|
||||
@@ -485,6 +621,7 @@ static const struct cfunction function_list[] = {
|
||||
{(cfunction_ptr)f_error, "error", 2},
|
||||
{(cfunction_ptr)f_format, "format", 2},
|
||||
};
|
||||
#undef LIBM_DD
|
||||
|
||||
struct bytecoded_builtin { const char* name; block code; };
|
||||
static block bind_bytecoded_builtins(block b) {
|
||||
@@ -529,29 +666,52 @@ static block bind_bytecoded_builtins(block b) {
|
||||
return block_bind_referenced(builtins, b, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
|
||||
#define LIBM_DD(name) "def " #name ": _" #name ";",
|
||||
|
||||
static const char* const jq_builtins[] = {
|
||||
"def map(f): [.[] | f];",
|
||||
"def select(f): if f then . else empty end;",
|
||||
"def sort_by(f): _sort_by_impl(map([f]));",
|
||||
"def group_by(f): _group_by_impl(map([f]));",
|
||||
"def unique: group_by(.) | map(.[0]);",
|
||||
"def unique_by(f): group_by(f) | map(.[0]);",
|
||||
"def max_by(f): _max_by_impl(map([f]));",
|
||||
"def min_by(f): _min_by_impl(map([f]));",
|
||||
#include "libm.h"
|
||||
"def add: reduce .[] as $x (null; . + $x);",
|
||||
"def del(f): delpaths([path(f)]);",
|
||||
"def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",
|
||||
"def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",
|
||||
"def recurse(f): ., (f | select(. != null) | recurse(f));",
|
||||
"def recurse_down: recurse(.[]?);",
|
||||
"def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];",
|
||||
"def from_entries: map({(.key): .value}) | 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]] else .[i] end;",
|
||||
"def index(i): if type == \"array\" and (i|type) == \"array\" then .[i] elif type == \"array\" then .[[i]] else .[i] end | .[0];",
|
||||
"def rindex(i): if type == \"array\" and (i|type) == \"array\" then .[i] elif type == \"array\" then .[[i]] else .[i] end | .[-1:][0];",
|
||||
"def paths: path(recurse(if (type|. == \"array\" or . == \"object\") then .[] else empty end))|select(length > 0);",
|
||||
"def leaf_paths: . as $dot|paths|select(. as $p|$dot|getpath($p)|type|. != \"array\" and . != \"object\");",
|
||||
"def any: reduce .[] as $i (false; . or $i);",
|
||||
"def all: reduce .[] as $i (true; . and $i);",
|
||||
"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 strings: select(type == \"string\");",
|
||||
"def nulls: select(type == \"null\");",
|
||||
"def values: arrays, objects, booleans, numbers, strings;",
|
||||
"def scalars: select(. == null or . == true or . == false or type == \"number\" or type == \"string\");",
|
||||
"def join(x): reduce .[] as $i (\"\"; . + (if . == \"\" then $i else x + $i end));",
|
||||
};
|
||||
#undef LIBM_DD
|
||||
|
||||
|
||||
int builtins_bind_one(block* bb, const char* code) {
|
||||
static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {
|
||||
struct locfile src;
|
||||
locfile_init(&src, code, strlen(code));
|
||||
locfile_init(&src, jq, code, strlen(code));
|
||||
block funcs;
|
||||
int nerrors = jq_parse_library(&src, &funcs);
|
||||
if (nerrors == 0) {
|
||||
@@ -561,14 +721,14 @@ int builtins_bind_one(block* bb, const char* code) {
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
int slurp_lib(block* bb) {
|
||||
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(bb, jv_string_value(data) );
|
||||
nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );
|
||||
}
|
||||
jv_free(filename);
|
||||
jv_free(data);
|
||||
@@ -576,14 +736,14 @@ int slurp_lib(block* bb) {
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
int builtins_bind(block* bb) {
|
||||
int nerrors = slurp_lib(bb);
|
||||
int builtins_bind(jq_state *jq, block* bb) {
|
||||
int nerrors = slurp_lib(jq, bb);
|
||||
if (nerrors) {
|
||||
block_free(*bb);
|
||||
return nerrors;
|
||||
}
|
||||
for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {
|
||||
nerrors = builtins_bind_one(bb, jq_builtins[i]);
|
||||
nerrors = builtins_bind_one(jq, bb, jq_builtins[i]);
|
||||
assert(!nerrors);
|
||||
}
|
||||
*bb = bind_bytecoded_builtins(*bb);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "jq.h"
|
||||
#include "bytecode.h"
|
||||
#include "compile.h"
|
||||
|
||||
int builtins_bind(block*);
|
||||
int builtins_bind(jq_state *, block*);
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,6 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE // for strdup
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -6,3 +6,5 @@ install-sh
|
||||
ltmain.sh
|
||||
missing
|
||||
ylwrap
|
||||
test-driver
|
||||
ar-lib
|
||||
@@ -0,0 +1,8 @@
|
||||
dnl 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])])
|
||||
@@ -0,0 +1,62 @@
|
||||
dnl 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)
|
||||
])
|
||||
@@ -0,0 +1,9 @@
|
||||
dnl 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
|
||||
])
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
AC_DEFUN([upcase],[`echo $1 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`])dnl
|
||||
+42
-3
@@ -1,11 +1,14 @@
|
||||
AC_INIT([jq], [1.3], [mu@netsoc.tcd.ie],
|
||||
m4_define([jq_version], m4_esyscmd_s([git describe --tags --dirty --match 'jq-*'|sed 's/^jq-//']))
|
||||
|
||||
AC_INIT([jq], [jq_version], [mu@netsoc.tcd.ie],
|
||||
[jq], [http://stedolan.github.com/jq/])
|
||||
|
||||
dnl Created autoconf implementation thompson@dtosolutions, 26NOV12
|
||||
AC_PREREQ([2.61])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
AM_INIT_AUTOMAKE([parallel-tests foreign])
|
||||
AM_INIT_AUTOMAKE([parallel-tests foreign -Wall])
|
||||
AM_SILENT_RULES([yes])
|
||||
AM_PROG_AR
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CPP_WERROR
|
||||
@@ -18,6 +21,17 @@ 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
|
||||
AC_MSG_CHECKING([bison version])
|
||||
if test "$YACC" != "bison -y"; then
|
||||
AC_MSG_RESULT([not bison])
|
||||
elif $YACC --help | grep -- --warnings >/dev/null; then
|
||||
AC_MSG_RESULT([ok])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([You need bison version 2.5 or more recent.])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl these program checks should probably be deleted
|
||||
dnl
|
||||
@@ -64,10 +78,35 @@ EOF
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
||||
|
||||
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 <pthread.h>], [NULL, NULL])
|
||||
AC_FIND_FUNC([pthread_once], [pthread], [#include <pthread.h>], [NULL, NULL])
|
||||
AC_FIND_FUNC([atexit], [pthread], [#include <stdlib.h>], [NULL])
|
||||
fi
|
||||
|
||||
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_SUBST([BUNDLER], ["$bundle_cmd"])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([config/m4])
|
||||
AM_INIT_AUTOMAKE([-Wall])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ $BINARIES = {
|
||||
|
||||
$BINARIES.each do |name, args|
|
||||
file "../build/#{name}" do |t|
|
||||
sh "MAKEFLAGS=-j4 ../scripts/crosscompile #{name} #{args}"
|
||||
sh "../scripts/crosscompile -j4 #{name} #{args}"
|
||||
end
|
||||
task :binaries => ["../build/#{name}"]
|
||||
end
|
||||
|
||||
+241
-160
@@ -2,123 +2,205 @@ headline: Tutorial
|
||||
body:
|
||||
- text: |
|
||||
|
||||
Twitter has a JSON API, so let's play with that. This URL gets
|
||||
us the last 5 tweets about JSON:
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true'"
|
||||
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: |
|
||||
{"completed_in":0.108,"max_id":247677287108067328,"max_id_str":"247677287108067328","next_page":"?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1","page":1,"query":"json","refresh_url":"?since_id=247677287108067328&q=json&include_entities=1","results":[{"created_at":"Mon, 17 Sep 2012 12:44:01 +0000","entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/XRvh1ZVw","expanded_url":"http:\/\/jase.im\/Ri7I0M","display_url":"jase.im\/Ri7I0M","indices":[112,132]}],"user_mentions":[{"screen_name":"imagemechanics","name":"Jason Cotterell","id":57271393,"id_str":"57271393","indices":[3,18]}]},"from_user":"_AaronNg","from_user_id":79771704,"from_user_id_str":"79771704","from_user_name":"NgChenChong","geo":null,"id":247677287108067328,"id_str":"247677287108067328","iso_language_code":"en","metadata":{"result_type":"recent"},"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","source":"<a href="http:\/\/twitter.com\/">web<\/a>","text":"RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp:\/\/t.co\/XRvh1ZVw","to_user":null,"to_user_id":0,"to_user_id_str":"0","to_user_name":null}, ...
|
||||
[
|
||||
{
|
||||
"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 a ton of info here, and it's relatively hard to
|
||||
read as it is. To syntax highlight it and order the attributes we pipe
|
||||
it through jq, telling jq to just spit the input back at us using
|
||||
the expression `.`:
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"parents": [
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
],
|
||||
"committer": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
...
|
||||
|
||||
- text: |
|
||||
|
||||
There's lots of info and no whitespace, so to make it a bit more
|
||||
legible we pipe it through jq, telling jq to just spit the input
|
||||
back at us using the expression `.`:
|
||||
There's still far too much info for our purposes, so we can try
|
||||
just grab the first commit:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.'"
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]'"
|
||||
result: |
|
||||
|
||||
{
|
||||
"since_id_str": "0",
|
||||
"since_id": 0,
|
||||
"results_per_page": 5,
|
||||
"completed_in": 0.108,
|
||||
"max_id": 247677287108067330,
|
||||
"max_id_str": "247677287108067328",
|
||||
"next_page": "?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1",
|
||||
"page": 1,
|
||||
"query": "json",
|
||||
"refresh_url": "?since_id=247677287108067328&q=json&include_entities=1",
|
||||
"results": [
|
||||
"parents": [
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
/* lots more results... */
|
||||
]
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
],
|
||||
"committer": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
"subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
|
||||
"starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
|
||||
"gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}",
|
||||
"login": "stedolan",
|
||||
"id": 79765,
|
||||
"avatar_url": "https://1.gravatar.com/avatar/31de909d8e55dd07ed782d92ece59842?d=https%3A%2F%2Fidenticons.github.com%2Ffc5b6765b1c9cfaecea48ae71df4d279.png",
|
||||
"gravatar_id": "31de909d8e55dd07ed782d92ece59842",
|
||||
"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}"
|
||||
},
|
||||
"author": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
"subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
|
||||
"starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
|
||||
"gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}",
|
||||
"login": "stedolan",
|
||||
"id": 79765,
|
||||
"avatar_url": "https://1.gravatar.com/avatar/31de909d8e55dd07ed782d92ece59842?d=https%3A%2F%2Fidenticons.github.com%2Ffc5b6765b1c9cfaecea48ae71df4d279.png",
|
||||
"gravatar_id": "31de909d8e55dd07ed782d92ece59842",
|
||||
"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}"
|
||||
},
|
||||
"comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
|
||||
"html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"commit": {
|
||||
"comment_count": 0,
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"tree": {
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40",
|
||||
"sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40"
|
||||
},
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
||||
"committer": {
|
||||
"date": "2013-06-22T16:30:59Z",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"name": "Stephen Dolan"
|
||||
},
|
||||
"author": {
|
||||
"date": "2013-06-22T16:30:59Z",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"name": "Stephen Dolan"
|
||||
}
|
||||
},
|
||||
"sha": "d25341478381063d1c76e81b3a52e0592a7c997f"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
Let's pull out the first tweet:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.results[0]'"
|
||||
result: |
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
},
|
||||
|
||||
- text: |
|
||||
|
||||
For the rest of the examples, I'll leave out the `curl` command - it's not going to change.
|
||||
|
||||
|
||||
There's still a lot of info we don't care about there, so we'll
|
||||
restrict it down to the most interesting fields.
|
||||
|
||||
|
||||
- command: "jq '.results[0] | {from_user, text}'"
|
||||
|
||||
- command: "jq '.[0] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
|
||||
The `|` operator in jq feeds the output of one filter
|
||||
(`.results[0]` which gets the first element of the results
|
||||
array) into the input of another (`{from_user, text}` which
|
||||
builds an object of those two fields).
|
||||
|
||||
Now let's get the rest of the tweets:
|
||||
|
||||
|
||||
(`.[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`:
|
||||
|
||||
- command: "jq '.results[] | {from_user, text}'"
|
||||
Now let's get the rest of the commits:
|
||||
|
||||
|
||||
- command: "jq '.[] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
}
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
}
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
"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."
|
||||
}
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
}
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
`.results[]` returns each element of the results array, one at a
|
||||
time, which are all fed into `{from_user, 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
|
||||
@@ -128,109 +210,108 @@ body:
|
||||
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 '[.results[] | {from_user, text}]'"
|
||||
- command: "jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
},
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
},
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
"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."
|
||||
},
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
},
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
- - -
|
||||
|
||||
Next, let's try getting the URLs out of those API results as
|
||||
well. In each tweet, the Twitter API includes a field called
|
||||
"entities" which looks like this:
|
||||
|
||||
{
|
||||
"user_mentions": [],
|
||||
"urls": [{
|
||||
"indices": [83, 103],
|
||||
"display_url": "bit.ly/StniqT",
|
||||
"expanded_url": "http://bit.ly/StniqT",
|
||||
"url": "http://t.co/28Jesbio"
|
||||
}],
|
||||
"hashtags": [
|
||||
{"indices": [108, 114], "text": "Apple" },
|
||||
{"indices": [121, 129], "text": "Awesome"}
|
||||
]
|
||||
}
|
||||
Next, let's try getting the URLs of the parrent 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.
|
||||
|
||||
We want to pull out all of the "url" fields inside that array of url
|
||||
objects, and make a simple list of strings to go along with the
|
||||
"from_user" and "text" fields we already have.
|
||||
|
||||
|
||||
"parents": [
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
]
|
||||
|
||||
- command: "jq '.results[] | {from_user, text, urls: [.entities.urls[].url]}'"
|
||||
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: |
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/XRvh1ZVw"
|
||||
],
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/Lhp92IqD"
|
||||
],
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/7KALdWaX"
|
||||
],
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/28Jesbio"
|
||||
],
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/M70snaIf"
|
||||
],
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
|
||||
|
||||
[
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/ff48bd6ec538b01d1057be8e93b94eef6914e9ef"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
],
|
||||
"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."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/3dcdc582ea993afea3f5503a78a77675967ecdfa"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/7c4171d414f647ab08bcd20c76a4d8ed68d9c602"
|
||||
],
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
]
|
||||
|
||||
- text: |
|
||||
|
||||
Here we're making an object as before, but this time the urls
|
||||
field is being set to `[.entities.urls[].url]`, which collects
|
||||
all of the URLs defined in the entities object.
|
||||
|
||||
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: |
|
||||
|
||||
@@ -238,4 +319,4 @@ body:
|
||||
|
||||
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](../download/) if you haven't already.
|
||||
@@ -51,7 +51,7 @@ body:
|
||||
|
||||
git clone https://github.com/stedolan/jq.git
|
||||
cd jq
|
||||
autoreconf
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
@@ -72,14 +72,35 @@ sections:
|
||||
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`:
|
||||
|
||||
@@ -114,6 +135,12 @@ sections:
|
||||
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.
|
||||
@@ -125,6 +152,20 @@ sections:
|
||||
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
|
||||
@@ -157,7 +198,7 @@ sections:
|
||||
input: '"Hello, world!"'
|
||||
output: ['"Hello, world!"']
|
||||
|
||||
- title: "`.foo`"
|
||||
- title: "`.foo`, `.foo.bar`"
|
||||
body: |
|
||||
|
||||
The simplest *useful* filter is `.foo`. When given a
|
||||
@@ -166,6 +207,8 @@ sections:
|
||||
|
||||
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'
|
||||
@@ -174,11 +217,31 @@ sections:
|
||||
- program: '.foo'
|
||||
input: '{"notfoo": true, "alsonotfoo": false}'
|
||||
output: ['null']
|
||||
- program: '."foo"'
|
||||
- program: '.["foo"]'
|
||||
input: '{"foo": 42}'
|
||||
output: [42]
|
||||
|
||||
- title: "`.[foo]`, `.[2]`, `.[10:15]`"
|
||||
- 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: "`.[<string>]`, `.[2]`, `.[10:15]`"
|
||||
body: |
|
||||
|
||||
You can also look up fields of an object using syntax like
|
||||
@@ -188,11 +251,16 @@ sections:
|
||||
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).
|
||||
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]'
|
||||
@@ -206,6 +274,10 @@ sections:
|
||||
- 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"]'
|
||||
@@ -218,7 +290,7 @@ sections:
|
||||
- title: "`.[]`"
|
||||
body: |
|
||||
|
||||
If you use the `.[foo]` syntax, but omit the index
|
||||
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
|
||||
@@ -242,6 +314,12 @@ sections:
|
||||
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: |
|
||||
|
||||
@@ -405,7 +483,8 @@ sections:
|
||||
- **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.
|
||||
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.
|
||||
@@ -442,17 +521,34 @@ sections:
|
||||
input: '["xml", "yaml", "json"]'
|
||||
output: ['["json"]']
|
||||
|
||||
- title: Multiplication, division - `*` and `/`
|
||||
- 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`
|
||||
- title: "`length`"
|
||||
body: |
|
||||
|
||||
The builtin function `length` gets the length of various
|
||||
@@ -473,7 +569,7 @@ sections:
|
||||
input: '[[1,2], "string", {"a":2}, null]'
|
||||
output: [2, 6, 1, 0]
|
||||
|
||||
- title: `keys`
|
||||
- title: "`keys`"
|
||||
body: |
|
||||
|
||||
The builtin function `keys`, when given an object, returns
|
||||
@@ -496,7 +592,7 @@ sections:
|
||||
input: '[42,3,35]'
|
||||
output: ['[0,1,2]']
|
||||
|
||||
- title: `has`
|
||||
- title: "`has`"
|
||||
body: |
|
||||
|
||||
The builtin function `has` returns whether the input object
|
||||
@@ -515,7 +611,21 @@ sections:
|
||||
input: '[[0,1], ["a","b","c"]]'
|
||||
output: ['[false, true]']
|
||||
|
||||
- title: `to_entries`, `from_entries`, `with_entries`
|
||||
- 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
|
||||
@@ -540,7 +650,7 @@ sections:
|
||||
output: ['{"KEY_a": 1, "KEY_b": 2}']
|
||||
|
||||
|
||||
- title: `select`
|
||||
- title: "`select`"
|
||||
body: |
|
||||
|
||||
The function `select(foo)` produces its input unchanged if
|
||||
@@ -548,14 +658,27 @@ sections:
|
||||
otherwise.
|
||||
|
||||
It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))`
|
||||
will give you `[3]`.
|
||||
will give you `[2,3]`.
|
||||
|
||||
examples:
|
||||
- program: 'map(select(. >= 2))'
|
||||
input: '[1,5,3,0,7]'
|
||||
output: ['[5,3,7]']
|
||||
|
||||
- title: `empty`
|
||||
|
||||
- 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`.
|
||||
@@ -570,7 +693,7 @@ sections:
|
||||
input: 'null'
|
||||
output: ['[1,2,3]']
|
||||
|
||||
- title: `map(x)`
|
||||
- title: "`map(x)`"
|
||||
body: |
|
||||
|
||||
For any filter `x`, `map(x)` will run that filter for each
|
||||
@@ -585,7 +708,33 @@ sections:
|
||||
input: '[1,2,3]'
|
||||
output: ['[2,3,4]']
|
||||
|
||||
- title: `add`
|
||||
- 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
|
||||
@@ -607,7 +756,47 @@ sections:
|
||||
input: '[]'
|
||||
output: ["null"]
|
||||
|
||||
- title: `range`
|
||||
- title: "`any`"
|
||||
body: |
|
||||
|
||||
The filter `any` takes as input an array of boolean values,
|
||||
and produces `true` as output if any of the the elements of
|
||||
the array is `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 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)`
|
||||
@@ -623,7 +812,27 @@ sections:
|
||||
input: 'null'
|
||||
output: ['[2,3]']
|
||||
|
||||
- title: `tonumber`
|
||||
- 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
|
||||
@@ -635,7 +844,7 @@ sections:
|
||||
input: '[1, "1"]'
|
||||
output: [1, 1]
|
||||
|
||||
- title: `tostring`
|
||||
- title: "`tostring`"
|
||||
body: |
|
||||
|
||||
The `tostring` function prints its input as a
|
||||
@@ -647,7 +856,7 @@ sections:
|
||||
input: '[1, "1", [1]]'
|
||||
output: ['"1"', '"1"', '"[1]"']
|
||||
|
||||
- title: `type`
|
||||
- title: "`type`"
|
||||
body: |
|
||||
|
||||
The `type` function returns the type of its argument as a
|
||||
@@ -659,7 +868,7 @@ sections:
|
||||
input: '[0, false, [], {}, null, "hello"]'
|
||||
output: ['["number", "boolean", "array", "object", "null", "string"]']
|
||||
|
||||
- title: `sort, sort_by`
|
||||
- title: "`sort, sort_by`"
|
||||
body: |
|
||||
|
||||
The `sort` functions sorts its input, which must be an
|
||||
@@ -691,7 +900,7 @@ sections:
|
||||
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`
|
||||
- title: "`group_by`"
|
||||
body: |
|
||||
|
||||
`group_by(.foo)` takes as input an array, groups the
|
||||
@@ -708,7 +917,7 @@ sections:
|
||||
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`
|
||||
- title: "`min`, `max`, `min_by`, `max_by`"
|
||||
body: |
|
||||
|
||||
Find the minimum or maximum element of the input array. The
|
||||
@@ -724,7 +933,7 @@ sections:
|
||||
input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]'
|
||||
output: ['{"foo":2, "bar":3}']
|
||||
|
||||
- title: `unique`
|
||||
- title: "`unique`"
|
||||
body: |
|
||||
|
||||
The `unique` function takes as input an array and produces
|
||||
@@ -736,7 +945,25 @@ sections:
|
||||
input: '[1,2,5,3,5,3,1,3]'
|
||||
output: ['[1,2,3,5]']
|
||||
|
||||
- title: `reverse`
|
||||
- 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: ['["chunky", "bacon", "asparagus"]']
|
||||
|
||||
|
||||
- title: "`reverse`"
|
||||
body: |
|
||||
|
||||
This function reverses an array.
|
||||
@@ -746,7 +973,7 @@ sections:
|
||||
input: '[1,2,3,4]'
|
||||
output: ['[4,3,2,1]']
|
||||
|
||||
- title: `contains`
|
||||
- title: "`contains`"
|
||||
body: |
|
||||
|
||||
The filter `contains(b)` will produce true if b is
|
||||
@@ -775,7 +1002,127 @@ sections:
|
||||
input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}'
|
||||
output: ['false']
|
||||
|
||||
- title: `recurse`
|
||||
- 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, true, false, false]']
|
||||
|
||||
- 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
|
||||
@@ -806,8 +1153,26 @@ sections:
|
||||
- '{"foo":[{"foo":[]}]}'
|
||||
- '{"foo":[]}'
|
||||
|
||||
- title: "`recurse_down`"
|
||||
body: |
|
||||
|
||||
A quieter version of `recurse(.[])`, equivalent to:
|
||||
|
||||
- title: "String interpolation - `\(foo)`"
|
||||
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
|
||||
@@ -819,6 +1184,25 @@ sections:
|
||||
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: |
|
||||
|
||||
@@ -871,9 +1255,9 @@ sections:
|
||||
@uri "http://www.google.com/search?q=\(.search)"
|
||||
|
||||
will produce the following output for the input
|
||||
`{"search":"jq!"}`:
|
||||
`{"search":"what is jq?"}`:
|
||||
|
||||
http://www.google.com/search?q=jq%21
|
||||
"http://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.
|
||||
@@ -893,7 +1277,7 @@ sections:
|
||||
|
||||
- title: Conditionals and Comparisons
|
||||
entries:
|
||||
- title: `==`, `!=`
|
||||
- title: "`==`, `!=`"
|
||||
body: |
|
||||
|
||||
The expression 'a == b' will produce 'true' if the result of a and b
|
||||
@@ -921,7 +1305,7 @@ sections:
|
||||
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
|
||||
need something more like `if (.name | length) > 0 then A else
|
||||
B end` instead.
|
||||
|
||||
If the condition A produces multiple results, it is
|
||||
@@ -942,7 +1326,7 @@ sections:
|
||||
input: 2
|
||||
output: ['"many"']
|
||||
|
||||
- title: `>, >=, <=, <`
|
||||
- title: "`>, >=, <=, <`"
|
||||
body: |
|
||||
|
||||
The comparison operators `>`, `>=`, `<=`, `<` return whether
|
||||
@@ -1056,7 +1440,7 @@ sections:
|
||||
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
|
||||
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
|
||||
@@ -1229,7 +1613,7 @@ sections:
|
||||
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: "`+=`, `-=`, `*=`, `/=`, `//=`"
|
||||
- title: "`+=`, `-=`, `*=`, `/=`, `%=`, `//=`"
|
||||
body: |
|
||||
|
||||
jq has a few operators of the form `a op= b`, which are all
|
||||
|
||||
@@ -27,8 +27,13 @@ tail: |
|
||||
for *way* more.
|
||||
|
||||
news:
|
||||
- date: 2014-06-08
|
||||
body: |
|
||||
|
||||
jq 1.4 (finally) released! Get it on the [download](download/) page.
|
||||
|
||||
- date: 2013-05-19
|
||||
body: |
|
||||
|
||||
jq 1.3 (finally) released! Get it on the [download](download/) page.
|
||||
jq 1.3 released.
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
# 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.3"
|
||||
jq_version: "1.4"
|
||||
|
||||
root: '/jq'
|
||||
footer: |
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
@@ -19,6 +21,9 @@ struct jq_state {
|
||||
void *nomem_handler_data;
|
||||
struct bytecode* bc;
|
||||
|
||||
jq_err_cb err_cb;
|
||||
void *err_cb_data;
|
||||
|
||||
struct stack stk;
|
||||
stack_ptr curr_frame;
|
||||
stack_ptr stk_top;
|
||||
@@ -238,16 +243,23 @@ static void jq_reset(jq_state *jq) {
|
||||
jq->subexp_nest = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void print_error(jv value) {
|
||||
static void print_error(jq_state *jq, jv value) {
|
||||
assert(!jv_is_valid(value));
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
|
||||
}
|
||||
jv msg2;
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING)
|
||||
msg2 = jv_string_fmt("jq: error: %s", jv_string_value(msg));
|
||||
else
|
||||
msg2 = jv_string_fmt("jq: error: <unknown>");
|
||||
jv_free(msg);
|
||||
|
||||
if (jq->err_cb)
|
||||
jq->err_cb(jq->err_cb_data, msg2);
|
||||
else if (jv_get_kind(msg2) == JV_KIND_STRING)
|
||||
fprintf(stderr, "%s\n", jv_string_value(msg2));
|
||||
else
|
||||
fprintf(stderr, "jq: error: %s\n", strerror(ENOMEM));
|
||||
jv_free(msg2);
|
||||
}
|
||||
#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
|
||||
|
||||
@@ -368,8 +380,8 @@ jv jq_next(jq_state *jq) {
|
||||
stack_push(jq, jv_object_set(objv, k, v));
|
||||
stack_push(jq, stktop);
|
||||
} else {
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
||||
jv_kind_name(jv_get_kind(k)))));
|
||||
print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
||||
jv_kind_name(jv_get_kind(k)))));
|
||||
jv_free(stktop);
|
||||
jv_free(v);
|
||||
jv_free(k);
|
||||
@@ -387,7 +399,7 @@ jv jq_next(jq_state *jq) {
|
||||
jv max = stack_pop(jq);
|
||||
if (jv_get_kind(*var) != JV_KIND_NUMBER ||
|
||||
jv_get_kind(max) != JV_KIND_NUMBER) {
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric")));
|
||||
print_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))) {
|
||||
@@ -491,7 +503,8 @@ jv jq_next(jq_state *jq) {
|
||||
goto do_backtrack;
|
||||
}
|
||||
|
||||
case INDEX: {
|
||||
case INDEX:
|
||||
case INDEX_OPT: {
|
||||
jv t = stack_pop(jq);
|
||||
jv k = stack_pop(jq);
|
||||
path_append(jq, jv_copy(k));
|
||||
@@ -499,7 +512,10 @@ jv jq_next(jq_state *jq) {
|
||||
if (jv_is_valid(v)) {
|
||||
stack_push(jq, v);
|
||||
} else {
|
||||
print_error(v);
|
||||
if (opcode == INDEX)
|
||||
print_error(jq, v);
|
||||
else
|
||||
jv_free(v);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
@@ -524,16 +540,18 @@ jv jq_next(jq_state *jq) {
|
||||
}
|
||||
|
||||
case EACH:
|
||||
case EACH_OPT:
|
||||
stack_push(jq, jv_number(-1));
|
||||
// fallthrough
|
||||
case ON_BACKTRACK(EACH): {
|
||||
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) idx = 0;
|
||||
if (opcode == EACH || opcode == EACH_OPT) idx = 0;
|
||||
else idx = idx + 1;
|
||||
int len = jv_array_length(jv_copy(container));
|
||||
keep_going = idx < len;
|
||||
@@ -543,7 +561,7 @@ jv jq_next(jq_state *jq) {
|
||||
value = jv_array_get(jv_copy(container), idx);
|
||||
}
|
||||
} else if (jv_get_kind(container) == JV_KIND_OBJECT) {
|
||||
if (opcode == EACH) idx = jv_object_iter(container);
|
||||
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) {
|
||||
@@ -551,9 +569,12 @@ jv jq_next(jq_state *jq) {
|
||||
value = jv_object_iter_value(container, idx);
|
||||
}
|
||||
} else {
|
||||
assert(opcode == EACH);
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
||||
jv_kind_name(jv_get_kind(container)))));
|
||||
assert(opcode == EACH || opcode == EACH_OPT);
|
||||
if (opcode == EACH) {
|
||||
print_error(jq,
|
||||
jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
||||
jv_kind_name(jv_get_kind(container)))));
|
||||
}
|
||||
keep_going = 0;
|
||||
}
|
||||
|
||||
@@ -624,7 +645,7 @@ jv jq_next(jq_state *jq) {
|
||||
if (jv_is_valid(top)) {
|
||||
stack_push(jq, top);
|
||||
} else {
|
||||
print_error(top);
|
||||
print_error(jq, top);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
@@ -682,10 +703,23 @@ jq_state *jq_init(void) {
|
||||
jq->fork_top = 0;
|
||||
jq->curr_frame = 0;
|
||||
|
||||
jq->err_cb = NULL;
|
||||
jq->err_cb_data = NULL;
|
||||
|
||||
jq->path = jv_null();
|
||||
return jq;
|
||||
}
|
||||
|
||||
void jq_set_error_cb(jq_state *jq, jq_err_cb cb, void *data) {
|
||||
jq->err_cb = cb;
|
||||
jq->err_cb_data = data;
|
||||
}
|
||||
|
||||
void jq_get_error_cb(jq_state *jq, jq_err_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;
|
||||
@@ -729,7 +763,7 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||
struct locfile locations;
|
||||
locfile_init(&locations, str, strlen(str));
|
||||
locfile_init(&locations, jq, str, strlen(str));
|
||||
block program;
|
||||
jq_reset(jq);
|
||||
if (jq->bc) {
|
||||
@@ -745,14 +779,22 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
program = gen_var_binding(gen_const(value), jv_string_value(name), program);
|
||||
jv_free(name);
|
||||
}
|
||||
jv_free(args);
|
||||
nerrors = builtins_bind(&program);
|
||||
nerrors = builtins_bind(jq, &program);
|
||||
if (nerrors == 0) {
|
||||
nerrors = block_compile(program, &locations, &jq->bc);
|
||||
}
|
||||
}
|
||||
jv_free(args);
|
||||
if (nerrors) {
|
||||
fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error");
|
||||
jv s = jv_string_fmt("%d compile %s", nerrors,
|
||||
nerrors > 1 ? "errors" : "error");
|
||||
if (jq->err_cb != NULL)
|
||||
jq->err_cb(jq->err_cb_data, s);
|
||||
else if (!jv_is_valid(s))
|
||||
fprintf(stderr, "Error formatting jq compilation errors: %s\n", strerror(errno));
|
||||
else
|
||||
fprintf(stderr, "%s\n", jv_string_value(s));
|
||||
jv_free(s);
|
||||
}
|
||||
locfile_free(&locations);
|
||||
return jq->bc != NULL;
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#ifndef _JQ_H_
|
||||
#define _JQ_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jv.h>
|
||||
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
typedef void (*jq_err_cb)(void *, jv);
|
||||
|
||||
jq_state *jq_init(void);
|
||||
void jq_set_error_cb(jq_state *, jq_err_cb, void *);
|
||||
void jq_get_error_cb(jq_state *, jq_err_cb *, void **);
|
||||
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
|
||||
int jq_compile(jq_state *, const char* str);
|
||||
int jq_compile_args(jq_state *, const char* str, jv args);
|
||||
|
||||
@@ -52,11 +52,16 @@ rm -rf %{buildroot}
|
||||
%endif
|
||||
%{_datadir}/doc/%{name}/AUTHORS
|
||||
%{_datadir}/doc/%{name}/COPYING
|
||||
%{_datadir}/doc/%{name}/INSTALL
|
||||
%{_datadir}/doc/%{name}/NEWS
|
||||
%{_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
|
||||
|
||||
|
||||
@@ -160,9 +160,9 @@ static void jv_test() {
|
||||
|
||||
jv_free(subarray);
|
||||
|
||||
void* before = sub2.val.nontrivial.ptr;
|
||||
void* before = sub2.u.ptr;
|
||||
sub2 = jv_array_append(sub2, jv_number(200));
|
||||
void* after = sub2.val.nontrivial.ptr;
|
||||
void* after = sub2.u.ptr;
|
||||
assert(before == after);
|
||||
jv_free(sub2);
|
||||
|
||||
@@ -182,8 +182,10 @@ static void jv_test() {
|
||||
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(a4) == 2);
|
||||
assert(jv_array_length(a5) == 2);
|
||||
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);
|
||||
@@ -201,8 +203,10 @@ static void jv_test() {
|
||||
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(shortstr) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(longstr) == (int)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";
|
||||
@@ -248,4 +252,11 @@ static void jv_test() {
|
||||
|
||||
jv_free(o2);
|
||||
}
|
||||
|
||||
/// Compile errors
|
||||
{
|
||||
jq_state *jq = jq_init();
|
||||
jq_compile_args(jq, "}", jv_array());
|
||||
jq_teardown(&jq);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifndef JV_H
|
||||
#define JV_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum {
|
||||
JV_KIND_INVALID,
|
||||
JV_KIND_NULL,
|
||||
@@ -13,17 +17,18 @@ typedef enum {
|
||||
} jv_kind;
|
||||
|
||||
struct jv_refcnt;
|
||||
typedef struct{
|
||||
struct jv_refcnt* ptr;
|
||||
int i[2];
|
||||
} jv_nontrivial;
|
||||
|
||||
/* All of the fields of this struct are private.
|
||||
Really. Do not play with them. */
|
||||
typedef struct {
|
||||
jv_kind kind;
|
||||
unsigned char kind_flags;
|
||||
unsigned char pad_;
|
||||
unsigned short offset; /* array offsets */
|
||||
int size;
|
||||
union {
|
||||
struct jv_refcnt* ptr;
|
||||
double number;
|
||||
jv_nontrivial nontrivial;
|
||||
} val;
|
||||
} u;
|
||||
} jv;
|
||||
|
||||
/*
|
||||
@@ -43,21 +48,22 @@ int jv_get_refcnt(jv);
|
||||
int jv_equal(jv, jv);
|
||||
int jv_contains(jv, jv);
|
||||
|
||||
jv jv_invalid();
|
||||
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();
|
||||
jv jv_true();
|
||||
jv jv_false();
|
||||
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();
|
||||
jv jv_array(void);
|
||||
jv jv_array_sized(int);
|
||||
int jv_array_length(jv);
|
||||
jv jv_array_get(jv, int);
|
||||
@@ -65,6 +71,7 @@ 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) \
|
||||
@@ -75,21 +82,30 @@ jv jv_array_slice(jv, int, int);
|
||||
|
||||
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 jv_string_fmt(const char*, ...);
|
||||
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();
|
||||
jv jv_object(void);
|
||||
jv jv_object_get(jv object, jv key);
|
||||
jv jv_object_set(jv object, jv key, jv value);
|
||||
jv jv_object_delete(jv object, jv key);
|
||||
int jv_object_length(jv object);
|
||||
jv jv_object_merge(jv, jv);
|
||||
jv jv_object_merge_recursive(jv, jv);
|
||||
|
||||
int jv_object_iter(jv);
|
||||
int jv_object_iter_next(jv, int);
|
||||
@@ -111,7 +127,9 @@ jv jv_object_iter_value(jv, int);
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2, JV_PRINT_COLOUR = 4, JV_PRINT_SORTED = 8 };
|
||||
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);
|
||||
|
||||
jv jv_parse(const char* string);
|
||||
@@ -123,7 +141,7 @@ void jv_nomem_handler(jv_nomem_handler_f, void *);
|
||||
jv jv_load_file(const char *, int);
|
||||
|
||||
struct jv_parser;
|
||||
struct jv_parser* jv_parser_new();
|
||||
struct jv_parser* jv_parser_new(int);
|
||||
void jv_parser_set_buf(struct jv_parser*, const char*, int, int);
|
||||
jv jv_parser_next(struct jv_parser*);
|
||||
void jv_parser_free(struct jv_parser*);
|
||||
|
||||
+52
-20
@@ -7,13 +7,27 @@ struct nomem_handler {
|
||||
void *data;
|
||||
};
|
||||
|
||||
#ifndef USE_PTHREAD_KEY
|
||||
#ifdef _MSC_VER
|
||||
static __declspec(thread) struct nomem_handler nomem_handler;
|
||||
#else
|
||||
static __thread struct nomem_handler nomem_handler;
|
||||
#endif
|
||||
#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;
|
||||
}
|
||||
@@ -25,44 +39,60 @@ static void memory_exhausted() {
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
#else /* USE_TLS */
|
||||
|
||||
#ifdef HAVE_PTHREAD_KEY_CREATE
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_key_t nomem_handler_key;
|
||||
pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void tsd_fini(void) {
|
||||
struct nomem_handler *nomem_handler;
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler) {
|
||||
(void) pthread_setspecific(nomem_handler_key, NULL);
|
||||
free(nomem_handler);
|
||||
}
|
||||
}
|
||||
|
||||
static void tsd_init(void) {
|
||||
if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
|
||||
fprintf(stderr, "error: cannot create thread specific key");
|
||||
abort();
|
||||
}
|
||||
if (atexit(tsd_fini) != 0) {
|
||||
fprintf(stderr, "error: cannot set an exit handler");
|
||||
abort();
|
||||
}
|
||||
struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));
|
||||
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
fprintf(stderr, "error: cannot set thread specific data");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
pthread_once(&mem_once, tsd_init); // cannot fail
|
||||
struct nomem_handler *nomem_handler = calloc(1, sizeof (nomem_handler));
|
||||
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;
|
||||
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
handler(data);
|
||||
fprintf(stderr, "error: cannot set thread specific data");
|
||||
abort();
|
||||
}
|
||||
nomem_handler->handler = handler;
|
||||
nomem_handler->data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
jv_nomem_handler_f handler;
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
pthread_once(&mem_once, tsd_init);
|
||||
handler = pthread_getspecific(nomem_handler_key);
|
||||
if (handler)
|
||||
handler(); // Maybe handler() will longjmp() to safety
|
||||
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();
|
||||
@@ -70,6 +100,8 @@ static void memory_exhausted() {
|
||||
|
||||
#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;
|
||||
@@ -82,7 +114,7 @@ static void memory_exhausted() {
|
||||
}
|
||||
|
||||
#endif /* HAVE_PTHREAD_KEY_CREATE */
|
||||
#endif /* !USE_PTHREAD_KEY */
|
||||
#endif /* USE_TLS */
|
||||
|
||||
|
||||
void* jv_mem_alloc(size_t sz) {
|
||||
|
||||
@@ -3,15 +3,19 @@
|
||||
#include <assert.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
static int parse_slice(jv array, jv slice, int* pstart, int* pend) {
|
||||
static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
|
||||
// Array slices
|
||||
int len = jv_array_length(jv_copy(array));
|
||||
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(jv_copy(j));
|
||||
else
|
||||
len = jv_string_length_codepoints(jv_copy(j));
|
||||
if (jv_get_kind(end_jv) == JV_KIND_NULL) {
|
||||
jv_free(end_jv);
|
||||
end_jv = jv_number(len);
|
||||
@@ -22,13 +26,18 @@ static int parse_slice(jv array, jv slice, int* pstart, int* pend) {
|
||||
jv_free(end_jv);
|
||||
return 0;
|
||||
} else {
|
||||
int start = (int)jv_number_value(start_jv);
|
||||
int end = (int)jv_number_value(end_jv);
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end;
|
||||
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 (start < 0) start = 0;
|
||||
if (start > len) start = len;
|
||||
if (end > len) end = len;
|
||||
if (end < start) end = start;
|
||||
assert(0 <= start && start <= end && end <= len);
|
||||
@@ -47,10 +56,15 @@ jv jv_get(jv t, jv k) {
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
|
||||
// FIXME: don't do lookup for noninteger index
|
||||
v = jv_array_get(t, (int)jv_number_value(k));
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(v);
|
||||
if(jv_is_integer(k)){
|
||||
v = jv_array_get(t, (int)jv_number_value(k));
|
||||
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) {
|
||||
@@ -61,6 +75,18 @@ jv jv_get(jv t, jv k) {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
|
||||
jv_free(t);
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {
|
||||
int start, end;
|
||||
if (parse_slice(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_STRING && jv_get_kind(k) == JV_KIND_STRING) {
|
||||
v = jv_string_indexes(t, k);
|
||||
} 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 ||
|
||||
@@ -545,21 +571,23 @@ jv jv_group(jv objects, jv keys) {
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = sort_items(objects, keys);
|
||||
jv ret = jv_array();
|
||||
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();
|
||||
if (n > 0) {
|
||||
jv curr_key = entries[0].key;
|
||||
jv group = jv_array_append(jv_array(), entries[0].object);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
|
||||
jv_free(entries[i].key);
|
||||
} else {
|
||||
jv_free(curr_key);
|
||||
curr_key = entries[i].key;
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array();
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
}
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
jv_mem_free(entries);
|
||||
return ret;
|
||||
}
|
||||
@@ -4225,7 +4225,7 @@ jvp_dtoa
|
||||
while((*b++ = *s++));
|
||||
goto done0;
|
||||
}
|
||||
if (decpt <= -4 || decpt > se - s + 5) {
|
||||
if (decpt <= -4 || decpt > se - s + 15) {
|
||||
*b++ = *s++;
|
||||
if (*s) {
|
||||
*b++ = '.';
|
||||
|
||||
@@ -17,6 +17,6 @@ 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 32
|
||||
#define JVP_DTOA_FMT_MAX_LEN 64
|
||||
char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);
|
||||
#endif
|
||||
@@ -18,7 +18,7 @@ jv jv_load_file(const char* filename, int raw) {
|
||||
data = jv_string("");
|
||||
} else {
|
||||
data = jv_array();
|
||||
parser = jv_parser_new();
|
||||
parser = jv_parser_new(0);
|
||||
}
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
|
||||
+1
-1
@@ -370,7 +370,7 @@ static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
||||
return answer;
|
||||
}
|
||||
|
||||
struct jv_parser* jv_parser_new() {
|
||||
struct jv_parser* jv_parser_new(int flags) {
|
||||
struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));
|
||||
parser_init(p);
|
||||
return p;
|
||||
|
||||
+13
-2
@@ -260,13 +260,24 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI
|
||||
}
|
||||
}
|
||||
|
||||
void jv_dump(jv x, int flags) {
|
||||
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, stdout, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
void jv_show(jv x, int flags) {
|
||||
if (flags == -1)
|
||||
flags = JV_PRINT_PRETTY | JV_PRINT_COLOUR;
|
||||
jv_dumpf(jv_copy(x), stderr, flags);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
jv jv_dump_string(jv x, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
|
||||
+41
-16
@@ -3,35 +3,60 @@
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_utf8_tables.h"
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint) {
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {
|
||||
assert(in <= end);
|
||||
if (in == end) {
|
||||
codepoint = 0;
|
||||
return 0;
|
||||
}
|
||||
int codepoint = -1;
|
||||
unsigned char first = (unsigned char)in[0];
|
||||
int length = utf8_coding_length[first];
|
||||
if (length == 0 || length == UTF8_CONTINUATION_BYTE || in + length > end) {
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
}
|
||||
*codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
int ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[(unsigned char)in[i]] != UTF8_CONTINUATION_BYTE){
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
if ((first & 0x80) == 0) {
|
||||
/* Fast-path for ASCII */
|
||||
codepoint = first;
|
||||
length = 1;
|
||||
} else if (length == 0 || length == UTF8_CONTINUATION_BYTE) {
|
||||
/* Bad single byte - either an invalid byte or an out-of-place continuation byte */
|
||||
length = 1;
|
||||
} else if (in + length > end) {
|
||||
/* String ends before UTF8 sequence ends */
|
||||
length = end - in;
|
||||
} else {
|
||||
codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
unsigned ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[ch] != UTF8_CONTINUATION_BYTE){
|
||||
/* Invalid UTF8 sequence - not followed by the right number of continuation bytes */
|
||||
codepoint = -1;
|
||||
length = i;
|
||||
break;
|
||||
}
|
||||
codepoint = (codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
if (codepoint < utf8_first_codepoint[length]) {
|
||||
/* Overlong UTF8 sequence */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (0xD800 <= codepoint && codepoint <= 0xDFFF) {
|
||||
/* Surrogate codepoints can't be encoded in UTF8 */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (codepoint > 0x10FFFF) {
|
||||
/* Outside Unicode range */
|
||||
codepoint = -1;
|
||||
}
|
||||
*codepoint = (*codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
assert(length > 0);
|
||||
*codepoint_ret = codepoint;
|
||||
return in + length;
|
||||
}
|
||||
|
||||
int jvp_utf8_verify(const char* in, const char* end) {
|
||||
int codepoint = 0;
|
||||
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 codepoint != -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int jvp_utf8_encode_length(int codepoint) {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#define JV_UNICODE_H
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);
|
||||
|
||||
int jvp_utf8_is_valid(const char* in, const char* end);
|
||||
|
||||
int jvp_utf8_decode_length(char startchar);
|
||||
|
||||
|
||||
@@ -33,3 +33,5 @@ static const unsigned char utf8_coding_bits[] =
|
||||
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};
|
||||
@@ -57,10 +57,12 @@ struct lexer_param;
|
||||
"-=" { return SETMINUS; }
|
||||
"*=" { return SETMULT; }
|
||||
"/=" { return SETDIV; }
|
||||
"%=" { return SETMOD; }
|
||||
"//=" { return SETDEFINEDOR; }
|
||||
"<=" { return LESSEQ; }
|
||||
">=" { return GREATEREQ; }
|
||||
"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$"|"<"|">" { return yytext[0];}
|
||||
".." { return REC; }
|
||||
"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}
|
||||
|
||||
"["|"{"|"(" {
|
||||
return enter(yytext[0], YY_START, yyscanner);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
LIBM_DD(acos)
|
||||
LIBM_DD(acosh)
|
||||
LIBM_DD(asin)
|
||||
LIBM_DD(asinh)
|
||||
LIBM_DD(atan)
|
||||
LIBM_DD(atanh)
|
||||
LIBM_DD(cbrt)
|
||||
LIBM_DD(cos)
|
||||
LIBM_DD(cosh)
|
||||
LIBM_DD(exp2)
|
||||
LIBM_DD(exp)
|
||||
LIBM_DD(floor)
|
||||
LIBM_DD(j0)
|
||||
LIBM_DD(j1)
|
||||
LIBM_DD(log10)
|
||||
LIBM_DD(log2)
|
||||
LIBM_DD(log)
|
||||
LIBM_DD(sin)
|
||||
LIBM_DD(sinh)
|
||||
LIBM_DD(sqrt)
|
||||
LIBM_DD(tan)
|
||||
LIBM_DD(tanh)
|
||||
LIBM_DD(tgamma)
|
||||
LIBM_DD(y0)
|
||||
LIBM_DD(y1)
|
||||
/* LIBM_DID(jn) */
|
||||
/* LIBM_DID(yn) */
|
||||
/* LIBM_DDD(pow) */
|
||||
/* LIBM_DDD(atan2) */
|
||||
/* LIBM_DDD(hypot) */
|
||||
/* LIBM_DDD(remainder) */
|
||||
/* LIBM_DDI(scalbn) */
|
||||
/* LIBM_DDIP(lgamma_r) */
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "jq.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "locfile.h"
|
||||
|
||||
|
||||
void locfile_init(struct locfile* l, const char* data, int length) {
|
||||
void locfile_init(struct locfile* l, jq_state *jq, const char* data, int length) {
|
||||
l->jq = jq;
|
||||
l->data = data;
|
||||
l->length = length;
|
||||
l->nlines = 1;
|
||||
@@ -43,23 +47,57 @@ static int locfile_line_length(struct locfile* l, int line) {
|
||||
}
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
jq_err_cb cb;
|
||||
void *cb_data;
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
vfprintf(stderr, fmt, fmtargs);
|
||||
va_end(fmtargs);
|
||||
fprintf(stderr, "\n");
|
||||
int startline;
|
||||
int offset;
|
||||
|
||||
if (loc.start != -1) {
|
||||
startline = locfile_get_line(l, loc.start);
|
||||
offset = l->linemap[startline];
|
||||
}
|
||||
|
||||
jq_get_error_cb(l->jq, &cb, &cb_data);
|
||||
|
||||
jv m1 = jv_string_vfmt(fmt, fmtargs);
|
||||
if (!jv_is_valid(m1)) {
|
||||
jv_free(m1);
|
||||
goto enomem;
|
||||
}
|
||||
jv m2;
|
||||
if (loc.start == -1) {
|
||||
fprintf(stderr, "<unknown location>\n");
|
||||
m2 = jv_string_fmt("%s\n<unknown location>", jv_string_value(m1));
|
||||
if (cb)
|
||||
cb(cb_data, m2);
|
||||
else
|
||||
fprintf(stderr, "%s", jv_string_value(m2));
|
||||
jv_free(m1);
|
||||
jv_free(m2);
|
||||
return;
|
||||
}
|
||||
int startline = locfile_get_line(l, loc.start);
|
||||
int offset = l->linemap[startline];
|
||||
fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset);
|
||||
fprintf(stderr, "%*s", loc.start - offset, "");
|
||||
for (int i = loc.start;
|
||||
i < loc.end && i < offset + locfile_line_length(l, startline);
|
||||
i++){
|
||||
fprintf(stderr, "^");
|
||||
m2 = jv_string_fmt("%s\n%.*s%*s", jv_string_value(m1),
|
||||
locfile_line_length(l, startline), l->data + offset,
|
||||
loc.start - offset, "");
|
||||
jv_free(m1);
|
||||
if (!jv_is_valid(m2)) {
|
||||
jv_free(m2);
|
||||
goto enomem;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
if (cb)
|
||||
cb(cb_data, m2);
|
||||
else
|
||||
fprintf(stderr, "%s", jv_string_value(m2));
|
||||
jv_free(m2);
|
||||
return;
|
||||
|
||||
enomem:
|
||||
if (cb != NULL)
|
||||
cb(cb_data, jv_invalid());
|
||||
else if (errno == ENOMEM || errno == 0)
|
||||
fprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno ? errno : ENOMEM));
|
||||
else
|
||||
fprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef _LOCFILE_H
|
||||
#define _LOCFILE_H
|
||||
|
||||
#include "jq.h"
|
||||
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} location;
|
||||
@@ -12,9 +14,11 @@ struct locfile {
|
||||
int length;
|
||||
int* linemap;
|
||||
int nlines;
|
||||
char *error;
|
||||
jq_state *jq;
|
||||
};
|
||||
|
||||
void locfile_init(struct locfile* l, const char* data, int length);
|
||||
void locfile_init(struct locfile* l, jq_state *jq, const char* data, int length);
|
||||
|
||||
void locfile_free(struct locfile* l);
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ static void usage() {
|
||||
fprintf(stderr, "how to write jq filters (and why you might want to)\n");
|
||||
fprintf(stderr, "see the jq manpage, or the online documentation at\n");
|
||||
fprintf(stderr, "http://stedolan.github.com/jq\n\n");
|
||||
exit(1);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void die() {
|
||||
fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
|
||||
fprintf(stderr, "or see the jq documentation at http://stedolan.github.com/jq\n");
|
||||
exit(1);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,20 +55,25 @@ enum {
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
SORTED_OUTPUT = 256,
|
||||
UNBUFFERED_OUTPUT = 4096,
|
||||
|
||||
FROM_FILE = 512,
|
||||
|
||||
EXIT_STATUS = 8192,
|
||||
|
||||
/* debugging only */
|
||||
DUMP_DISASM = 2048,
|
||||
};
|
||||
static int options = 0;
|
||||
|
||||
static void process(jq_state *jq, jv value, int flags) {
|
||||
static int process(jq_state *jq, jv value, int flags) {
|
||||
int ret = 14; // No valid results && -e -> exit(4)
|
||||
jq_start(jq, value, flags);
|
||||
jv result;
|
||||
while (jv_is_valid(result = jq_next(jq))) {
|
||||
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
|
||||
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
|
||||
ret = 0;
|
||||
jv_free(result);
|
||||
} else {
|
||||
int dumpopts;
|
||||
@@ -84,15 +89,22 @@ static void process(jq_state *jq, jv value, int flags) {
|
||||
if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
|
||||
if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
|
||||
if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
|
||||
if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
|
||||
ret = 11;
|
||||
else
|
||||
ret = 0;
|
||||
jv_dump(result, dumpopts);
|
||||
}
|
||||
printf("\n");
|
||||
if (options & UNBUFFERED_OUTPUT)
|
||||
fflush(stdout);
|
||||
}
|
||||
jv_free(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FILE* current_input;
|
||||
const char** input_filenames;
|
||||
const char** input_filenames = NULL;
|
||||
int ninput_files;
|
||||
int next_input_idx;
|
||||
static int read_more(char* buf, size_t size) {
|
||||
@@ -120,7 +132,7 @@ static int read_more(char* buf, size_t size) {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
jq_state *jq;
|
||||
jq_state *jq = NULL;
|
||||
int ret = 0;
|
||||
int compiled = 0;
|
||||
|
||||
@@ -133,7 +145,8 @@ int main(int argc, char* argv[]) {
|
||||
jq = jq_init();
|
||||
if (jq == NULL) {
|
||||
perror("malloc");
|
||||
return 1;
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
const char* program = 0;
|
||||
@@ -166,6 +179,8 @@ int main(int argc, char* argv[]) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'a', "ascii-output")) {
|
||||
options |= ASCII_OUTPUT;
|
||||
} else if (isoption(argv[i], 0, "unbuffered")) {
|
||||
options |= UNBUFFERED_OUTPUT;
|
||||
} else if (isoption(argv[i], 'S', "sort-keys")) {
|
||||
options |= SORTED_OUTPUT;
|
||||
} else if (isoption(argv[i], 'R', "raw-input")) {
|
||||
@@ -174,6 +189,8 @@ int main(int argc, char* argv[]) {
|
||||
options |= PROVIDE_NULL;
|
||||
} else if (isoption(argv[i], 'f', "from-file")) {
|
||||
options |= FROM_FILE;
|
||||
} else if (isoption(argv[i], 'e', "exit-status")) {
|
||||
options |= EXIT_STATUS;
|
||||
} else if (isoption(argv[i], 0, "arg")) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname);
|
||||
@@ -197,7 +214,8 @@ int main(int argc, char* argv[]) {
|
||||
fprintf(stderr, "%s: Bad JSON in --argfile %s %s: %s\n", progname,
|
||||
argv[i+1], argv[i+2], jv_string_value(data));
|
||||
jv_free(data);
|
||||
return 1;
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
if (jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
|
||||
data = jv_array_get(data, 0);
|
||||
@@ -212,7 +230,8 @@ int main(int argc, char* argv[]) {
|
||||
usage();
|
||||
} else if (isoption(argv[i], 'V', "version")) {
|
||||
printf("jq-%s\n", JQ_VERSION);
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
die();
|
||||
@@ -232,14 +251,18 @@ int main(int argc, char* argv[]) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
|
||||
jv_free(data);
|
||||
return 1;
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
compiled = jq_compile_args(jq, jv_string_value(data), program_arguments);
|
||||
jv_free(data);
|
||||
} else {
|
||||
compiled = jq_compile_args(jq, program, program_arguments);
|
||||
}
|
||||
if (!compiled) return 1;
|
||||
if (!compiled){
|
||||
ret = 3;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (options & DUMP_DISASM) {
|
||||
jq_dump_disassembly(jq, 0);
|
||||
@@ -247,7 +270,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
if (options & PROVIDE_NULL) {
|
||||
process(jq, jv_null(), jq_flags);
|
||||
ret = process(jq, jv_null(), jq_flags);
|
||||
} else {
|
||||
jv slurped;
|
||||
if (options & SLURP) {
|
||||
@@ -257,7 +280,7 @@ int main(int argc, char* argv[]) {
|
||||
slurped = jv_array();
|
||||
}
|
||||
}
|
||||
struct jv_parser* parser = jv_parser_new();
|
||||
struct jv_parser* parser = jv_parser_new(0);
|
||||
char buf[4096];
|
||||
while (read_more(buf, sizeof(buf))) {
|
||||
if (options & RAW_INPUT) {
|
||||
@@ -267,7 +290,7 @@ int main(int argc, char* argv[]) {
|
||||
slurped = jv_string_concat(slurped, jv_string(buf));
|
||||
} else {
|
||||
if (buf[len-1] == '\n') buf[len-1] = 0;
|
||||
process(jq, jv_string(buf), jq_flags);
|
||||
ret = process(jq, jv_string(buf), jq_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -277,14 +300,14 @@ int main(int argc, char* argv[]) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_array_append(slurped, value);
|
||||
} else {
|
||||
process(jq, value, jq_flags);
|
||||
ret = process(jq, value, jq_flags);
|
||||
}
|
||||
}
|
||||
if (jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
|
||||
jv_free(msg);
|
||||
ret = 1;
|
||||
ret = 4;
|
||||
break;
|
||||
} else {
|
||||
jv_free(value);
|
||||
@@ -295,11 +318,15 @@ int main(int argc, char* argv[]) {
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
if (options & SLURP) {
|
||||
process(jq, slurped, jq_flags);
|
||||
ret = process(jq, slurped, jq_flags);
|
||||
}
|
||||
}
|
||||
out:
|
||||
jv_mem_free(input_filenames);
|
||||
jq_teardown(&jq);
|
||||
if (ret >= 10 && (options & EXIT_STATUS))
|
||||
return ret - 10;
|
||||
if (ret >= 10)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
@@ -6,7 +6,9 @@ OP(LOADV, VARIABLE, 1, 1)
|
||||
OP(LOADVN, VARIABLE, 1, 1)
|
||||
OP(STOREV, VARIABLE, 1, 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(JUMP, BRANCH, 0, 0)
|
||||
OP(JUMP_F,BRANCH, 1, 0)
|
||||
|
||||
@@ -50,6 +50,9 @@ struct lexer_param;
|
||||
%token <literal> FIELD
|
||||
%token <literal> LITERAL
|
||||
%token <literal> FORMAT
|
||||
%token Q "?"
|
||||
%token REC ".."
|
||||
%token SETMOD "%="
|
||||
%token EQ "=="
|
||||
%token NEQ "!="
|
||||
%token DEFINEDOR "//"
|
||||
@@ -83,12 +86,12 @@ struct lexer_param;
|
||||
%right '|'
|
||||
%left ','
|
||||
%right "//"
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETMOD SETDEFINEDOR
|
||||
%left OR
|
||||
%left AND
|
||||
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
%left '*' '/' '%'
|
||||
|
||||
|
||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String
|
||||
@@ -136,7 +139,11 @@ static block gen_index(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_slice_index(block obj, block start, block end) {
|
||||
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),
|
||||
@@ -144,7 +151,7 @@ static block gen_slice_index(block obj, block start, block end) {
|
||||
gen_subexp(gen_const(jv_string("end"))),
|
||||
gen_subexp(end),
|
||||
gen_op_simple(INSERT));
|
||||
return BLOCK(key, obj, gen_op_simple(INDEX));
|
||||
return BLOCK(key, obj, gen_op_simple(idx_op));
|
||||
}
|
||||
|
||||
static block gen_binop(block a, block b, int op) {
|
||||
@@ -154,6 +161,7 @@ static block gen_binop(block a, block b, int op) {
|
||||
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;
|
||||
@@ -228,7 +236,7 @@ Term "as" '$' IDENT '|' Exp {
|
||||
$$ = gen_cond($2, $4, $5);
|
||||
} |
|
||||
"if" Exp "then" error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statment");
|
||||
FAIL(@$, "Possibly unterminated 'if' statement");
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
@@ -296,10 +304,18 @@ Exp '/' Exp {
|
||||
$$ = gen_binop($1, $3, '/');
|
||||
} |
|
||||
|
||||
Exp '%' Exp {
|
||||
$$ = gen_binop($1, $3, '%');
|
||||
} |
|
||||
|
||||
Exp "/=" Exp {
|
||||
$$ = gen_update($1, $3, '/');
|
||||
} |
|
||||
|
||||
Exp SETMOD Exp {
|
||||
$$ = gen_update($1, $3, '%');
|
||||
} |
|
||||
|
||||
Exp "==" Exp {
|
||||
$$ = gen_binop($1, $3, EQ);
|
||||
} |
|
||||
@@ -350,8 +366,67 @@ FuncDef:
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
}
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6)),
|
||||
gen_param(jv_string_value($8))),
|
||||
$11);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
jv_free($8);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ';' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6)),
|
||||
gen_param(jv_string_value($8)),
|
||||
gen_param(jv_string_value($10))),
|
||||
$13);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
jv_free($8);
|
||||
jv_free($10);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ';' IDENT ';' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6)),
|
||||
gen_param(jv_string_value($8)),
|
||||
gen_param(jv_string_value($10)),
|
||||
gen_param(jv_string_value($12))),
|
||||
$15);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
jv_free($8);
|
||||
jv_free($10);
|
||||
jv_free($12);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ';' IDENT ';' IDENT ';' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6)),
|
||||
gen_param(jv_string_value($8)),
|
||||
gen_param(jv_string_value($10)),
|
||||
gen_param(jv_string_value($12)),
|
||||
gen_param(jv_string_value($14))),
|
||||
$17);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
jv_free($8);
|
||||
jv_free($10);
|
||||
jv_free($12);
|
||||
jv_free($14);
|
||||
}
|
||||
|
||||
|
||||
String:
|
||||
@@ -389,7 +464,9 @@ ExpD:
|
||||
ExpD '|' ExpD {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
|
||||
'-' ExpD {
|
||||
$$ = BLOCK($2, gen_call("_negate", gen_noop()));
|
||||
} |
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
@@ -399,6 +476,21 @@ Term:
|
||||
'.' {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
REC {
|
||||
$$ = gen_call("recurse_down", 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 {
|
||||
$$ = gen_index($1, gen_const($2));
|
||||
} |
|
||||
@@ -421,20 +513,35 @@ Term '.' String {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
/* FIXME: string literals */
|
||||
Term '[' Exp ']' '?' {
|
||||
$$ = gen_index_opt($1, $3);
|
||||
} |
|
||||
Term '[' Exp ']' {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
Term '[' ']' '?' {
|
||||
$$ = block_join($1, gen_op_simple(EACH_OPT));
|
||||
} |
|
||||
Term '[' ']' {
|
||||
$$ = 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 ']' {
|
||||
$$ = gen_slice_index($1, $3, $5);
|
||||
$$ = gen_slice_index($1, $3, $5, INDEX);
|
||||
} |
|
||||
Term '[' Exp ':' ']' {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()));
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX);
|
||||
} |
|
||||
Term '[' ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4);
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX);
|
||||
} |
|
||||
LITERAL {
|
||||
$$ = gen_const($1);
|
||||
@@ -475,6 +582,26 @@ IDENT '(' Exp ';' Exp ')' {
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5), gen_lambda($7)));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5), gen_lambda($7), gen_lambda($9)));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ';' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5), gen_lambda($7), gen_lambda($9), gen_lambda($11)));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ';' Exp ';' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5), gen_lambda($7), gen_lambda($9), gen_lambda($11), gen_lambda($13)));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
'(' error ')' { $$ = gen_noop(); } |
|
||||
'[' error ']' { $$ = gen_noop(); } |
|
||||
Term '[' error ']' { $$ = $1; } |
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
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
|
||||
@@ -26,6 +31,7 @@ mkdir install_other
|
||||
--prefix="`pwd`/install_other" \
|
||||
--bindir="`pwd`/.." \
|
||||
"$@"
|
||||
make install-binaries
|
||||
make "$jobs" install-binaries
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
rm -rf tmp
|
||||
|
||||
@@ -6,13 +6,12 @@ mask = lambda n: (1 << n) - 1
|
||||
|
||||
|
||||
def print_table(type, name, t):
|
||||
assert len(t) == 256
|
||||
print("static const %s %s[] =" % (type, name))
|
||||
first = True
|
||||
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 < 256 else "};"))
|
||||
("," if i + 16 < len(t) else "};"))
|
||||
|
||||
|
||||
def utf8info(c):
|
||||
@@ -30,3 +29,4 @@ 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])
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
set -e
|
||||
cd `dirname "$0"`
|
||||
git rev-parse --verify -q jq-1.0 > /dev/null 2>&1
|
||||
git describe --always --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
git describe --tags --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
@@ -9,12 +9,14 @@
|
||||
|
||||
if [ "clean" == "$1" ]; then
|
||||
make distclean || echo "continuing..."
|
||||
rm -rf tmp autom4te.cache config
|
||||
find config/m4 -type f \( -name libtool\* -o -name lt\* \) | xargs rm -f
|
||||
find config -name m4 -prune -o -type f -print | xargs rm -f
|
||||
rm -rf tmp autom4te.cache
|
||||
rm -f INSTALL Makefile.in aclocal.m4 configure config.h.in ChangeLog
|
||||
rm -f jv_utf8_tables.gen.h lexer.c lexer.h parser.c parser.h
|
||||
elif [ "superclean" == "$1" ]; then
|
||||
# if autoconf errors during distcheck, it leaves files that need chmod'ing
|
||||
ver=$(tr -d '\n' <VERSION)
|
||||
ver=$(scripts/version|tr -d '\n')
|
||||
if [ "x${ver}" != "x" ]; then
|
||||
if [ -d jq-${ver} ]; then
|
||||
chmod -R u+w jq-${ver}
|
||||
|
||||
+238
-1
@@ -36,6 +36,10 @@ 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.
|
||||
@@ -75,6 +79,10 @@ null
|
||||
"<script>hax</script>"
|
||||
"<b><script>hax</script></b>"
|
||||
|
||||
[.[]|tojson|fromjson]
|
||||
["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]]
|
||||
["foo",1,["a",1,"b",2,{"foo":"bar"}]]
|
||||
|
||||
#
|
||||
# Dictionary construction syntax
|
||||
#
|
||||
@@ -119,6 +127,25 @@ null
|
||||
{"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]]
|
||||
|
||||
#
|
||||
# Multiple outputs, iteration
|
||||
@@ -178,6 +205,10 @@ null
|
||||
[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]
|
||||
@@ -289,6 +320,14 @@ null
|
||||
null
|
||||
[2, 8, 10, 14]
|
||||
|
||||
25 % 7
|
||||
null
|
||||
4
|
||||
|
||||
49732 % 472
|
||||
null
|
||||
172
|
||||
|
||||
1 + tonumber + ("10" | tonumber)
|
||||
4
|
||||
15
|
||||
@@ -336,6 +375,10 @@ def f: (1000,2000); f
|
||||
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]
|
||||
|
||||
([1,2] + [4,5])
|
||||
[1,2,3]
|
||||
[1,2,4,5]
|
||||
@@ -354,6 +397,36 @@ null
|
||||
[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]]]
|
||||
@@ -404,6 +477,14 @@ path(.)
|
||||
42
|
||||
[]
|
||||
|
||||
[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"
|
||||
@@ -452,12 +533,17 @@ null
|
||||
{"foo": 42}
|
||||
{"foo": 43}
|
||||
|
||||
.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2
|
||||
.[] += 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}
|
||||
@@ -549,6 +635,75 @@ def inc(x): x |= .+1; inc(.[].a)
|
||||
{}
|
||||
[true, true, false]
|
||||
|
||||
# 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]
|
||||
|
||||
[.[]|ltrimstr("foo")]
|
||||
["fo", "foo", "barfoo", "foobar", "afoo"]
|
||||
["fo","","barfoo","bar","afoo"]
|
||||
|
||||
[.[]|rtrimstr("foo")]
|
||||
["fo", "foo", "barfoo", "foobar", "foob"]
|
||||
["fo","","bar","foobar","foob"]
|
||||
|
||||
.[","]
|
||||
"a,bc,def,ghij,klmno"
|
||||
[1,4,8,13]
|
||||
|
||||
.[", "]
|
||||
"a,bc,def,ghij,klmno"
|
||||
[]
|
||||
|
||||
.[", "]
|
||||
"a,b,, c, d,ef, , ghi, jklmn, o"
|
||||
[4,7,13,15,20,27]
|
||||
|
||||
[(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]
|
||||
@@ -585,6 +740,10 @@ 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"]]
|
||||
@@ -604,3 +763,81 @@ unique
|
||||
[{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}]
|
||||
{"a": 1, "b": 2}
|
||||
|
||||
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]
|
||||
Reference in new issue
Block a user