Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ed2da6d43 | ||
|
|
444f0aacca | ||
|
|
22006172ba |
No files matched your search
+1
-24
@@ -1,13 +1,5 @@
|
||||
*.o
|
||||
*.a
|
||||
*.lo
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.so.*
|
||||
*~
|
||||
.*.sw[a-p]
|
||||
tags
|
||||
|
||||
jq
|
||||
jq.1
|
||||
@@ -22,30 +14,15 @@ parser.c
|
||||
parser.h
|
||||
|
||||
# Autotools junk
|
||||
.libs
|
||||
.deps
|
||||
libtool
|
||||
*.log
|
||||
stamp-h1
|
||||
config.log
|
||||
config.status
|
||||
autom4te.cache
|
||||
INSTALL
|
||||
config.h
|
||||
Makefile
|
||||
jq-*.tar.gz
|
||||
configure
|
||||
aclocal.m4
|
||||
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
@@ -1,17 +0,0 @@
|
||||
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
|
||||
@@ -1,31 +1,6 @@
|
||||
Created By:
|
||||
Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Maintained by:
|
||||
Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Contributions by:
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
Santiago Lapresta <santiago.lapresta@gmail.com> - join, arrays, all, any, other filters
|
||||
Nicolas Williams <nico@cryptonector.com> - library-fication, autoconf stuff, various
|
||||
Jingwen Owen Ou <jingweno@gmail.com> - jqplay.org and link to it
|
||||
Andrew Rodland <andrew@cleverdomain.org> - bug fixes
|
||||
Ankur <ankz.kothari@gmail.com> - bug fixes
|
||||
Brendan Macmillan <melbourne.research@gmail.com> - bug fixes, load library from ~/.jq
|
||||
David R. MacIver <david@drmaciver.com> - bug fixes
|
||||
Filippo Giunchedi <fgiunchedi@gmail.com> - bug fixes
|
||||
Filippo Valsorda <filippo.valsorda@gmail.com> - recursive object merge (`*`)
|
||||
Jack Pearkes <jackpearkes@gmail.com> - update tutorial
|
||||
Joe Littlejohn <joe.littlejohn@nokia.com> - bug fixes
|
||||
Juan Guerrero <juan.guerrero.lozano@gmail.com> - bug fixes
|
||||
Kenny Shen <kenny.shen@zalora.com> - doc fixes
|
||||
Markus Lanthaler <mark_lanthaler@gmx.net> - doc fixes
|
||||
Michael Daines <michael@mdaines.com> - add sqrt; doc fixes
|
||||
Mike McCabe <mccabe@archive.org> - math (libm) functions
|
||||
Nicolas Pouillard <nicolas.pouillard@gmail.com> - add tests
|
||||
Peter van Dijk <peter@7bits.nl> - doc fixes
|
||||
Philipp Hagemeister <phihag@phihag.de> - doc fixes
|
||||
polyester <paul@cleanclothes.org> - automake version update
|
||||
Rémy Léone <remy.leone@gmail.com> - add .travis.yml
|
||||
Ryoichi KATO <ryo1kato@gmail.com> - doc fixes
|
||||
Contributions by
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
@@ -1,617 +0,0 @@
|
||||
2014-06-09 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add libm.h to dist file list
|
||||
|
||||
Add note about cmd.exe quoting
|
||||
|
||||
Building docs fails on powerpc (#349)
|
||||
|
||||
2014-06-08 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Update site news
|
||||
|
||||
Also fix configure.ac to use git describe --tags
|
||||
|
||||
Fix scripts/version: use git describe --tags ...
|
||||
After tagging as 1.4 scripts/version was still producing jq-1.3-....
|
||||
|
||||
Add `indices(s)`, improve `index(s)`, `rindex(s)`
|
||||
Now these deal with arrays as input and `s` being an array or a scalar.
|
||||
|
||||
Improve `index` and `rindex` examples
|
||||
|
||||
Remove reference to `getpath` from docs
|
||||
|
||||
Document `index` and `rindex` (#389)
|
||||
|
||||
2014-06-07 Santiago Lapresta <santiago.lapresta@gmail.com>
|
||||
|
||||
Added `join` function
|
||||
|
||||
2014-06-07 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
String * number should be commutative
|
||||
|
||||
2014-06-04 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add cross-compilation notes to README
|
||||
A detailed set of instruction as to how to setup a cross-compilation
|
||||
environment for OS X and Win32/64 would be nice.
|
||||
|
||||
Add -j option to scripts/crosscompile
|
||||
|
||||
Add flags argument to jv_parser_new()
|
||||
For extensibility. We might add streaming parser options, even binary
|
||||
JSON encoding options.
|
||||
|
||||
2014-06-02 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix tests failures on Windows
|
||||
And Solaris 8 and 9 too, no doubt. The problem was that non-standard
|
||||
vsnprintf()s that return -1 when the buffer is too small were not
|
||||
properly supported.
|
||||
|
||||
2014-05-20 Santiago Lapresta <santiago.lapresta@gmail.com>
|
||||
|
||||
Documented `del` command
|
||||
|
||||
2014-05-11 Santiago Lapresta <santiago.lapresta@gmail.com>
|
||||
|
||||
Added texts/examples to unique_by function
|
||||
|
||||
Added unique_by function
|
||||
|
||||
2014-04-17 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Make pthread tls configurable for Mingw build
|
||||
For the Mingw build we don't want to pull in the pthread DLL just
|
||||
because we can autodetect pthread support. That would make the jq.exe
|
||||
binary not self-contained.
|
||||
|
||||
2014-04-16 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add autoconf checks for pthreads; fix #340
|
||||
|
||||
2014-03-20 Jingwen Owen Ou <jingweno@gmail.com>
|
||||
|
||||
Add link to jqplay
|
||||
|
||||
2014-03-13 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix for #303 in the sources
|
||||
|
||||
2014-03-13 Santiago Lapresta <santiago.lapresta@gmail.com>
|
||||
|
||||
Added `arrays` and other filters
|
||||
Arrays, objects, numbers, strings, booleans, nulls, values (non-nulls)
|
||||
-- these builtins filter out those inputs that don't match the name of
|
||||
the builtin.
|
||||
|
||||
This fixes #322 and #324.
|
||||
|
||||
2014-03-07 Filippo Valsorda <filippo.valsorda@gmail.com>
|
||||
|
||||
Add a recursive object merge strategy and bind it to *
|
||||
This commit adds a jv_object_merge_recursive function, that performs
|
||||
recursive object merging, and binds it to multiply when applied to
|
||||
two objects.
|
||||
|
||||
Closes #320
|
||||
|
||||
2014-03-06 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Make libm tests more portable
|
||||
|
||||
2014-02-26 Andrew Rodland <andrew@cleverdomain.org>
|
||||
|
||||
Repair jv_show
|
||||
|
||||
2014-02-26 Andrew Rodland <andrew@cleverdomain.org>
|
||||
|
||||
Make jq --raw-output --unbuffered work
|
||||
--unbuffered was only affecting the normal output case, not the --raw-output case. Make the two of them play together.
|
||||
|
||||
This also makes sure that the output is flushed *after* printing the newline, so a consumer doesn't lag a line behind.
|
||||
|
||||
2014-02-21 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add cbrt (cube root)
|
||||
|
||||
Add missing trig functions and barebones test
|
||||
|
||||
Remove non-standard exp10()
|
||||
|
||||
2014-02-21 Mike McCabe <mccabe@archive.org>
|
||||
|
||||
Initial add of math functions.
|
||||
|
||||
2014-02-20 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add `?`, `.[]?`, and `..` operators
|
||||
Make XPath-like `//a/b` recursive structure traversal easier in jq,
|
||||
which then becomes:
|
||||
|
||||
..|.a?.b?
|
||||
|
||||
The `?` operator suppresses errors about . not being an array or object.
|
||||
The `..` operator is equivalent to calling the new `recurse_down`
|
||||
built-in, which in turn is equivalent to
|
||||
|
||||
recurse(.[]?)
|
||||
|
||||
Note that `..a` is not supported; neither is `...a`. That could be add
|
||||
added, but it doesn't seem worth the trouble of saving the need to type
|
||||
a '|'.
|
||||
|
||||
2014-02-16 Santiago Lapresta <santiago.lapresta@gmail.com>
|
||||
|
||||
Added `all` and `any` builtins
|
||||
|
||||
2014-01-25 polyester <paul@cleanclothes.org>
|
||||
|
||||
work with newer versions of automake
|
||||
when using a newer automake, the autoreconf step fails with warnings:
|
||||
"linking libtool libraries using a non-POSIX archiver requires 'AM_PROG_AR' in 'configure.ac' "
|
||||
|
||||
This happens for instance on ubuntu 13.10.
|
||||
Doing just that, adding 'AM_PROG_AR' to configure.ac fixes the problem.
|
||||
|
||||
2014-01-01 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix #201; check that bison accepts --warnings
|
||||
|
||||
2013-12-27 Joe Littlejohn <joe.littlejohn@nokia.com>
|
||||
|
||||
Fix rpm build (`make rpm`)
|
||||
* Re-add VERSION as it's required for `./setup superclean`
|
||||
and `make rpm`.
|
||||
* Add *.rpm to git ignore, we never want them under version control.
|
||||
|
||||
2013-12-27 Filippo Giunchedi <fgiunchedi@gmail.com>
|
||||
|
||||
include additional files in jq.spec
|
||||
this will probably need changing upon SONAME bump
|
||||
|
||||
fix rpm Makefile target and prerequisites
|
||||
depend on dist and the specfile, plus use automake's variables
|
||||
|
||||
2013-12-26 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Document --version
|
||||
|
||||
2013-12-26 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add jv_dumpf() and jv_show()
|
||||
jv_dumpf() takes a FILE *.
|
||||
|
||||
jv_show() is intended for use in debuggers, so it dumps the jv to stderr
|
||||
and it does not jv_free() the jv, so it's safe to
|
||||
"call jv_show(some_jv, -1)" in a debugger. If flags == -1 then the jv
|
||||
will be shown pretty-printed and in color.
|
||||
|
||||
2013-12-26 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Document .foo.bar in manual
|
||||
|
||||
Document exit numbers
|
||||
|
||||
Normalize errors for -e
|
||||
|
||||
2013-12-25 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix doc typos (.[foo] wanted to be .["foo"])
|
||||
|
||||
Add note to jq.1 about shell quoting
|
||||
|
||||
2013-12-20 Philipp Hagemeister <phihag@phihag.de>
|
||||
|
||||
Ignore the config/test-driver file
|
||||
This file is automatically generated and does not need to be committed.
|
||||
|
||||
Fix @uri example
|
||||
Previously, the @uri example didn't match the actual behavior of the current jq, as exclamation marks do not need to be encoded in URIs.
|
||||
Replace the example with an input that needs encoding, and is encoded by jq.
|
||||
|
||||
2013-12-17 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Allow negated object values without parens. Fixes #247
|
||||
|
||||
2013-12-17 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix memmem() error
|
||||
|
||||
2013-12-13 Rémy Léone <remy.leone@gmail.com>
|
||||
|
||||
Adding a .travis.yml file to use the travis-ci.org
|
||||
From wikipedia:
|
||||
|
||||
Travis CI is a hosted, distributed continuous integration service used
|
||||
to build and test projects hosted at GitHub.
|
||||
|
||||
Travis CI is configured by adding a file named .travis.yml, which is a
|
||||
YAML format text file, to the root directory of the GitHub repository.
|
||||
|
||||
Travis CI automatically detects when a commit has been made and pushed
|
||||
to a GitHub repository that is using Travis CI, and each time this
|
||||
happens, it will try to build the project and run tests. This includes
|
||||
commits to all branches, not just to the master branch. When that
|
||||
process has completed, it will notify a developer in the way it has been
|
||||
configured to do so — for example, by sending an email containing the
|
||||
test results (showing success or failure), or by posting a message on an
|
||||
IRC channel. It can be configured to run the tests on a range of
|
||||
different machines, with different software installed (such as older
|
||||
versions of a programming language, to test for compatibility).
|
||||
|
||||
2013-12-13 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Make the testsuite run on machines without valgrind
|
||||
|
||||
Format more integers as integers, not scientific notation.
|
||||
jq is now willing to put up to 15 zeros after an integer before
|
||||
moving to scientific notation.
|
||||
|
||||
2013-12-11 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Complete more-arity feature not complete
|
||||
And test
|
||||
|
||||
2013-12-10 David R. MacIver <david@drmaciver.com>
|
||||
|
||||
convert range bounds to integers in a way that avoids undefined behaviour
|
||||
|
||||
add checking of numeric indices to an array to see if they can reasonably be considered integers. Avoid undefined behaviour if out of bounds
|
||||
|
||||
2013-12-09 David R. MacIver <david@drmaciver.com>
|
||||
|
||||
some functions were missing prototypes. Add them
|
||||
|
||||
2013-12-08 David R. MacIver <david@drmaciver.com>
|
||||
|
||||
These vfprintfs are being used as if they were printfs. Fix that
|
||||
|
||||
consistent use of goto out in main
|
||||
|
||||
2013-12-08 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Refactor jv structure.
|
||||
New structure layout is simpler and also faster. In particular, it's
|
||||
now small enough to be passed in registers on amd64.
|
||||
|
||||
Make testsuite not leak when compiled with -DNDEBUG.
|
||||
|
||||
2013-12-08 David R. MacIver <david@drmaciver.com>
|
||||
|
||||
test for losing memory on compile errors
|
||||
|
||||
args to jq_compile_args were not getting freed when there were errors in the compile
|
||||
|
||||
2013-12-06 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix double-free typo in print_error()
|
||||
|
||||
Fix manual.yml
|
||||
|
||||
2013-12-04 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Conditionally #define _GNU_SOURCE in compile.c
|
||||
|
||||
Add tests for string index by string and builtins
|
||||
|
||||
Add index and rindex builtins
|
||||
|
||||
Add index strings by string; return string indexes
|
||||
% jq '.[","]'
|
||||
"a,bc,def,ghij,klmno"
|
||||
[1,4,8,13]
|
||||
%
|
||||
|
||||
Make length return abs value of numeric inputs
|
||||
|
||||
Add callback interface for errors
|
||||
Printing to stderr is not the right answer for a library.
|
||||
|
||||
Add jv_string_vfmt()
|
||||
|
||||
Document ltrimstr and rtrimstr
|
||||
|
||||
Test ltrimstr and rtrimstr functions
|
||||
|
||||
Add ltrimstr and rtrimstr functions
|
||||
|
||||
Document -e / --exit-status argument
|
||||
|
||||
Add -e | --exit-status CLI option
|
||||
|
||||
Document tojson and fromjson builtins
|
||||
|
||||
Test tojson and fromjson
|
||||
|
||||
Add tojson and fromjson builtins
|
||||
|
||||
Document split function
|
||||
|
||||
Document string multiplication and division
|
||||
|
||||
Document string functions and slicing
|
||||
|
||||
Test string slicing
|
||||
|
||||
Add string slicing
|
||||
|
||||
Add tests for string division/splitting
|
||||
|
||||
Add string division by string (split on separator)
|
||||
|
||||
Test starts/endswith and string multiplication
|
||||
|
||||
Add string multiplication by number
|
||||
|
||||
Add startswith/endswith
|
||||
|
||||
Add explode/implode jq functions to match jv API
|
||||
|
||||
Use uint32_t for codepoint in jv_string_append_codepoint()
|
||||
|
||||
Add jv string utility functions
|
||||
jv_string_empty()
|
||||
-> return an empty string with given allocated length (for fast
|
||||
appends)
|
||||
jv_string_append_codepoint
|
||||
-> append a single codepoint (int) to the given string
|
||||
jv_string_explode
|
||||
-> return an array of codepoints making up a string
|
||||
jv_string_implode
|
||||
-> return the UTF-8 encoding of an array of codepoint numbers
|
||||
|
||||
Support more arguments for defs
|
||||
|
||||
2013-12-04 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Preserve insertion order in objects. Closes #169.
|
||||
|
||||
2013-11-30 Nicolas Pouillard <nicolas.pouillard@gmail.com>
|
||||
|
||||
Add a few more test cases (from the man page)
|
||||
|
||||
2013-11-08 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Add a --unbuffered option. Closes #206
|
||||
|
||||
2013-11-07 Peter van Dijk <peter@7bits.nl>
|
||||
|
||||
count should be length
|
||||
Example refers to a count function, which does not exist. Replacing it with length works.
|
||||
|
||||
2013-11-07 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Fix a crash on group_by of empty list. Fixes #208.
|
||||
|
||||
2013-10-16 Ryoichi KATO <ryo1kato@gmail.com>
|
||||
|
||||
Docs: add description of --from-file option
|
||||
|
||||
2013-10-06 Juan Guerrero <juan.guerrero.lozano@gmail.com>
|
||||
|
||||
Fix typo on error message
|
||||
|
||||
2013-09-19 Kenny Shen <kenny.shen@zalora.com>
|
||||
|
||||
Add missing -i flag in build instructions
|
||||
|
||||
2013-09-14 Michael Daines <michael@mdaines.com>
|
||||
|
||||
Add test showing calculation of standard deviation
|
||||
|
||||
2013-09-13 Mike Daines <michael@mdaines.com>
|
||||
|
||||
Fix typo
|
||||
|
||||
2013-09-11 Michael Daines <michael@mdaines.com>
|
||||
|
||||
Add sqrt operator
|
||||
|
||||
2013-09-04 Jack Pearkes <jackpearkes@gmail.com>
|
||||
|
||||
docs: update the tutorial to use GitHub's API
|
||||
|
||||
2013-09-01 Ankur <ankz.kothari@gmail.com>
|
||||
|
||||
Call AM_INIT_AUTOMAKE once only
|
||||
Fixes build with automake-1.14
|
||||
|
||||
2013-08-19 Joe Littlejohn <joe.littlejohn@nokia.com>
|
||||
|
||||
Fix Makefile after refactoring of stacks in 05d90517b02
|
||||
|
||||
2013-06-23 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Remove #includes from jv.h
|
||||
|
||||
Fix the jv_parser interface.
|
||||
|
||||
Use libtool's built-in symbol exporting rather than a mapfile.
|
||||
|
||||
Move gen_utf8_tables to scripts
|
||||
|
||||
Move libtool m4 junk to config/ and delete some autogenerated files.
|
||||
|
||||
Remove Autoconf-generated config.h.
|
||||
|
||||
2013-06-22 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Build libjq only once, and link it statically to ./jq
|
||||
This means ./jq is a real binary rather than a libtool turd.
|
||||
|
||||
Fix distcheck.
|
||||
Update list of files to be distributed.
|
||||
|
||||
Utf8 fixes. Closes #161
|
||||
|
||||
Reject all overlong UTF8 sequences.
|
||||
|
||||
Fix various UTF8 parsing bugs.
|
||||
In particular, parse bad UTF8 by replacing the broken bits with U+FFFD
|
||||
and resychronise correctly after broken sequences.
|
||||
|
||||
Fix example in manual for `floor`. See #155.
|
||||
|
||||
2013-06-21 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Document floor
|
||||
|
||||
Add floor operator
|
||||
|
||||
Document mod
|
||||
|
||||
Add mod (and setmod) operators
|
||||
|
||||
Update .gitignore
|
||||
|
||||
Add libjq autoconf goo
|
||||
|
||||
Quiet setup.sh re: tmp dir
|
||||
|
||||
2013-06-21 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Move cfunction invocation code to the interpreter loop.
|
||||
|
||||
2013-06-18 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Fix serious bug in handling of --argfile
|
||||
|
||||
Fix leaks in jv_load_file()
|
||||
|
||||
2013-06-17 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Fold opcode.{c,h} into bytecode.{c,h}
|
||||
|
||||
Simplify block functions for variables
|
||||
|
||||
Saner build instructions in README.md
|
||||
Closes #144
|
||||
|
||||
Remove some initialise-to-zero code.
|
||||
This lets valgrind find more bugs - if a field isn't given a
|
||||
well-defined value valgrind will now find it instead of seeing it
|
||||
set to zero with memset.
|
||||
|
||||
2013-06-17 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Remove accidentally introduced use of fopen "e"
|
||||
|
||||
2013-06-16 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Merge pull request #114 from nicowilliams/nomem_handler
|
||||
Add jv_nomem_handler()
|
||||
|
||||
2013-06-16 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Remove last remnant of main.h
|
||||
|
||||
2013-06-15 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Allow --run-tests to take a file argument
|
||||
|
||||
Fixup API to get closer to a libjq
|
||||
|
||||
2013-06-15 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Move slurp_file() into library as jv_load_file()
|
||||
Needed as part of creating a libjq.
|
||||
|
||||
2013-06-14 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Clean up lots of stack and frame logic.
|
||||
Move frame defs to execute.c
|
||||
|
||||
2013-06-13 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Simplify frame logic.
|
||||
|
||||
Unify all stacks. Passes tests, but needs cleanup.
|
||||
|
||||
2013-06-11 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Support ."foo" syntax for accessing fields. See #141.
|
||||
|
||||
2013-06-09 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Unify frame and data stacks
|
||||
|
||||
2013-06-05 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Speed up cached configure (./configure -C)
|
||||
|
||||
Clean up flex lines in build
|
||||
|
||||
Lex and parse .foo better.
|
||||
'.as' is now valid, '. foo' is now invalid. See #141.
|
||||
|
||||
2013-06-04 Markus Lanthaler <mark_lanthaler@gmx.net>
|
||||
|
||||
Update README.md
|
||||
Update the link to the documentation. All GitHub pages are now using the github.io domain.
|
||||
|
||||
2013-06-03 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Make jq --version print to stdout, not stderr
|
||||
|
||||
Better error handling for .foo case in parser. See #141.
|
||||
|
||||
Let the parser rather than the lexer handle invalid characters.
|
||||
|
||||
Add command-line option to sort object keys.
|
||||
Closes #79.
|
||||
|
||||
Clean up Makefile.am (distcheck, rebuild version.h less often)
|
||||
|
||||
2013-05-31 Brendan Macmillan <melbourne.research@gmail.com>
|
||||
|
||||
Stop warning on fgets, simple version
|
||||
|
||||
Stop warning on fgets, complex version
|
||||
|
||||
2013-05-31 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Squash a warning on some GCC versions
|
||||
|
||||
2013-05-29 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Support for printing object keys in sorted order.
|
||||
No command-line option to enable this yet. See #79.
|
||||
|
||||
2013-05-29 Brendan Macmillan <melbourne.research@gmail.com>
|
||||
|
||||
Bugfix multiline off-by-one (locfile.c)
|
||||
|
||||
locfile.h -> locfile.h + locfile.c
|
||||
clean up includes of a few files
|
||||
|
||||
Hack bugfix for multiline off-by-one (locfile.c)
|
||||
|
||||
Load library from ~/.jq
|
||||
|
||||
2013-05-24 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Make jq --version report an actual git revision.
|
||||
Closes #129.
|
||||
|
||||
2013-05-23 Nicolas Williams <nico@cryptonector.com>
|
||||
|
||||
Add --argfile variant of --arg (issue #117)
|
||||
This is useful when one has a database (in JSON form) to query using jq
|
||||
input data.
|
||||
|
||||
% echo '{"a":1, "c":5}' > db.json
|
||||
% echo '"c"'|./jq --argfile f /tmp/a '$f[.]'
|
||||
5
|
||||
% echo '"a"'|./jq --argfile f /tmp/a '$f[.]'
|
||||
1
|
||||
% echo '"b"'|./jq --argfile f /tmp/a '$f[.]'
|
||||
null
|
||||
%
|
||||
|
||||
2013-05-23 Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
'make clean' won't delete jq.1 if it can't be rebuilt.
|
||||
See #131
|
||||
+28
-52
@@ -1,12 +1,13 @@
|
||||
### C source files to be built and distributed.
|
||||
|
||||
LIBJQ_INCS = jq_parser.h builtin.h bytecode.h compile.h exec_stack.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
|
||||
JQ_INCS = jq_parser.h builtin.h bytecode.h compile.h execute.h \
|
||||
forkable_stack.h frame_layout.h jv.h jv_alloc.h jv_aux.h jv_dtoa.h \
|
||||
jv_parse.h jv_unicode.h locfile.h opcode.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 \
|
||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_file.c \
|
||||
jv_alloc.c jq_test.c ${LIBJQ_INCS}
|
||||
JQ_SRC = opcode.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_alloc.c \
|
||||
jq_test.c ${JQ_INCS}
|
||||
|
||||
|
||||
### C build options
|
||||
@@ -14,15 +15,14 @@ LIBJQ_SRC = locfile.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
|
||||
-Wno-unused-parameter -Wno-unused-function
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config/m4
|
||||
|
||||
### Generating the lexer and parser
|
||||
|
||||
# While there is some autoconf macro support for lex/flex, it doesn't support
|
||||
# header file creation so we'll use good old make
|
||||
BUILT_SOURCES = lexer.h lexer.c parser.h parser.c version.h
|
||||
BUILT_SOURCES = lexer.h lexer.c parser.h parser.c
|
||||
lexer.c: lexer.l
|
||||
$(AM_V_LEX) flex -o lexer.c --header-file=lexer.h $<
|
||||
flex -o lexer.c --header-file=lexer.h $<
|
||||
lexer.h: lexer.c
|
||||
|
||||
# Tell YACC (bison) autoconf macros that you want a header file created.
|
||||
@@ -30,31 +30,14 @@ lexer.h: lexer.c
|
||||
# OSX ships an old bison, so update with homebrew or macports
|
||||
AM_YFLAGS = --warnings=all -d
|
||||
|
||||
### libjq
|
||||
|
||||
lib_LTLIBRARIES = libjq.la
|
||||
libjq_la_SOURCES = ${LIBJQ_SRC}
|
||||
libjq_la_LIBADD = -lm
|
||||
libjq_la_LDFLAGS = -export-symbols-regex '^j[qv]_' -version-info 1:4:0
|
||||
|
||||
include_HEADERS = jv.h jq.h
|
||||
|
||||
|
||||
### Building the jq binary
|
||||
|
||||
# Remake the version.h header file if, and only if, the git ID has changed
|
||||
.PHONY: .FORCE
|
||||
.FORCE:
|
||||
generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed 's/.*/\#define JQ_VERSION \"&\"/'`"
|
||||
.remake-version-h: .FORCE
|
||||
@ $(generate_ver); test "x`cat version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h
|
||||
version.h: .remake-version-h
|
||||
$(AM_V_GEN) $(generate_ver); echo "$$ver" > $@
|
||||
main.c: config.h
|
||||
|
||||
bin_PROGRAMS = jq
|
||||
jq_SOURCES = main.c
|
||||
jq_LDFLAGS = -static-libtool-libs
|
||||
jq_LDADD = libjq.la -lm
|
||||
jq_SOURCES = ${JQ_SRC} main.c
|
||||
|
||||
|
||||
### Tests (make check)
|
||||
|
||||
@@ -73,23 +56,20 @@ man_MANS = jq.1
|
||||
.PHONY: real_docs
|
||||
if ENABLE_DOCS
|
||||
jq.1: $(srcdir)/docs/content/3.manual/manual.yml
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
|
||||
jq.1.prebuilt: jq.1
|
||||
$(AM_V_GEN) cp $^ $@ || { rm -f $@; false; }
|
||||
( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
|
||||
jq.1.default: $(srcdir)/docs/default_manpage.md
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage_default ) > $@ || { rm -f $@; false; }
|
||||
real_docs: jq.1 jq.1.prebuilt
|
||||
@if cmp jq.1 $(srcdir)/jq.1.default > /dev/null; then\
|
||||
( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage_default ) > $@ || { rm -f $@; false; }
|
||||
real_docs: jq.1
|
||||
if cmp jq.1 $(srcdir)/jq.1.default > /dev/null; then\
|
||||
rm -f jq.1; $(MAKE) $(AM_MAKEFLAGS) jq.1;\
|
||||
fi
|
||||
else
|
||||
jq.1: $(srcdir)/jq.1.prebuilt
|
||||
$(AM_V_GEN) cp $^ $@
|
||||
jq.1: $(srcdir)/jq.1.default
|
||||
cp $^ $@
|
||||
real_docs:
|
||||
@echo "Ruby dependencies not found, cannot build manpage." > /dev/stderr
|
||||
@echo "Follow the instructions in docs/README.md to install them" > /dev/stderr
|
||||
@echo "and then rerun ./configure" > /dev/stderr
|
||||
@echo "A pre-built, possibly out-of-date manpage used." > /dev/stderr
|
||||
false
|
||||
endif
|
||||
|
||||
@@ -113,21 +93,24 @@ 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) jq.spec \
|
||||
$(DOC_FILES) scripts/version parser.h parser.c lexer.h \
|
||||
lexer.c
|
||||
EXTRA_DIST = config.h.in $(BUILT_SOURCES) $(man_MANS) $(TESTS) \
|
||||
$(TEST_LOG_COMPILER) gen_utf8_tables.py jq.spec \
|
||||
$(DOC_FILES)
|
||||
|
||||
# README.md is expected in Github projects, good stuff in it, so we'll
|
||||
# distribute it and install it with the package in the doc directory.
|
||||
docdir = ${datadir}/doc/${PACKAGE}
|
||||
dist_doc_DATA = README.md COPYING AUTHORS README
|
||||
|
||||
releasetag:
|
||||
git tag -s "jq-$$(cat VERSION)" -m "jq release $$(cat VERSION)"
|
||||
|
||||
RELEASE ?= 1
|
||||
rpm: dist jq.spec
|
||||
rpm: jq
|
||||
@echo "Packaging jq as an RPM ..."
|
||||
mkdir -p rpm/SOURCES rpm/BUILD rpm/BUILDROOT rpm/RPMS rpm/SPECS
|
||||
cp jq-$(VERSION).tar.gz rpm/SOURCES/
|
||||
rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $(VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$(VERSION).tar.gz
|
||||
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
|
||||
find rpm/RPMS/ -name "*.rpm" -exec mv {} ./ \;
|
||||
rm -rf rpm
|
||||
|
||||
@@ -136,12 +119,5 @@ dist-clean-local:
|
||||
|
||||
# Not sure why this doesn't get cleaned up automatically, guess
|
||||
# automake used to man pages which are hand coded?
|
||||
# 'make clean' doesn't delete the manpage if it can't be rebuilt
|
||||
.PHONY: clean-local-docs
|
||||
clean-local-docs:
|
||||
if ENABLE_DOCS
|
||||
clean-local:
|
||||
rm -f jq.1
|
||||
endif
|
||||
|
||||
clean-local: clean-local-docs
|
||||
rm -f version.h .remake-version-h
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
Release history
|
||||
|
||||
* jq version 1.0 was released on Sun Oct 21 2012
|
||||
* jq version 1.1 was released on Sun Oct 21 2012
|
||||
* jq version 1.2 was released on Thu Dec 20 2012
|
||||
* jq version 1.3 was released on Sun May 19 2013
|
||||
* jq version 1.4 was released on Mon Jun 9 2014
|
||||
|
||||
As yet unreleased features in master branch, since 1.4:
|
||||
|
||||
- semantics
|
||||
|
||||
- multiple function argument numbers for any one function name,
|
||||
dispatching on number of arguments in function calls
|
||||
|
||||
- misc
|
||||
|
||||
- env builting (to access the environment)
|
||||
- prebuilt jq.1 man page
|
||||
|
||||
New features in 1.4 since 1.3:
|
||||
|
||||
- command-line arguments
|
||||
|
||||
- jq --arg-file variable file
|
||||
- jq --unbuffered
|
||||
- jq -e / --exit-status (set exit status based on outputs)
|
||||
- jq -S / --sort-keys (now jq no longer sorts object keys by
|
||||
default
|
||||
|
||||
- syntax
|
||||
|
||||
- .. -> like // in XPath (recursive traversal)
|
||||
- question mark (e.g., .a?) to suppress errors
|
||||
- ."foo" syntax (equivalent to .["foo"])
|
||||
- better error handling for .foo
|
||||
- added % operator (modulo)
|
||||
- allow negation without requiring extra parenthesis
|
||||
- more function arguments (up to six)
|
||||
|
||||
- filters:
|
||||
|
||||
- any, all
|
||||
- iterables, arrays, objects, scalars, nulls, booleans, numbers,
|
||||
strings, values
|
||||
|
||||
- string built-ins:
|
||||
|
||||
- split
|
||||
- join (join an array of strings with a given separator string)
|
||||
- ltrimstr, rtrimstr
|
||||
- startswith, endswith
|
||||
- explode, implode
|
||||
- fromjson, tojson
|
||||
- index, rindex, indices
|
||||
|
||||
- math functions
|
||||
|
||||
- floor, sqrt, cbrt, etetera (depends on what's available from libm)
|
||||
|
||||
- libjq -- a C API interface to jq's JSON representation and for
|
||||
running jq programs from C applications
|
||||
|
||||
@@ -6,30 +6,18 @@ 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 [jqplay.org](http://jqplay.org).
|
||||
|
||||
If you want to hack on jq, feel free, but be warned that its internals
|
||||
are not well-documented at the moment. Bring a hard hat and a
|
||||
shovel. Also, read the wiki: http://github.com/stedolan/jq/wiki
|
||||
shovel. Also, read the wiki: https://github.com/stedolan/jq/wiki
|
||||
|
||||
If you're building directly from the latest git, you'll need flex and
|
||||
bison installed. To build, run:
|
||||
To build jq, run
|
||||
|
||||
autoreconf -i
|
||||
./configure
|
||||
make -j8
|
||||
make check
|
||||
make
|
||||
sudo make install (optionally)
|
||||
|
||||
After make finishes, you'll be able to use `./jq`. You can also
|
||||
install it using:
|
||||
|
||||
sudo make install
|
||||
|
||||
If you're not using the latest git version but instead building a
|
||||
released tarball (available on the website), then you won't need to
|
||||
run `autoreconf` (and shouldn't), and you won't need flex or bison.
|
||||
|
||||
To cross-compile for OS X and Windows, see docs/Rakefile's build task
|
||||
and scripts/crosscompile. You'll need a cross-compilation environment,
|
||||
such as Mingw for cross-compiling for Windows.
|
||||
If you've just checked out the latest version from git (rather than
|
||||
using a released source tarball) then you'll need to run this first:
|
||||
|
||||
autoreconf
|
||||
@@ -1,17 +1,29 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <oniguruma.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "builtin.h"
|
||||
#include "compile.h"
|
||||
#include "jq_parser.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
#include "jv_aux.h"
|
||||
#include "jv_unicode.h"
|
||||
|
||||
|
||||
|
||||
typedef jv (*func_1)(jv);
|
||||
typedef jv (*func_2)(jv,jv);
|
||||
typedef jv (*func_3)(jv,jv,jv);
|
||||
typedef jv (*func_4)(jv,jv,jv,jv);
|
||||
typedef jv (*func_5)(jv,jv,jv,jv,jv);
|
||||
jv cfunction_invoke(struct cfunction* function, jv input[]) {
|
||||
switch (function->nargs) {
|
||||
case 1: return ((func_1)function->fptr)(input[0]);
|
||||
case 2: return ((func_2)function->fptr)(input[0], input[1]);
|
||||
case 3: return ((func_3)function->fptr)(input[0], input[1], input[2]);
|
||||
case 4: return ((func_4)function->fptr)(input[0], input[1], input[2], input[3]);
|
||||
case 5: return ((func_5)function->fptr)(input[0], input[1], input[2], input[3], input[4]);
|
||||
default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));
|
||||
}
|
||||
}
|
||||
|
||||
static jv type_error(jv bad, const char* msg) {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s %s",
|
||||
jv_kind_name(jv_get_kind(bad)),
|
||||
@@ -52,18 +64,6 @@ 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");
|
||||
@@ -73,64 +73,6 @@ 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) {
|
||||
@@ -158,34 +100,9 @@ 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 (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) {
|
||||
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 ((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");
|
||||
}
|
||||
@@ -195,17 +112,6 @@ 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");
|
||||
}
|
||||
@@ -261,19 +167,6 @@ 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;
|
||||
@@ -295,8 +188,6 @@ 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);
|
||||
@@ -413,7 +304,7 @@ static jv f_format(jv input, jv fmt) {
|
||||
if (ch < 128 && unreserved[ch]) {
|
||||
line = jv_string_append_buf(line, s, 1);
|
||||
} else {
|
||||
line = jv_string_concat(line, jv_string_fmt("%%%02X", ch));
|
||||
line = jv_string_concat(line, jv_string_fmt("%%%02x", ch));
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@@ -454,7 +345,7 @@ static jv f_format(jv input, jv fmt) {
|
||||
input = f_tostring(input);
|
||||
jv line = jv_string("");
|
||||
const char b64[64 + 1] = CHARS_ALPHANUM "+/";
|
||||
const unsigned char* data = (const unsigned char*)jv_string_value(input);
|
||||
const char* data = jv_string_value(input);
|
||||
int len = jv_string_length_bytes(jv_copy(input));
|
||||
for (int i=0; i<len; i+=3) {
|
||||
uint32_t code = 0;
|
||||
@@ -515,214 +406,6 @@ static jv f_group_by_impl(jv input, jv keys) {
|
||||
}
|
||||
}
|
||||
|
||||
static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups,
|
||||
int *groups, regex_t *reg, void *arg) {
|
||||
jv captures = *(jv*)arg;
|
||||
for (int i = 0; i < ngroups; ++i) {
|
||||
jv cap = jv_array_get(jv_copy(captures),groups[i]-1);
|
||||
if (jv_get_kind(cap) == JV_KIND_OBJECT) {
|
||||
cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name));
|
||||
captures = jv_array_set(captures,groups[i]-1,cap);
|
||||
} else {
|
||||
jv_free(cap);
|
||||
}
|
||||
}
|
||||
*(jv *)arg = captures;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static jv f_match(jv input, jv regex, jv modifiers, jv testmode) {
|
||||
int test = jv_equal(testmode, jv_true());
|
||||
jv result;
|
||||
int onigret;
|
||||
int global = 0;
|
||||
regex_t *reg;
|
||||
OnigErrorInfo einfo;
|
||||
OnigRegion* region;
|
||||
|
||||
if (jv_get_kind(input) != JV_KIND_STRING) {
|
||||
jv_free(regex);
|
||||
jv_free(modifiers);
|
||||
return type_error(input, "cannot be matched, as it is not a string");
|
||||
}
|
||||
|
||||
if (jv_get_kind(regex) != JV_KIND_STRING) {
|
||||
jv_free(input);
|
||||
jv_free(modifiers);
|
||||
return type_error(regex, "is not a string");
|
||||
}
|
||||
|
||||
OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP;
|
||||
|
||||
if (jv_get_kind(modifiers) == JV_KIND_STRING) {
|
||||
jv modarray = jv_string_explode(jv_copy(modifiers));
|
||||
jv_array_foreach(modarray, i, mod) {
|
||||
switch ((int)jv_number_value(mod)) {
|
||||
case 'g':
|
||||
global = 1;
|
||||
break;
|
||||
case 'i':
|
||||
options |= ONIG_OPTION_IGNORECASE;
|
||||
break;
|
||||
case 'x':
|
||||
options |= ONIG_OPTION_EXTEND;
|
||||
break;
|
||||
case 'm':
|
||||
options |= ONIG_OPTION_MULTILINE;
|
||||
break;
|
||||
case 's':
|
||||
options |= ONIG_OPTION_SINGLELINE;
|
||||
break;
|
||||
case 'p':
|
||||
options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE;
|
||||
break;
|
||||
case 'l':
|
||||
options |= ONIG_OPTION_FIND_LONGEST;
|
||||
break;
|
||||
case 'n':
|
||||
options |= ONIG_OPTION_FIND_NOT_EMPTY;
|
||||
break;
|
||||
default:
|
||||
jv_free(input);
|
||||
jv_free(regex);
|
||||
jv_free(modarray);
|
||||
return jv_invalid_with_msg(jv_string_concat(modifiers,
|
||||
jv_string(" is not a valid modifier string")));
|
||||
}
|
||||
}
|
||||
jv_free(modarray);
|
||||
} else if (jv_get_kind(modifiers) != JV_KIND_NULL) {
|
||||
// If it isn't a string or null, then it is the wrong type...
|
||||
jv_free(input);
|
||||
jv_free(regex);
|
||||
return type_error(modifiers, "is not a string");
|
||||
}
|
||||
|
||||
jv_free(modifiers);
|
||||
|
||||
onigret = onig_new(®, (const UChar*)jv_string_value(regex),
|
||||
(const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))),
|
||||
options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo);
|
||||
if (onigret != ONIG_NORMAL) {
|
||||
UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];
|
||||
onig_error_code_to_str(ebuf, onigret, einfo);
|
||||
jv_free(input);
|
||||
jv_free(regex);
|
||||
return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),
|
||||
jv_string((char*)ebuf)));
|
||||
}
|
||||
if (!test)
|
||||
result = jv_array();
|
||||
const char *input_string = jv_string_value(input);
|
||||
const UChar* start = (const UChar*)jv_string_value(input);
|
||||
const unsigned long length = jv_string_length_bytes(jv_copy(input));
|
||||
const UChar* end = start + length;
|
||||
region = onig_region_new();
|
||||
do {
|
||||
onigret = onig_search(reg,
|
||||
(const UChar*)jv_string_value(input), end, /* string boundaries */
|
||||
start, end, /* search boundaries */
|
||||
region, ONIG_OPTION_NONE);
|
||||
if (onigret >= 0) {
|
||||
if (test) {
|
||||
result = jv_true();
|
||||
break;
|
||||
}
|
||||
|
||||
// Zero-width match
|
||||
if (region->end[0] == region->beg[0]) {
|
||||
unsigned long idx;
|
||||
const char *fr = (const char*)input_string;
|
||||
for (idx = 0; fr != input_string+region->beg[0]; idx++) {
|
||||
fr += jvp_utf8_decode_length(*fr);
|
||||
}
|
||||
jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
||||
match = jv_object_set(match, jv_string("length"), jv_number(0));
|
||||
match = jv_object_set(match, jv_string("string"), jv_string(""));
|
||||
match = jv_object_set(match, jv_string("captures"), jv_array());
|
||||
result = jv_array_append(result, match);
|
||||
start += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long idx;
|
||||
unsigned long len;
|
||||
const char *fr = (const char*)input_string;
|
||||
|
||||
for (idx = len = 0; fr != input_string+region->end[0]; len++) {
|
||||
if (fr == input_string+region->beg[0]) idx = len, len=0;
|
||||
fr += jvp_utf8_decode_length(*fr);
|
||||
}
|
||||
|
||||
jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
||||
|
||||
unsigned long blen = region->end[0]-region->beg[0];
|
||||
match = jv_object_set(match, jv_string("length"), jv_number(len));
|
||||
match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen));
|
||||
jv captures = jv_array();
|
||||
for (int i = 1; i < region->num_regs; ++i) {
|
||||
// Empty capture.
|
||||
if (region->beg[i] == region->end[i]) {
|
||||
// Didn't match.
|
||||
jv cap;
|
||||
if (region->beg[i] == -1) {
|
||||
cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1));
|
||||
cap = jv_object_set(cap, jv_string("string"), jv_null());
|
||||
} else {
|
||||
fr = input_string;
|
||||
for (idx = 0; fr != input_string+region->beg[i]; idx++) {
|
||||
fr += jvp_utf8_decode_length(*fr);
|
||||
}
|
||||
cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
||||
cap = jv_object_set(cap, jv_string("string"), jv_string(""));
|
||||
}
|
||||
cap = jv_object_set(cap, jv_string("length"), jv_number(0));
|
||||
cap = jv_object_set(cap, jv_string("name"), jv_null());
|
||||
captures = jv_array_append(captures, cap);
|
||||
continue;
|
||||
}
|
||||
fr = input_string;
|
||||
for (idx = len = 0; fr != input_string+region->end[i]; len++) {
|
||||
if (fr == input_string+region->beg[i]) idx = len, len=0;
|
||||
fr += jvp_utf8_decode_length(*fr);
|
||||
}
|
||||
|
||||
blen = region->end[i]-region->beg[i];
|
||||
jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
|
||||
cap = jv_object_set(cap, jv_string("length"), jv_number(len));
|
||||
cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen));
|
||||
cap = jv_object_set(cap, jv_string("name"), jv_null());
|
||||
captures = jv_array_append(captures,cap);
|
||||
}
|
||||
onig_foreach_name(reg,f_match_name_iter,&captures);
|
||||
match = jv_object_set(match, jv_string("captures"), captures);
|
||||
result = jv_array_append(result, match);
|
||||
start = (const UChar*)(input_string+region->end[0]);
|
||||
onig_region_free(region,0);
|
||||
} else if (onigret == ONIG_MISMATCH) {
|
||||
if (test)
|
||||
result = jv_false();
|
||||
break;
|
||||
} else { /* Error */
|
||||
UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];
|
||||
onig_error_code_to_str(ebuf, onigret, einfo);
|
||||
jv_free(result);
|
||||
result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),
|
||||
jv_string((char*)ebuf)));
|
||||
break;
|
||||
}
|
||||
} while (global && start != end);
|
||||
onig_region_free(region,1);
|
||||
region = NULL;
|
||||
if (region)
|
||||
onig_region_free(region,1);
|
||||
onig_free(reg);
|
||||
jv_free(input);
|
||||
jv_free(regex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static jv minmax_by(jv values, jv keys, int is_min) {
|
||||
if (jv_get_kind(values) != JV_KIND_ARRAY)
|
||||
return type_error2(values, keys, "cannot be iterated over");
|
||||
@@ -785,49 +468,15 @@ static jv f_error(jv input, jv msg) {
|
||||
return jv_invalid_with_msg(msg);
|
||||
}
|
||||
|
||||
// FIXME Should autoconf check for this!
|
||||
#ifndef WIN32
|
||||
extern const char **environ;
|
||||
#endif
|
||||
|
||||
static jv f_env(jv input) {
|
||||
jv_free(input);
|
||||
jv env = jv_object();
|
||||
const char *var, *val;
|
||||
for (const char **e = environ; *e != NULL; e++) {
|
||||
var = e[0];
|
||||
val = strchr(e[0], '=');
|
||||
if (val == NULL)
|
||||
env = jv_object_set(env, jv_string(var), jv_null());
|
||||
else if (var - val < INT_MAX)
|
||||
env = jv_object_set(env, jv_string_sized(var, val - var), jv_string(val + 1));
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
#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},
|
||||
@@ -850,10 +499,7 @@ static const struct cfunction function_list[] = {
|
||||
{(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},
|
||||
{(cfunction_ptr)f_error, "error", 2},
|
||||
{(cfunction_ptr)f_format, "format", 2},
|
||||
{(cfunction_ptr)f_env, "env", 1},
|
||||
{(cfunction_ptr)f_match, "_match_impl", 4},
|
||||
};
|
||||
#undef LIBM_DD
|
||||
|
||||
struct bytecoded_builtin { const char* name; block code; };
|
||||
static block bind_bytecoded_builtins(block b) {
|
||||
@@ -880,121 +526,56 @@ static block bind_bytecoded_builtins(block b) {
|
||||
};
|
||||
for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {
|
||||
builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,
|
||||
gen_param("arg"),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, "arg"),
|
||||
builtin_def_1arg[i].code));
|
||||
}
|
||||
}
|
||||
{
|
||||
block rangevar = gen_op_var_fresh(STOREV, "rangevar");
|
||||
block rangevar = block_bind(gen_op_var_unbound(STOREV, "rangevar"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
block init = BLOCK(gen_op_simple(DUP), gen_call("start", gen_noop()), rangevar);
|
||||
block range = BLOCK(init,
|
||||
gen_call("end", gen_noop()),
|
||||
gen_op_bound(RANGE, rangevar));
|
||||
gen_op_var_bound(RANGE, rangevar));
|
||||
builtins = BLOCK(builtins, gen_function("range",
|
||||
BLOCK(gen_param("start"), gen_param("end")),
|
||||
BLOCK(gen_op_block_unbound(CLOSURE_PARAM, "start"),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, "end")),
|
||||
range));
|
||||
}
|
||||
|
||||
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(f): _sort_by_impl(map([f]));",
|
||||
"def sort_by(f): sort(f);",
|
||||
"def group(f): _group_by_impl(map([f]));",
|
||||
"def group_by(f): group(f);",
|
||||
"def unique: group(.) | map(.[0]);",
|
||||
"def unique(f): group(f) | map(.[0]);",
|
||||
"def unique_by(f): unique(f);",
|
||||
"def max(f): _max_by_impl(map([f]));",
|
||||
"def min(f): _min_by_impl(map([f]));",
|
||||
"def max_by(f): max(f);",
|
||||
"def min_by(f): min(f);",
|
||||
#include "libm.h"
|
||||
"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 max_by(f): _max_by_impl(map([f]));",
|
||||
"def min_by(f): _min_by_impl(map([f]));",
|
||||
"def add: reduce .[] as $x (null; . + $x);",
|
||||
"def del(f): delpaths([path(f)]);",
|
||||
"def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",
|
||||
"def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",
|
||||
"def recurse(f): ., (f | select(. != null) | recurse(f));",
|
||||
"def recurse: recurse(.[]?);",
|
||||
"def recurse_down: recurse;",
|
||||
"def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];",
|
||||
"def from_entries: map({(.key): .value}) | add | .//={};",
|
||||
"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 paths(node_filter): . as $dot|paths|select(. as $p|$dot|getpath($p)|node_filter);",
|
||||
"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 leaf_paths: paths(scalars);",
|
||||
"def join(x): reduce .[] as $i (\"\"; . + (if . == \"\" then $i else x + $i end));",
|
||||
"def flatten: reduce .[] as $i ([]; if $i | type == \"array\" then . + ($i | flatten) else . + [$i] end);",
|
||||
"def flatten(x): reduce .[] as $i ([]; if $i | type == \"array\" and x > 0 then . + ($i | flatten(x-1)) else . + [$i] end);",
|
||||
"def range(x): range(0;x);",
|
||||
"def match(re; mode): _match_impl(re; mode; false)|.[];",
|
||||
"def match(val): if val | type == \"string\" then match(val; null) elif val | type == \"array\" and (val | length) > 1 then match(val[0]; val[1]) elif val | type == \"array\" and (val | length > 0) then match(val[0]; null) else error((val | type) + \" not a string or array\") end;",
|
||||
"def test(re; mode): _match_impl(re; mode; true);",
|
||||
"def test(val): if val |type == \"string\" then test(val; null) elif val | type == \"array\" and (val | length) > 1 then test(val[0]; val[1]) elif val | type == \"array\" and (val | length > 0) then test(val[0]; null) else error((val | type) + \" not a string or array\") end;",
|
||||
// "def test(re): _match(re; null; 1);",
|
||||
|
||||
};
|
||||
#undef LIBM_DD
|
||||
|
||||
|
||||
static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {
|
||||
struct locfile src;
|
||||
locfile_init(&src, jq, code, strlen(code));
|
||||
block funcs;
|
||||
int nerrors = jq_parse_library(&src, &funcs);
|
||||
if (nerrors == 0) {
|
||||
*bb = block_bind_referenced(funcs, *bb, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
locfile_free(&src);
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
static int slurp_lib(jq_state *jq, block* bb) {
|
||||
int nerrors = 0;
|
||||
char* home = getenv("HOME");
|
||||
if (home) { // silently ignore no $HOME
|
||||
jv filename = jv_string_append_str(jv_string(home), "/.jq");
|
||||
jv data = jv_load_file(jv_string_value(filename), 1);
|
||||
if (jv_is_valid(data)) {
|
||||
nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );
|
||||
}
|
||||
jv_free(filename);
|
||||
jv_free(data);
|
||||
}
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
int builtins_bind(jq_state *jq, block* bb) {
|
||||
int nerrors = slurp_lib(jq, bb);
|
||||
if (nerrors) {
|
||||
block_free(*bb);
|
||||
return nerrors;
|
||||
}
|
||||
block builtins_bind(block b) {
|
||||
for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {
|
||||
nerrors = builtins_bind_one(jq, bb, jq_builtins[i]);
|
||||
struct locfile src;
|
||||
locfile_init(&src, jq_builtins[i], strlen(jq_builtins[i]));
|
||||
block funcs;
|
||||
int nerrors = jq_parse_library(&src, &funcs);
|
||||
assert(!nerrors);
|
||||
b = block_bind_referenced(funcs, b, OP_IS_CALL_PSEUDO);
|
||||
locfile_free(&src);
|
||||
}
|
||||
*bb = bind_bytecoded_builtins(*bb);
|
||||
*bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);
|
||||
return nerrors;
|
||||
b = bind_bytecoded_builtins(b);
|
||||
return gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), b);
|
||||
}
|
||||
@@ -1,10 +1,21 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "jq.h"
|
||||
#include "bytecode.h"
|
||||
#include "compile.h"
|
||||
|
||||
int builtins_bind(jq_state *, block*);
|
||||
block builtins_bind(block);
|
||||
|
||||
|
||||
typedef void (*cfunction_ptr)(void);
|
||||
|
||||
struct cfunction {
|
||||
cfunction_ptr fptr;
|
||||
const char* name;
|
||||
int nargs;
|
||||
};
|
||||
|
||||
|
||||
jv cfunction_invoke(struct cfunction* function, jv input[]);
|
||||
|
||||
|
||||
#endif
|
||||
+20
-52
@@ -3,63 +3,17 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "opcode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
// flags, length
|
||||
#define NONE 0, 1
|
||||
#define CONSTANT OP_HAS_CONSTANT, 2
|
||||
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
||||
#define BRANCH OP_HAS_BRANCH, 2
|
||||
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
||||
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
||||
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
||||
|
||||
#define OP(name, imm, in, out) \
|
||||
{name, #name, imm, in, out},
|
||||
|
||||
static const struct opcode_description opcode_descriptions[] = {
|
||||
#include "opcode_list.h"
|
||||
};
|
||||
|
||||
static const struct opcode_description invalid_opcode_description = {
|
||||
-1, "#INVALID", 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op) {
|
||||
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
||||
return &opcode_descriptions[op];
|
||||
} else {
|
||||
return &invalid_opcode_description;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int bytecode_operation_length(uint16_t* codeptr) {
|
||||
static int bytecode_operation_length(uint16_t* codeptr) {
|
||||
int length = opcode_describe(*codeptr)->length;
|
||||
if (*codeptr == CALL_JQ || *codeptr == TAIL_CALL_JQ) {
|
||||
if (*codeptr == CALL_JQ) {
|
||||
length += codeptr[1] * 2;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void dump_code(int indent, struct bytecode* bc) {
|
||||
int pc = 0;
|
||||
while (pc < bc->codelen) {
|
||||
printf("%*s", indent, "");
|
||||
dump_operation(bc, bc->code + pc);
|
||||
printf("\n");
|
||||
pc += bytecode_operation_length(bc->code + pc);
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol_table_free(struct symbol_table* syms) {
|
||||
jv_mem_free(syms->cfunctions);
|
||||
jv_free(syms->cfunc_names);
|
||||
jv_mem_free(syms);
|
||||
}
|
||||
|
||||
void dump_disassembly(int indent, struct bytecode* bc) {
|
||||
if (bc->nclosures > 0) {
|
||||
printf("%*s[params: ", indent, "");
|
||||
@@ -83,6 +37,16 @@ void dump_disassembly(int indent, struct bytecode* bc) {
|
||||
}
|
||||
}
|
||||
|
||||
void dump_code(int indent, struct bytecode* bc) {
|
||||
int pc = 0;
|
||||
while (pc < bc->codelen) {
|
||||
printf("%*s", indent, "");
|
||||
dump_operation(bc, bc->code + pc);
|
||||
printf("\n");
|
||||
pc += bytecode_operation_length(bc->code + pc);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bytecode* getlevel(struct bytecode* bc, int level) {
|
||||
while (level > 0) {
|
||||
bc = bc->parent;
|
||||
@@ -98,7 +62,7 @@ void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
printf("%s", op->name);
|
||||
if (op->length > 1) {
|
||||
uint16_t imm = bc->code[pc++];
|
||||
if (op->op == CALL_JQ || op->op == TAIL_CALL_JQ) {
|
||||
if (op->op == CALL_JQ) {
|
||||
for (int i=0; i<imm+1; i++) {
|
||||
uint16_t level = bc->code[pc++];
|
||||
uint16_t idx = bc->code[pc++];
|
||||
@@ -145,9 +109,13 @@ void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_table_free(struct symbol_table* syms) {
|
||||
jv_mem_free(syms->cfunctions);
|
||||
jv_free(syms->cfunc_names);
|
||||
jv_mem_free(syms);
|
||||
}
|
||||
|
||||
void bytecode_free(struct bytecode* bc) {
|
||||
if (!bc)
|
||||
return;
|
||||
jv_mem_free(bc->code);
|
||||
jv_free(bc->constants);
|
||||
for (int i=0; i<bc->nsubfunctions; i++)
|
||||
|
||||
+4
-45
@@ -3,52 +3,10 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
typedef enum {
|
||||
#define OP(name, imm, in, out) name,
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
} opcode;
|
||||
|
||||
enum {
|
||||
NUM_OPCODES =
|
||||
#define OP(name, imm, in, out) +1
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
enum {
|
||||
OP_HAS_CONSTANT = 2,
|
||||
OP_HAS_VARIABLE = 4,
|
||||
OP_HAS_BRANCH = 8,
|
||||
OP_HAS_CFUNC = 32,
|
||||
OP_HAS_UFUNC = 64,
|
||||
OP_IS_CALL_PSEUDO = 128,
|
||||
OP_HAS_BINDING = 1024,
|
||||
};
|
||||
struct opcode_description {
|
||||
opcode op;
|
||||
const char* name;
|
||||
|
||||
int flags;
|
||||
|
||||
// length in 16-bit units
|
||||
int length;
|
||||
|
||||
int stack_in, stack_out;
|
||||
};
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op);
|
||||
|
||||
#include "opcode.h"
|
||||
#include "builtin.h"
|
||||
|
||||
#define MAX_CFUNCTION_ARGS 10
|
||||
typedef void (*cfunction_ptr)();
|
||||
struct cfunction {
|
||||
cfunction_ptr fptr;
|
||||
const char* name;
|
||||
int nargs;
|
||||
};
|
||||
|
||||
struct symbol_table {
|
||||
struct cfunction* cfunctions;
|
||||
int ncfunctions;
|
||||
@@ -81,9 +39,10 @@ struct bytecode {
|
||||
};
|
||||
|
||||
void dump_disassembly(int, struct bytecode* code);
|
||||
void dump_code(int, struct bytecode* code);
|
||||
void dump_operation(struct bytecode* bc, uint16_t* op);
|
||||
|
||||
int bytecode_operation_length(uint16_t* codeptr);
|
||||
void symbol_table_free(struct symbol_table* syms);
|
||||
void bytecode_free(struct bytecode* bc);
|
||||
|
||||
#endif
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# old values
|
||||
OLD_CFLAGS=$CFLAGS
|
||||
OLD_LDFLAGS=$LDFLAGS
|
||||
ORIG_PWD=`pwd`
|
||||
|
||||
# For parallelism in make
|
||||
NJOBS="-j`sysctl -n hw.ncpu || echo 1`"
|
||||
|
||||
|
||||
# Get oniguruma
|
||||
rm -rf $PWD/build/ios onig-5.9.5
|
||||
echo "Downloading oniguruma 5.9.5"
|
||||
curl http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.5.tar.gz | tar xz
|
||||
cd onig-5.9.5
|
||||
|
||||
# So, we need to remake the configure scripts so that the arm64 architecture
|
||||
# exists in config.sub. In order to keep autoreconf from failing, create
|
||||
# NEWS and ChangeLog.
|
||||
touch NEWS ChangeLog
|
||||
autoreconf -fi >/dev/null 2>&1
|
||||
|
||||
|
||||
|
||||
|
||||
CC=`xcrun -f clang`
|
||||
cd $ORIG_PWD
|
||||
|
||||
autoreconf -fi
|
||||
for arch in i386 x86_64 armv7 armv7s arm64; do
|
||||
|
||||
# Some of the architectures are a bit different...
|
||||
if [[ "$arch" = "i386" || "$arch" = "x86_64" ]]
|
||||
then
|
||||
SYSROOT=`xcrun -f --sdk iphonesimulator --show-sdk-path`
|
||||
else
|
||||
SYSROOT=`xcrun -f --sdk iphoneos --show-sdk-path`
|
||||
fi
|
||||
if [[ "$arch" = "arm64" ]]
|
||||
then
|
||||
HOST="aarch64-apple-darwin"
|
||||
else
|
||||
HOST="$arch-apple-darwin"
|
||||
fi
|
||||
|
||||
CFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_CFLAGS"
|
||||
LDFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_LDFLAGS"
|
||||
|
||||
|
||||
|
||||
# Build oniguruma for this architecture
|
||||
cd onig-5.9.5
|
||||
CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static --host=$HOST --prefix=$ORIG_PWD/build/ios/$arch
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to configure oniguruma for architecture $arch. Check `pwd`/config.log for details."
|
||||
cd $PWD
|
||||
exit $STATUS
|
||||
fi
|
||||
make clean
|
||||
make $NJBOS install
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to make oniguruma for architecture $arch."
|
||||
cd $PWD
|
||||
exit $STATUS
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Build jq for this architecture
|
||||
cd $ORIG_PWD
|
||||
CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static -host=$HOST --prefix=$ORIG_PWD/build/ios/$arch --with-oniguruma=$ORIG_PWD/build/ios/$arch
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to configure jq for architecture $arch"
|
||||
exit $STATUS
|
||||
fi
|
||||
make clean
|
||||
make $NJOBS install
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to make jq for architecture $arch"
|
||||
exit $STATUS
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# lipo together the different architectures into a universal 'fat' file
|
||||
lipo -create -output $ORIG_PWD/build/ios/libonig.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libonig.a
|
||||
lipo -create -output $ORIG_PWD/build/ios/libjq.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libjq.a
|
||||
|
||||
# copy the products into the destination directory and clean up the single-architecture files.
|
||||
cp $ORIG_PWD/build/ios/i386/include/*.h $ORIG_PWD/build/ios/
|
||||
rm -rf $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}
|
||||
|
||||
echo "Products are in $ORIG_PWD/build/ios"
|
||||
@@ -1,9 +1,8 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE // for strdup
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "opcode.h"
|
||||
#include "compile.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
@@ -49,9 +48,6 @@ struct inst {
|
||||
struct inst* bound_by;
|
||||
char* symbol;
|
||||
|
||||
int nformals;
|
||||
int nactuals;
|
||||
|
||||
block subfn; // used by CLOSURE_CREATE (body of function)
|
||||
block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments)
|
||||
|
||||
@@ -69,12 +65,9 @@ static inst* inst_new(opcode op) {
|
||||
i->bytecode_pos = -1;
|
||||
i->bound_by = 0;
|
||||
i->symbol = 0;
|
||||
i->nformals = -1;
|
||||
i->nactuals = -1;
|
||||
i->subfn = gen_noop();
|
||||
i->arglist = gen_noop();
|
||||
i->source = UNKNOWN_LOCATION;
|
||||
i->compiled = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -160,26 +153,33 @@ void inst_set_target(block b, block target) {
|
||||
b.first->imm.target = target.last;
|
||||
}
|
||||
|
||||
block gen_op_unbound(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_BINDING);
|
||||
block gen_op_var_unbound(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
|
||||
inst* i = inst_new(op);
|
||||
i->symbol = strdup(name);
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_var_fresh(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
|
||||
return block_bind(gen_op_unbound(op, name),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
}
|
||||
|
||||
block gen_op_bound(opcode op, block binder) {
|
||||
block gen_op_var_bound(opcode op, block binder) {
|
||||
assert(block_is_single(binder));
|
||||
block b = gen_op_unbound(op, binder.first->symbol);
|
||||
block b = gen_op_var_unbound(op, binder.first->symbol);
|
||||
b.first->bound_by = binder.first;
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_op_block_unbound(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_IS_CALL_PSEUDO);
|
||||
inst* i = inst_new(op);
|
||||
i->symbol = strdup(name);
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_block_bound(opcode op, block binder) {
|
||||
assert(block_is_single(binder));
|
||||
block b = gen_op_block_unbound(op, binder.first->symbol);
|
||||
b.first->bound_by = binder.first;
|
||||
return b;
|
||||
}
|
||||
|
||||
static void inst_join(inst* a, inst* b) {
|
||||
assert(a && b);
|
||||
@@ -216,34 +216,6 @@ int block_has_only_binders(block binders, int bindflags) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Count a binder's (function) formal params
|
||||
static int block_count_formals(block b) {
|
||||
int args = 0;
|
||||
if (b.first->op == CLOSURE_CREATE_C)
|
||||
return b.first->imm.cfunc->nargs - 1;
|
||||
for (inst* i = b.first->arglist.first; i; i = i->next) {
|
||||
assert(i->op == CLOSURE_PARAM);
|
||||
args++;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Count a call site's actual params
|
||||
static int block_count_actuals(block b) {
|
||||
int args = 0;
|
||||
for (inst* i = b.first; i; i = i->next) {
|
||||
switch (i->op) {
|
||||
default: assert(0 && "Unknown function type"); break;
|
||||
case CLOSURE_CREATE:
|
||||
case CLOSURE_PARAM:
|
||||
case CLOSURE_CREATE_C:
|
||||
args++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
static int block_bind_subblock(block binder, block body, int bindflags) {
|
||||
assert(block_is_single(binder));
|
||||
assert((opcode_describe(binder.first->op)->flags & bindflags) == bindflags);
|
||||
@@ -251,8 +223,6 @@ static int block_bind_subblock(block binder, block body, int bindflags) {
|
||||
assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);
|
||||
|
||||
binder.first->bound_by = binder.first;
|
||||
if (binder.first->nformals == -1)
|
||||
binder.first->nformals = block_count_formals(binder);
|
||||
int nrefs = 0;
|
||||
for (inst* i = body.first; i; i = i->next) {
|
||||
int flags = opcode_describe(i->op)->flags;
|
||||
@@ -260,12 +230,8 @@ static int block_bind_subblock(block binder, block body, int bindflags) {
|
||||
i->bound_by == 0 &&
|
||||
!strcmp(i->symbol, binder.first->symbol)) {
|
||||
// bind this instruction
|
||||
if (i->op == CALL_JQ && i->nactuals == -1)
|
||||
i->nactuals = block_count_actuals(i->arglist);
|
||||
if (i->nactuals == -1 || i->nactuals == binder.first->nformals) {
|
||||
i->bound_by = binder.first;
|
||||
nrefs++;
|
||||
}
|
||||
i->bound_by = binder.first;
|
||||
nrefs++;
|
||||
}
|
||||
// binding recurses into closures
|
||||
nrefs += block_bind_subblock(binder, i->subfn, bindflags);
|
||||
@@ -314,16 +280,12 @@ block gen_function(const char* name, block formals, block body) {
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_param(const char* name) {
|
||||
return gen_op_unbound(CLOSURE_PARAM, name);
|
||||
}
|
||||
|
||||
block gen_lambda(block body) {
|
||||
return gen_function("@lambda", gen_noop(), body);
|
||||
}
|
||||
|
||||
block gen_call(const char* name, block args) {
|
||||
block b = gen_op_unbound(CALL_JQ, name);
|
||||
block b = gen_op_block_unbound(CALL_JQ, name);
|
||||
b.first->arglist = args;
|
||||
return b;
|
||||
}
|
||||
@@ -344,27 +306,30 @@ block gen_both(block a, block b) {
|
||||
|
||||
|
||||
block gen_collect(block expr) {
|
||||
block array_var = gen_op_var_fresh(STOREV, "collect");
|
||||
block array_var = block_bind(gen_op_var_unbound(STOREV, "collect"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
block c = BLOCK(gen_op_simple(DUP), gen_const(jv_array()), array_var);
|
||||
|
||||
block tail = BLOCK(gen_op_bound(APPEND, array_var),
|
||||
block tail = BLOCK(gen_op_var_bound(APPEND, array_var),
|
||||
gen_op_simple(BACKTRACK));
|
||||
|
||||
return BLOCK(c,
|
||||
gen_op_target(FORK, tail),
|
||||
expr,
|
||||
tail,
|
||||
gen_op_bound(LOADVN, array_var));
|
||||
gen_op_var_bound(LOADVN, array_var));
|
||||
}
|
||||
|
||||
block gen_reduce(const char* varname, block source, block init, block body) {
|
||||
block res_var = gen_op_var_fresh(STOREV, "reduce");
|
||||
block res_var = block_bind(gen_op_var_unbound(STOREV, "reduce"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
|
||||
block loop = BLOCK(gen_op_simple(DUP),
|
||||
source,
|
||||
block_bind(gen_op_unbound(STOREV, varname),
|
||||
BLOCK(gen_op_bound(LOADVN, res_var),
|
||||
block_bind(gen_op_var_unbound(STOREV, varname),
|
||||
BLOCK(gen_op_var_bound(LOADVN, res_var),
|
||||
body,
|
||||
gen_op_bound(STOREV, res_var)),
|
||||
gen_op_var_bound(STOREV, res_var)),
|
||||
OP_HAS_VARIABLE),
|
||||
gen_op_simple(BACKTRACK));
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
@@ -372,18 +337,19 @@ block gen_reduce(const char* varname, block source, block init, block body) {
|
||||
res_var,
|
||||
gen_op_target(FORK, loop),
|
||||
loop,
|
||||
gen_op_bound(LOADVN, res_var));
|
||||
gen_op_var_bound(LOADVN, res_var));
|
||||
}
|
||||
|
||||
block gen_definedor(block a, block b) {
|
||||
// var found := false
|
||||
block found_var = gen_op_var_fresh(STOREV, "found");
|
||||
block found_var = block_bind(gen_op_var_unbound(STOREV, "found"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);
|
||||
|
||||
// if found, backtrack. Otherwise execute b
|
||||
block backtrack = gen_op_simple(BACKTRACK);
|
||||
block tail = BLOCK(gen_op_simple(DUP),
|
||||
gen_op_bound(LOADV, found_var),
|
||||
gen_op_var_bound(LOADV, found_var),
|
||||
gen_op_target(JUMP_F, backtrack),
|
||||
backtrack,
|
||||
gen_op_simple(POP),
|
||||
@@ -395,7 +361,7 @@ block gen_definedor(block a, block b) {
|
||||
// found := true, produce result
|
||||
block if_found = BLOCK(gen_op_simple(DUP),
|
||||
gen_const(jv_true()),
|
||||
gen_op_bound(STOREV, found_var),
|
||||
gen_op_var_bound(STOREV, found_var),
|
||||
gen_op_target(JUMP, tail));
|
||||
|
||||
return BLOCK(init,
|
||||
@@ -434,7 +400,7 @@ block gen_or(block a, block b) {
|
||||
|
||||
block gen_var_binding(block var, const char* name, block body) {
|
||||
return BLOCK(gen_op_simple(DUP), var,
|
||||
block_bind(gen_op_unbound(STOREV, name),
|
||||
block_bind(gen_op_var_unbound(STOREV, name),
|
||||
body, OP_HAS_VARIABLE));
|
||||
}
|
||||
|
||||
@@ -475,12 +441,6 @@ static int count_cfunctions(block b) {
|
||||
}
|
||||
|
||||
|
||||
static int returns_immediately(inst* i) {
|
||||
while (i->op == JUMP)
|
||||
i = i->imm.target;
|
||||
return i->op == RET;
|
||||
}
|
||||
|
||||
// Expands call instructions into a calling sequence
|
||||
static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
int errors = 0;
|
||||
@@ -488,7 +448,7 @@ static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
for (inst* curr; (curr = block_take(b));) {
|
||||
if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {
|
||||
if (!curr->bound_by) {
|
||||
locfile_locate(locations, curr->source, "error: %s/%d is not defined", curr->symbol, block_count_actuals(curr->arglist));
|
||||
locfile_locate(locations, curr->source, "error: %s is not defined", curr->symbol);
|
||||
errors++;
|
||||
// don't process this instruction if it's not well-defined
|
||||
ret = BLOCK(ret, inst_block(curr));
|
||||
@@ -505,12 +465,6 @@ static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
case CLOSURE_CREATE:
|
||||
case CLOSURE_PARAM: {
|
||||
block callargs = gen_noop();
|
||||
/* A call is a tail call if:
|
||||
a) the next instruction is a RET or a chain of unconditional JUMPs
|
||||
that ends in a RET, and
|
||||
b) none of the closures -callee included- are in the current bytecode block. */
|
||||
int is_tail_call = b->first && returns_immediately(b->first);
|
||||
if (!curr->bound_by->compiled) is_tail_call = 0;
|
||||
for (inst* i; (i = block_take(&curr->arglist));) {
|
||||
assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO);
|
||||
block b = inst_block(i);
|
||||
@@ -521,15 +475,13 @@ static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
break;
|
||||
case CLOSURE_CREATE:
|
||||
block_append(&prelude, b);
|
||||
block_append(&callargs, gen_op_bound(CLOSURE_REF, b));
|
||||
block_append(&callargs, gen_op_block_bound(CLOSURE_REF, b));
|
||||
break;
|
||||
}
|
||||
if (!i->bound_by->compiled) is_tail_call = 0;
|
||||
actual_args++;
|
||||
}
|
||||
curr->imm.intval = actual_args;
|
||||
curr->arglist = callargs;
|
||||
if (is_tail_call) curr->op = TAIL_CALL_JQ;
|
||||
|
||||
if (curr->bound_by->op == CLOSURE_CREATE) {
|
||||
for (inst* i = curr->bound_by->arglist.first; i; i = i->next) {
|
||||
@@ -561,7 +513,14 @@ static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
}
|
||||
}
|
||||
|
||||
assert(actual_args == desired_args); // because now handle this above
|
||||
if (actual_args != desired_args) {
|
||||
locfile_locate(locations, curr->source,
|
||||
"error: %s arguments to %s (expected %d but got %d)",
|
||||
actual_args > desired_args ? "too many" : "too few",
|
||||
curr->symbol, desired_args, actual_args);
|
||||
errors++;
|
||||
}
|
||||
|
||||
}
|
||||
ret = BLOCK(ret, prelude, inst_block(curr));
|
||||
}
|
||||
@@ -574,13 +533,13 @@ static int compile(struct locfile* locations, struct bytecode* bc, block b) {
|
||||
int pos = 0;
|
||||
int var_frame_idx = 0;
|
||||
bc->nsubfunctions = 0;
|
||||
b = BLOCK(b, gen_op_simple(RET));
|
||||
errors += expand_call_arglist(locations, &b);
|
||||
b = BLOCK(b, gen_op_simple(RET));
|
||||
jv localnames = jv_array();
|
||||
for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
if (!curr->next) assert(curr == b.last);
|
||||
int length = opcode_describe(curr->op)->length;
|
||||
if (curr->op == CALL_JQ || curr->op == TAIL_CALL_JQ) {
|
||||
if (curr->op == CALL_JQ) {
|
||||
for (inst* arg = curr->arglist.first; arg; arg = arg->next) {
|
||||
length += 2;
|
||||
}
|
||||
@@ -654,7 +613,7 @@ static int compile(struct locfile* locations, struct bytecode* bc, block b) {
|
||||
assert(!curr->arglist.first);
|
||||
code[pos++] = (uint16_t)curr->imm.intval;
|
||||
code[pos++] = curr->bound_by->imm.intval;
|
||||
} else if (curr->op == CALL_JQ || curr->op == TAIL_CALL_JQ) {
|
||||
} else if (curr->op == CALL_JQ) {
|
||||
assert(curr->bound_by->op == CLOSURE_CREATE ||
|
||||
curr->bound_by->op == CLOSURE_PARAM);
|
||||
code[pos++] = (uint16_t)curr->imm.intval;
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
#define COMPILE_H
|
||||
#include <stdint.h>
|
||||
#include "jv.h"
|
||||
#include "bytecode.h"
|
||||
#include "opcode.h"
|
||||
#include "locfile.h"
|
||||
|
||||
struct bytecode;
|
||||
struct symbol_table;
|
||||
struct cfunction;
|
||||
|
||||
struct inst;
|
||||
typedef struct inst inst;
|
||||
|
||||
@@ -20,12 +24,11 @@ block gen_noop();
|
||||
block gen_op_simple(opcode op);
|
||||
block gen_const(jv constant);
|
||||
block gen_op_target(opcode op, block target);
|
||||
block gen_op_unbound(opcode op, const char* name);
|
||||
block gen_op_bound(opcode op, block binder);
|
||||
block gen_op_var_fresh(opcode op, const char* name);
|
||||
block gen_op_var_unbound(opcode op, const char* name);
|
||||
block gen_op_var_bound(opcode op, block binder);
|
||||
block gen_op_block_unbound(opcode op, const char* name);
|
||||
|
||||
block gen_function(const char* name, block formals, block body);
|
||||
block gen_param(const char* name);
|
||||
block gen_lambda(block body);
|
||||
block gen_call(const char* name, block body);
|
||||
block gen_subexp(block a);
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#undef NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#undef YYTEXT_POINTER
|
||||
@@ -1,10 +0,0 @@
|
||||
config.guess
|
||||
config.sub
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
ltmain.sh
|
||||
missing
|
||||
ylwrap
|
||||
test-driver
|
||||
ar-lib
|
||||
Executable
+343
@@ -0,0 +1,343 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-03-05.13; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
|
||||
# Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
Executable
+708
@@ -0,0 +1,708 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2012-03-27.16; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
|
||||
# 2011, 2012 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' "$nl" < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
|
||||
# However on
|
||||
# $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\':
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
# tcc 0.9.26 (FIXME still under development at the moment of writing)
|
||||
# will emit a similar output, but also prepend the continuation lines
|
||||
# with horizontal tabulation characters.
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form 'foo.o: dependent.h',
|
||||
# or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \
|
||||
< "$tmpdepfile" > "$depfile"
|
||||
sed '
|
||||
s/[ '"$tab"'][ '"$tab"']*/ /g
|
||||
s/^ *//
|
||||
s/ *\\*$//
|
||||
s/^[^:]*: *//
|
||||
/^$/d
|
||||
/:$/d
|
||||
s/$/ :/
|
||||
' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test "$stat" = 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' "$nl" < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
Executable
+527
@@ -0,0 +1,527 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2011-01-19.21; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit=${DOITPROG-}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
no_target_directory=
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t) dst_arg=$2
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -1,2 +0,0 @@
|
||||
libtool.m4
|
||||
lt*.m4
|
||||
@@ -1,8 +0,0 @@
|
||||
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])])
|
||||
@@ -1,62 +0,0 @@
|
||||
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)
|
||||
])
|
||||
@@ -1,9 +0,0 @@
|
||||
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
|
||||
])
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
AC_DEFUN([upcase],[`echo $1 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`])dnl
|
||||
Executable
+331
@@ -0,0 +1,331 @@
|
||||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2012-01-06.13; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
|
||||
# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
|
||||
\`g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# normalize program name to check for.
|
||||
program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program). This is about non-GNU programs, so use $1 not
|
||||
# $program.
|
||||
case $1 in
|
||||
lex*|yacc*)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $program in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison*|yacc*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex*|flex*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit $?
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#! /bin/sh
|
||||
# ylwrap - wrapper for lex/yacc invocations.
|
||||
|
||||
scriptversion=2011-08-25.18; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
|
||||
# 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
#
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
case "$1" in
|
||||
'')
|
||||
echo "$0: No files given. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1
|
||||
;;
|
||||
--basedir)
|
||||
basedir=$2
|
||||
shift 2
|
||||
;;
|
||||
-h|--h*)
|
||||
cat <<\EOF
|
||||
Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
|
||||
|
||||
Wrapper for lex/yacc invocations, renaming files as desired.
|
||||
|
||||
INPUT is the input file
|
||||
OUTPUT is one file PROG generates
|
||||
DESIRED is the file we actually want instead of OUTPUT
|
||||
PROGRAM is program to run
|
||||
ARGS are passed to PROG
|
||||
|
||||
Any number of OUTPUT,DESIRED pairs may be used.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v|--v*)
|
||||
echo "ylwrap $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# The input.
|
||||
input="$1"
|
||||
shift
|
||||
case "$input" in
|
||||
[\\/]* | ?:[\\/]*)
|
||||
# Absolute path; do nothing.
|
||||
;;
|
||||
*)
|
||||
# Relative path. Make it absolute.
|
||||
input="`pwd`/$input"
|
||||
;;
|
||||
esac
|
||||
|
||||
pairlist=
|
||||
while test "$#" -ne 0; do
|
||||
if test "$1" = "--"; then
|
||||
shift
|
||||
break
|
||||
fi
|
||||
pairlist="$pairlist $1"
|
||||
shift
|
||||
done
|
||||
|
||||
# The program to run.
|
||||
prog="$1"
|
||||
shift
|
||||
# Make any relative path in $prog absolute.
|
||||
case "$prog" in
|
||||
[\\/]* | ?:[\\/]*) ;;
|
||||
*[\\/]*) prog="`pwd`/$prog" ;;
|
||||
esac
|
||||
|
||||
# FIXME: add hostname here for parallel makes that run commands on
|
||||
# other machines. But that might take us over the 14-char limit.
|
||||
dirname=ylwrap$$
|
||||
do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
mkdir $dirname || exit 1
|
||||
|
||||
cd $dirname
|
||||
|
||||
case $# in
|
||||
0) "$prog" "$input" ;;
|
||||
*) "$prog" "$@" "$input" ;;
|
||||
esac
|
||||
ret=$?
|
||||
|
||||
if test $ret -eq 0; then
|
||||
set X $pairlist
|
||||
shift
|
||||
first=yes
|
||||
# Since DOS filename conventions don't allow two dots,
|
||||
# the DOS version of Bison writes out y_tab.c instead of y.tab.c
|
||||
# and y_tab.h instead of y.tab.h. Test to see if this is the case.
|
||||
y_tab_nodot="no"
|
||||
if test -f y_tab.c || test -f y_tab.h; then
|
||||
y_tab_nodot="yes"
|
||||
fi
|
||||
|
||||
# The directory holding the input.
|
||||
input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
|
||||
# Quote $INPUT_DIR so we can use it in a regexp.
|
||||
# FIXME: really we should care about more than `.' and `\'.
|
||||
input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
|
||||
|
||||
while test "$#" -ne 0; do
|
||||
from="$1"
|
||||
# Handle y_tab.c and y_tab.h output by DOS
|
||||
if test $y_tab_nodot = "yes"; then
|
||||
if test $from = "y.tab.c"; then
|
||||
from="y_tab.c"
|
||||
else
|
||||
if test $from = "y.tab.h"; then
|
||||
from="y_tab.h"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test -f "$from"; then
|
||||
# If $2 is an absolute path name, then just use that,
|
||||
# otherwise prepend `../'.
|
||||
case "$2" in
|
||||
[\\/]* | ?:[\\/]*) target="$2";;
|
||||
*) target="../$2";;
|
||||
esac
|
||||
|
||||
# We do not want to overwrite a header file if it hasn't
|
||||
# changed. This avoid useless recompilations. However the
|
||||
# parser itself (the first file) should always be updated,
|
||||
# because it is the destination of the .y.c rule in the
|
||||
# Makefile. Divert the output of all other files to a temporary
|
||||
# file so we can compare them to existing versions.
|
||||
if test $first = no; then
|
||||
realtarget="$target"
|
||||
target="tmp-`echo $target | sed s/.*[\\/]//g`"
|
||||
fi
|
||||
# Edit out `#line' or `#' directives.
|
||||
#
|
||||
# We don't want the resulting debug information to point at
|
||||
# an absolute srcdir; it is better for it to just mention the
|
||||
# .y file with no path.
|
||||
#
|
||||
# We want to use the real output file name, not yy.lex.c for
|
||||
# instance.
|
||||
#
|
||||
# We want the include guards to be adjusted too.
|
||||
FROM=`echo "$from" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
TARGET=`echo "$2" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
|
||||
sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
|
||||
-e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
|
||||
|
||||
# Check whether header files must be updated.
|
||||
if test $first = no; then
|
||||
if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
|
||||
echo "$2" is unchanged
|
||||
rm -f "$target"
|
||||
else
|
||||
echo updating "$2"
|
||||
mv -f "$target" "$realtarget"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# A missing file is only an error for the first file. This
|
||||
# is a blatant hack to let us support using "yacc -d". If -d
|
||||
# is not specified, we don't want an error when the header
|
||||
# file is "missing".
|
||||
if test $first = yes; then
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
shift
|
||||
shift
|
||||
first=no
|
||||
done
|
||||
else
|
||||
ret=$?
|
||||
fi
|
||||
|
||||
# Remove the directory.
|
||||
cd ..
|
||||
rm -rf $dirname
|
||||
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
+12
-125
@@ -1,37 +1,21 @@
|
||||
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],
|
||||
AC_INIT([jq], [1.3], [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 -Wall])
|
||||
AM_INIT_AUTOMAKE([parallel-tests foreign])
|
||||
AM_SILENT_RULES([yes])
|
||||
AM_PROG_AR
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CPP_WERROR
|
||||
AC_PROG_YACC
|
||||
AC_OBJEXT
|
||||
AC_EXEEXT
|
||||
LT_INIT([shared static win32-dll])
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
dnl couldn't use AM_PROG_LEX as it doesn't support header files like the
|
||||
dnl AC_PROG_YACC macros...
|
||||
|
||||
dnl check bison version
|
||||
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
|
||||
@@ -42,63 +26,6 @@ if test "x$LEX" != xflex; then
|
||||
fi
|
||||
|
||||
|
||||
##########################################################################
|
||||
# check for ONIGURUMA library
|
||||
##########################################################################
|
||||
|
||||
AC_ARG_WITH([oniguruma],
|
||||
[AS_HELP_STRING([--with-oniguruma=prefix],
|
||||
[try this for a non-standard install prefix of the oniguruma library])],
|
||||
[ONIGURUMAPATHSET=1],
|
||||
[ONIGURUMAPATHSET=0])
|
||||
|
||||
if test $ONIGURUMAPATHSET == 1; then
|
||||
CFLAGS="$CFLAGS -I${with_oniguruma}/include"
|
||||
LDFLAGS="$LDFLAGS -L${with_oniguruma}/lib"
|
||||
fi
|
||||
|
||||
# store current *FLAGS and merge with AM_*FLAGS for compilation and linker check
|
||||
OLD_CFLAGS=$CFLAGS;
|
||||
OLD_LDFLAGS=$LDFLAGS;
|
||||
CFLAGS="$AM_CFLAGS $CFLAGS"
|
||||
LDFLAGS="$AM_LDFLAGS $LDFLAGS"
|
||||
|
||||
# ensure the library to check for is covered by the LIBS variable
|
||||
OLD_LIBS=$LIBS
|
||||
LIBS="$LIBS -lonig"
|
||||
|
||||
# check for ONIGURUMA library headers
|
||||
AC_MSG_CHECKING([for oniguruma.h])
|
||||
# try to compile a file that includes a header of the library oniguruma
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include <oniguruma.h> ])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
# try to link the function 'onig_free' out of library oniguruma
|
||||
AC_MSG_CHECKING([for oniguruma usability])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <oniguruma.h>]],
|
||||
[[onig_free(0);]])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
FOUND_ONIGURUMA=1;],
|
||||
[AC_MSG_RESULT([no])
|
||||
LIBS=$OLD_LIBS; dnl reset to old value since oniguruma was not found
|
||||
FOUND_ONIGURUMA=0;])],
|
||||
[AC_MSG_RESULT([not found])
|
||||
FOUND_ONIGURUMA=0;])
|
||||
|
||||
# reset original *FLAGS
|
||||
CFLAGS=$OLD_CFLAGS
|
||||
LDFLAGS=$OLD_LDFLAGS
|
||||
|
||||
# handle check results
|
||||
if test $FOUND_ONIGURUMA != 1; then
|
||||
AC_MSG_NOTICE([Oniguruma was not found.])
|
||||
AC_MSG_NOTICE([ Try setting the location using '--with-oniguruma=PREFIX' ])
|
||||
AC_MSG_ERROR([ oniguruma is required to build jq.])
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
dnl Check for valgrind
|
||||
AC_CHECK_PROGS(valgrind_cmd, valgrind)
|
||||
if test "x$valgrind_cmd" = "x" ; then
|
||||
@@ -112,13 +39,10 @@ AC_ARG_ENABLE([docs],
|
||||
|
||||
AS_IF([test "x$enable_docs" != "xno"],[
|
||||
AC_CHECK_PROGS(bundle_cmd, bundle)
|
||||
|
||||
AC_CACHE_CHECK([for Ruby dependencies], [jq_cv_ruby_deps],
|
||||
[jq_cv_ruby_deps=yes;
|
||||
AS_IF([test "x$bundle_cmd" = "x" || \
|
||||
! bmsg="`cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null`"],[
|
||||
AC_MSG_WARN([$bmsg])
|
||||
cat <<EOF
|
||||
AS_IF([test "x$bundle_cmd" = "x" || \
|
||||
! ( cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null )],[
|
||||
AC_MSG_WARN([no bundler])
|
||||
cat <<EOF
|
||||
*****************************************************************
|
||||
* Ruby dependencies for building jq documentation not found. *
|
||||
* You can still build, install and hack on jq, but the manpage *
|
||||
@@ -126,53 +50,16 @@ AS_IF([test "x$enable_docs" != "xno"],[
|
||||
* See docs/README.md for how to install the docs dependencies. *
|
||||
*****************************************************************
|
||||
EOF
|
||||
jq_cv_ruby_deps=no
|
||||
])])
|
||||
|
||||
if test "x$jq_cv_ruby_deps" != "xyes"; then
|
||||
enable_docs=no
|
||||
fi
|
||||
enable_docs=no
|
||||
])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
||||
|
||||
AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
|
||||
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
|
||||
|
||||
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_C_BIGENDIAN(
|
||||
AC_DEFINE([IEEE_MC68k], 1, [machine is bigendian]),
|
||||
AC_DEFINE([IEEE_8087], 1, [machine is littleendian]),
|
||||
AC_MSG_ERROR(unknown endianess),
|
||||
AC_MSG_ERROR(universial endianess not supported)
|
||||
)
|
||||
|
||||
AC_SUBST([BUNDLER], ["$bundle_cmd"])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([config/m4])
|
||||
dnl AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AM_INIT_AUTOMAKE([-Wall])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
+12
-4
@@ -75,7 +75,7 @@ $BINARIES = {
|
||||
|
||||
$BINARIES.each do |name, args|
|
||||
file "../build/#{name}" do |t|
|
||||
sh "../scripts/crosscompile -j4 #{name} #{args}"
|
||||
sh "MAKEFLAGS=-j4 ../scripts/crosscompile #{name} #{args}"
|
||||
end
|
||||
task :binaries => ["../build/#{name}"]
|
||||
end
|
||||
@@ -91,6 +91,10 @@ def load_manual
|
||||
YAML::load(File.open("content/3.manual/manual.yml"))
|
||||
end
|
||||
|
||||
def jq_version
|
||||
YAML::load(File.open("site.yml"))["jq_version"]
|
||||
end
|
||||
|
||||
task :manpage do
|
||||
Tempfile.open "manpage" do |f|
|
||||
manual = load_manual
|
||||
@@ -137,10 +141,14 @@ task :mantests do
|
||||
end
|
||||
end
|
||||
|
||||
$TARBALL = "../jq-#{jq_version}.tar.gz"
|
||||
file $TARBALL do
|
||||
sh "cd ..; ./configure && make dist && make distclean"
|
||||
end
|
||||
|
||||
directory "output/download/source"
|
||||
task :tarball => ["output/download/source"] do
|
||||
sh "cd ..; ./configure && make dist && make distclean"
|
||||
sh "cp ../jq-*.tar.gz output/download/source"
|
||||
task :tarball => ["output/download/source", $TARBALL] do
|
||||
sh "cp #{$TARBALL} output/download/source"
|
||||
end
|
||||
|
||||
task :dist => [:build, :binaries, :tarball]
|
||||
+160
-241
@@ -2,205 +2,123 @@ headline: Tutorial
|
||||
body:
|
||||
- text: |
|
||||
|
||||
GitHub has a JSON API, so let's play with that. This URL gets
|
||||
us the last 5 commits from the jq repo:
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5'"
|
||||
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'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"sha": "d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"commit": {
|
||||
"author": {
|
||||
"name": "Stephen Dolan",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"date": "2013-06-22T16:30:59Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Stephen Dolan",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"date": "2013-06-22T16:30:59Z"
|
||||
},
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
||||
"tree": {
|
||||
"sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
|
||||
},
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"comment_count": 0
|
||||
},
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
|
||||
"author": {
|
||||
"login": "stedolan",
|
||||
...
|
||||
|
||||
- text: |
|
||||
Github returns 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",
|
||||
...
|
||||
{"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}, ...
|
||||
|
||||
- text: |
|
||||
|
||||
There's still far too much info for our purposes, so we can try
|
||||
just grab the first commit:
|
||||
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 `.`:
|
||||
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]'"
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.'"
|
||||
result: |
|
||||
|
||||
{
|
||||
"parents": [
|
||||
"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": [
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
"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/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"
|
||||
/* lots more results... */
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
- 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 '.[0] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
- command: "jq '.results[0] | {from_user, text}'"
|
||||
result: |
|
||||
{
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
|
||||
The `|` operator in jq feeds the output of one filter
|
||||
(`.[0]` which gets the first element of the array in the response)
|
||||
into the input of another (`{...}` which
|
||||
builds an object out of those fields). You can access nested attributes,
|
||||
such as `.commit.message`:
|
||||
(`.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:
|
||||
|
||||
|
||||
|
||||
Now let's get the rest of the commits:
|
||||
|
||||
|
||||
- command: "jq '.[] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
- command: "jq '.results[] | {from_user, text}'"
|
||||
result: |
|
||||
{
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
"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": "Reject all overlong UTF8 sequences."
|
||||
"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": "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": "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 example in manual for `floor`. See #155."
|
||||
"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": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
|
||||
|
||||
|
||||
- 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}`.
|
||||
|
||||
`.results[]` returns each element of the results array, one at a
|
||||
time, which are all fed into `{from_user, text}`.
|
||||
|
||||
|
||||
Data in jq is represented as streams of JSON values - every jq
|
||||
@@ -210,108 +128,109 @@ 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 '[.[] | {message: .commit.message, name: .commit.committer.name}]'"
|
||||
- command: "jq '[.results[] | {from_user, text}]'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
"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": "Reject all overlong UTF8 sequences."
|
||||
"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": "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": "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 example in manual for `floor`. See #155."
|
||||
"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": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
- 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:
|
||||
|
||||
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.
|
||||
{
|
||||
"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"}
|
||||
]
|
||||
}
|
||||
|
||||
"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"
|
||||
}
|
||||
]
|
||||
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.
|
||||
|
||||
|
||||
|
||||
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]}]'"
|
||||
- command: "jq '.results[] | {from_user, text, urls: [.entities.urls[].url]}'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
{
|
||||
"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"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
Here we're making an object as before, but this time the `parents`
|
||||
field is being set to `[.parents[].html_url]}]`, which collects
|
||||
all of the parent commit urls defined in the parents object.
|
||||
|
||||
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.
|
||||
|
||||
- text: |
|
||||
|
||||
@@ -319,4 +238,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.
|
||||
@@ -16,8 +16,7 @@ body:
|
||||
|
||||
### Linux
|
||||
|
||||
* jq 1.4 binaries for [64-bit](linux64/jq) or [32-bit](linux32/jq) systems.
|
||||
* jq 1.3 binaries for [64-bit](linux64/jq-1.3/jq) or [32-bit](linux32/jq-1.3/jq) systems.
|
||||
* Binaries for [64-bit](linux64/jq) or [32-bit](linux32/jq) systems.
|
||||
|
||||
* For Arch users, a PKGBUILD for jq-1.1 is in the
|
||||
[AUR](https://aur.archlinux.org/packages.php?ID=63849), as
|
||||
@@ -28,39 +27,31 @@ body:
|
||||
manually by following instructions on [Arch Linux's
|
||||
Wiki](https://wiki.archlinux.org/index.php/Arch_UseRepository).
|
||||
|
||||
* jq 1.3 is [in Debian](http://packages.debian.org/jq) (a request to update to 1.4 has been filed)
|
||||
* jq 1.2 is [in Debian](http://packages.debian.org/jq)
|
||||
|
||||
### OS X
|
||||
|
||||
* `brew install jq` using [homebrew](http://mxcl.github.com/homebrew/)
|
||||
* `brew install jq` using [homebrew](http://mxcl.github.io/homebrew/)
|
||||
|
||||
* Or, grab prebuilt jq 1.4 [64-bit binaries](osx64/jq) or [32-bit
|
||||
* Or, grab prebuilt [64-bit binaries](osx64/jq) or [32-bit
|
||||
binaries](osx32/jq)
|
||||
|
||||
* Or, grab prebuilt jq 1.3 [64-bit binaries](osx64/jq-1.3/jq) or [32-bit
|
||||
binaries](osx32/jq-1.3/jq)
|
||||
|
||||
### Solaris 11
|
||||
|
||||
* jq 1.4 executables for [64-bit](solaris11-64/jq.exe) or [32-bit](solaris11-32/jq.exe)
|
||||
|
||||
### Windows
|
||||
|
||||
* jq 1.4 executables for [64-bit](win64/jq.exe) or [32-bit](win32/jq.exe)
|
||||
* jq 1.3 executables for [64-bit](win64/jq-1.3/jq.exe) or [32-bit](win32/jq-1.3/jq.exe)
|
||||
* Executables for [64-bit](win64/jq.exe) or [32-bit](win32/jq.exe)
|
||||
|
||||
### From source on Linux, OS X, Cygwin, and other POSIX-like operating systems
|
||||
### From source on Linux or OS X
|
||||
|
||||
* [Source tarball for jq $JQ_VERSION](source/jq-$JQ_VERSION.tar.gz)
|
||||
|
||||
You can build it using the usual `./configure && make && sudo
|
||||
make install` rigmarole.
|
||||
|
||||
If you're interested in using the lastest development version, try:
|
||||
If you're interested in using the latest development version, try:
|
||||
|
||||
git clone https://github.com/stedolan/jq.git
|
||||
cd jq
|
||||
autoreconf -i
|
||||
autoreconf
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
@@ -81,7 +72,7 @@ body:
|
||||
On OS X, these are all included in Apple's command line tools, which
|
||||
can be installed from [Xcode](http://developer.apple.com/technologies/tools/).
|
||||
However, you may find that you need a newer version of Bison than the one provided
|
||||
by Apple. This can be found in [Homebrew](http://mxcl.github.com/homebrew/)
|
||||
by Apple. This can be found in [Homebrew](http://mxcl.github.io/homebrew/)
|
||||
or [MacPorts](http://macports.org/).
|
||||
|
||||
`flex` and `bison` are used to generate the lexer and parser for
|
||||
@@ -91,14 +82,9 @@ body:
|
||||
#### Building the documentation
|
||||
|
||||
jq's documentation is compiled into static HTML using
|
||||
[Bonsai](http://www.tinytree.info). To view the documentation
|
||||
locally, run `rake serve` (or `bundle exec rake serve`) from the
|
||||
docs/ subdirectory. To build the docs just `rake build` from the
|
||||
docs subdirectory. You'll need a few Ruby dependencies, which can
|
||||
be installed by following the instructions in `docs/README.md`.
|
||||
|
||||
The man page is built by `make jq.1`, or just `make`, also from
|
||||
the YAML docs, and you'll still need the Ruby dependencies to
|
||||
build the manpage.
|
||||
[Bonsai](http://www.tinytree.info). To view the documentation
|
||||
locally, run `rake serve` from the docs/ subdirectory. You'll
|
||||
need a few Ruby dependencies, which can be installed by
|
||||
following the instructions in `docs/README.md`.
|
||||
|
||||
|
||||
+180
-774
File diff suppressed because it is too large.
Load diff
@@ -27,13 +27,8 @@ 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 released.
|
||||
jq 1.3 (finally) released! Get it on the [download](download/) page.
|
||||
|
||||
+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.4-2-g15c4a7f-dirty"
|
||||
jq_version: "1.3"
|
||||
|
||||
root: '/jq'
|
||||
footer: |
|
||||
|
||||
Vendored
+1
-10
@@ -10,7 +10,6 @@
|
||||
<img src="{{root}}/jq.png" style="width: 400px; height: 220px; float: left; margin-right: 60px">
|
||||
<div id="blurb">
|
||||
{{blurb | markdownify}}
|
||||
|
||||
<div class="btn-group" style="float:right">
|
||||
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
Download jq-{{jq_version}}
|
||||
@@ -19,18 +18,10 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{root}}/download/linux64/jq">Linux (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/osx64/jq">OS X (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/solaris11-64/jq">Solaris (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/win64/jq.exe">Windows (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/">Other platforms, older versions, and source</a></li>
|
||||
<li><a href="{{root}}/download/">Other platforms and source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" style="float:right">
|
||||
<a class="btn btn-primary" href="http://jqplay.org">
|
||||
Try online!
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="clear:both;"></div>
|
||||
</div>
|
||||
|
||||
Vendored
-2
@@ -17,8 +17,6 @@
|
||||
{% endfor %}
|
||||
<li><a href="https://github.com/stedolan/jq/issues">Issues</a></li>
|
||||
<li><a href="https://github.com/stedolan/jq">Source</a></li>
|
||||
<li><a href="http://jqplay.org">Try online!</a></li>
|
||||
<li><a href="https://raw.githubusercontent.com/stedolan/jq/master/NEWS">NEWS</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef EXEC_STACK_H
|
||||
#define EXEC_STACK_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct determine_alignment {
|
||||
char x;
|
||||
union { int i; double d; uint64_t u64; size_t sz; void* ptr; } u;
|
||||
};
|
||||
enum {ALIGNMENT = offsetof(struct determine_alignment, u)};
|
||||
|
||||
static size_t align_round_up(size_t sz) {
|
||||
return ((sz + (ALIGNMENT - 1)) / ALIGNMENT) * ALIGNMENT;
|
||||
}
|
||||
|
||||
typedef int stack_ptr;
|
||||
|
||||
struct stack {
|
||||
char* mem_end; // one-past-the-end of allocated region
|
||||
stack_ptr bound;
|
||||
stack_ptr limit; // 0 - stack is empty
|
||||
};
|
||||
|
||||
static void stack_init(struct stack* s) {
|
||||
s->mem_end = 0;
|
||||
s->bound = ALIGNMENT;
|
||||
s->limit = 0;
|
||||
}
|
||||
|
||||
static void stack_reset(struct stack* s) {
|
||||
assert(s->limit == 0 && "stack freed while not empty");
|
||||
char* mem_start = s->mem_end - ( -s->bound + ALIGNMENT);
|
||||
free(mem_start);
|
||||
stack_init(s);
|
||||
}
|
||||
|
||||
static int stack_pop_will_free(struct stack* s, stack_ptr p) {
|
||||
return p == s->limit;
|
||||
}
|
||||
|
||||
static void* stack_block(struct stack* s, stack_ptr p) {
|
||||
return (void*)(s->mem_end + p);
|
||||
}
|
||||
|
||||
static stack_ptr* stack_block_next(struct stack* s, stack_ptr p) {
|
||||
return &((stack_ptr*)stack_block(s, p))[-1];
|
||||
}
|
||||
|
||||
static void stack_reallocate(struct stack* s, size_t sz) {
|
||||
int old_mem_length = -(s->bound) + ALIGNMENT;
|
||||
char* old_mem_start = s->mem_end - old_mem_length;
|
||||
|
||||
int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2);
|
||||
char* new_mem_start = realloc(old_mem_start, new_mem_length);
|
||||
memmove(new_mem_start + (new_mem_length - old_mem_length),
|
||||
new_mem_start, old_mem_length);
|
||||
s->mem_end = new_mem_start + new_mem_length;
|
||||
s->bound = -(new_mem_length - ALIGNMENT);
|
||||
}
|
||||
|
||||
static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
stack_ptr r = s->limit - alloc_sz;
|
||||
if (r < s->bound) {
|
||||
stack_reallocate(s, alloc_sz);
|
||||
}
|
||||
s->limit = r;
|
||||
*stack_block_next(s, r) = p;
|
||||
return r;
|
||||
}
|
||||
|
||||
static stack_ptr stack_pop_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
stack_ptr r = *stack_block_next(s, p);
|
||||
if (p == s->limit) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
s->limit += alloc_sz;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
@@ -1,195 +1,80 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "exec_stack.h"
|
||||
#include "execute.h"
|
||||
|
||||
#include "opcode.h"
|
||||
#include "bytecode.h"
|
||||
|
||||
#include "forkable_stack.h"
|
||||
#include "frame_layout.h"
|
||||
|
||||
#include "jv_alloc.h"
|
||||
#include "jq_parser.h"
|
||||
#include "locfile.h"
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
#include "jv_aux.h"
|
||||
#include "parser.h"
|
||||
#include "builtin.h"
|
||||
|
||||
struct jq_state {
|
||||
void (*nomem_handler)(void *);
|
||||
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;
|
||||
stack_ptr fork_top;
|
||||
|
||||
struct forkable_stack data_stk;
|
||||
struct forkable_stack frame_stk;
|
||||
struct forkable_stack fork_stk;
|
||||
jv path;
|
||||
int subexp_nest;
|
||||
int debug_trace_enabled;
|
||||
int initial_execution;
|
||||
};
|
||||
|
||||
struct closure {
|
||||
struct bytecode* bc; // jq bytecode
|
||||
stack_ptr env; // jq stack address of closed frame
|
||||
};
|
||||
|
||||
// locals for any function called: either a closure or a local variable
|
||||
union frame_entry {
|
||||
struct closure closure;
|
||||
jv localvar;
|
||||
};
|
||||
|
||||
// jq function call frame
|
||||
struct frame {
|
||||
struct bytecode* bc; // jq bytecode for callee
|
||||
stack_ptr env; // jq stack address of frame to return to
|
||||
stack_ptr retdata; // jq stack address to unwind to on RET
|
||||
uint16_t* retaddr; // jq bytecode return address
|
||||
union frame_entry entries[0]; // nclosures + nlocals
|
||||
};
|
||||
|
||||
static int frame_size(struct bytecode* bc) {
|
||||
return sizeof(struct frame) + sizeof(union frame_entry) * (bc->nclosures + bc->nlocals);
|
||||
}
|
||||
|
||||
static struct frame* frame_current(struct jq_state* jq) {
|
||||
struct frame* fp = stack_block(&jq->stk, jq->curr_frame);
|
||||
|
||||
stack_ptr next = *stack_block_next(&jq->stk, jq->curr_frame);
|
||||
if (next) {
|
||||
struct frame* fpnext = stack_block(&jq->stk, next);
|
||||
struct bytecode* bc = fpnext->bc;
|
||||
assert(fp->retaddr >= bc->code && fp->retaddr < bc->code + bc->codelen);
|
||||
} else {
|
||||
assert(fp->retaddr == 0);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static stack_ptr frame_get_level(struct jq_state* jq, int level) {
|
||||
stack_ptr fr = jq->curr_frame;
|
||||
for (int i=0; i<level; i++) {
|
||||
struct frame* fp = stack_block(&jq->stk, fr);
|
||||
fr = fp->env;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
static jv* frame_local_var(struct jq_state* jq, int var, int level) {
|
||||
struct frame* fr = stack_block(&jq->stk, frame_get_level(jq, level));
|
||||
assert(var >= 0);
|
||||
assert(var < fr->bc->nlocals);
|
||||
return &fr->entries[fr->bc->nclosures + var].localvar;
|
||||
}
|
||||
|
||||
static struct closure make_closure(struct jq_state* jq, uint16_t* pc) {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t idx = *pc++;
|
||||
stack_ptr fridx = frame_get_level(jq, level);
|
||||
struct frame* fr = stack_block(&jq->stk, fridx);
|
||||
if (idx & ARG_NEWCLOSURE) {
|
||||
// A new closure closing the frame identified by level, and with
|
||||
// the bytecode body of the idx'th subfunction of that frame
|
||||
int subfn_idx = idx & ~ARG_NEWCLOSURE;
|
||||
assert(subfn_idx < fr->bc->nsubfunctions);
|
||||
struct closure cl = {fr->bc->subfunctions[subfn_idx],
|
||||
fridx};
|
||||
return cl;
|
||||
} else {
|
||||
// A reference to a closure from the frame identified by level; copy
|
||||
// it as-is
|
||||
int closure = idx;
|
||||
assert(closure >= 0);
|
||||
assert(closure < fr->bc->nclosures);
|
||||
return fr->entries[closure].closure;
|
||||
}
|
||||
}
|
||||
|
||||
static struct frame* frame_push(struct jq_state* jq, struct closure callee,
|
||||
uint16_t* argdef, int nargs) {
|
||||
stack_ptr new_frame_idx = stack_push_block(&jq->stk, jq->curr_frame, frame_size(callee.bc));
|
||||
struct frame* new_frame = stack_block(&jq->stk, new_frame_idx);
|
||||
new_frame->bc = callee.bc;
|
||||
new_frame->env = callee.env;
|
||||
assert(nargs == new_frame->bc->nclosures);
|
||||
union frame_entry* entries = new_frame->entries;
|
||||
for (int i=0; i<nargs; i++) {
|
||||
entries->closure = make_closure(jq, argdef + i * 2);
|
||||
entries++;
|
||||
}
|
||||
for (int i=0; i<callee.bc->nlocals; i++) {
|
||||
entries->localvar = jv_invalid();
|
||||
entries++;
|
||||
}
|
||||
jq->curr_frame = new_frame_idx;
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
static void frame_pop(struct jq_state* jq) {
|
||||
assert(jq->curr_frame);
|
||||
struct frame* fp = frame_current(jq);
|
||||
if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {
|
||||
int nlocals = fp->bc->nlocals;
|
||||
for (int i=0; i<nlocals; i++) {
|
||||
jv_free(*frame_local_var(jq, i, 0));
|
||||
}
|
||||
}
|
||||
jq->curr_frame = stack_pop_block(&jq->stk, jq->curr_frame, frame_size(fp->bc));
|
||||
}
|
||||
typedef struct {
|
||||
FORKABLE_STACK_HEADER;
|
||||
jv val;
|
||||
} data_stk_elem;
|
||||
|
||||
void stack_push(jq_state *jq, jv val) {
|
||||
assert(jv_is_valid(val));
|
||||
jq->stk_top = stack_push_block(&jq->stk, jq->stk_top, sizeof(jv));
|
||||
jv* sval = stack_block(&jq->stk, jq->stk_top);
|
||||
*sval = val;
|
||||
data_stk_elem* s = forkable_stack_push(&jq->data_stk, sizeof(data_stk_elem));
|
||||
s->val = val;
|
||||
}
|
||||
|
||||
jv stack_pop(jq_state *jq) {
|
||||
jv* sval = stack_block(&jq->stk, jq->stk_top);
|
||||
jv val = *sval;
|
||||
if (!stack_pop_will_free(&jq->stk, jq->stk_top)) {
|
||||
data_stk_elem* s = forkable_stack_peek(&jq->data_stk);
|
||||
jv val = s->val;
|
||||
if (!forkable_stack_pop_will_free(&jq->data_stk)) {
|
||||
val = jv_copy(val);
|
||||
}
|
||||
jq->stk_top = stack_pop_block(&jq->stk, jq->stk_top, sizeof(jv));
|
||||
forkable_stack_pop(&jq->data_stk);
|
||||
assert(jv_is_valid(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
struct forkpoint {
|
||||
stack_ptr saved_data_stack;
|
||||
stack_ptr saved_curr_frame;
|
||||
FORKABLE_STACK_HEADER;
|
||||
struct forkable_stack_state saved_data_stack;
|
||||
struct forkable_stack_state saved_call_stack;
|
||||
int path_len, subexp_nest;
|
||||
uint16_t* return_address;
|
||||
};
|
||||
|
||||
struct stack_pos {
|
||||
stack_ptr saved_data_stack, saved_curr_frame;
|
||||
};
|
||||
|
||||
struct stack_pos stack_get_pos(jq_state* jq) {
|
||||
struct stack_pos sp = {jq->stk_top, jq->curr_frame};
|
||||
return sp;
|
||||
}
|
||||
|
||||
void stack_save(jq_state *jq, uint16_t* retaddr, struct stack_pos sp){
|
||||
jq->fork_top = stack_push_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));
|
||||
struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);
|
||||
fork->saved_data_stack = jq->stk_top;
|
||||
fork->saved_curr_frame = jq->curr_frame;
|
||||
void stack_save(jq_state *jq, uint16_t* retaddr){
|
||||
struct forkpoint* fork = forkable_stack_push(&jq->fork_stk, sizeof(struct forkpoint));
|
||||
forkable_stack_save(&jq->data_stk, &fork->saved_data_stack);
|
||||
forkable_stack_save(&jq->frame_stk, &fork->saved_call_stack);
|
||||
fork->path_len =
|
||||
jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0;
|
||||
fork->subexp_nest = jq->subexp_nest;
|
||||
fork->return_address = retaddr;
|
||||
jq->stk_top = sp.saved_data_stack;
|
||||
jq->curr_frame = sp.saved_curr_frame;
|
||||
}
|
||||
|
||||
void stack_switch(jq_state *jq) {
|
||||
struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk);
|
||||
forkable_stack_switch(&jq->data_stk, &fork->saved_data_stack);
|
||||
forkable_stack_switch(&jq->frame_stk, &fork->saved_call_stack);
|
||||
}
|
||||
|
||||
void path_append(jq_state* jq, jv component) {
|
||||
@@ -204,24 +89,23 @@ void path_append(jq_state* jq, jv component) {
|
||||
}
|
||||
|
||||
uint16_t* stack_restore(jq_state *jq){
|
||||
while (!stack_pop_will_free(&jq->stk, jq->fork_top)) {
|
||||
if (stack_pop_will_free(&jq->stk, jq->stk_top)) {
|
||||
jv_free(stack_pop(jq));
|
||||
} else if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {
|
||||
frame_pop(jq);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
while (!forkable_stack_empty(&jq->data_stk) &&
|
||||
forkable_stack_pop_will_free(&jq->data_stk)) {
|
||||
jv_free(stack_pop(jq));
|
||||
}
|
||||
while (!forkable_stack_empty(&jq->frame_stk) &&
|
||||
forkable_stack_pop_will_free(&jq->frame_stk)) {
|
||||
frame_pop(&jq->frame_stk);
|
||||
}
|
||||
|
||||
if (jq->fork_top == 0) {
|
||||
if (forkable_stack_empty(&jq->fork_stk)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);
|
||||
struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk);
|
||||
uint16_t* retaddr = fork->return_address;
|
||||
jq->stk_top = fork->saved_data_stack;
|
||||
jq->curr_frame = fork->saved_curr_frame;
|
||||
forkable_stack_restore(&jq->data_stk, &fork->saved_data_stack);
|
||||
forkable_stack_restore(&jq->frame_stk, &fork->saved_call_stack);
|
||||
int path_len = fork->path_len;
|
||||
if (jv_get_kind(jq->path) == JV_KIND_ARRAY) {
|
||||
assert(path_len >= 0);
|
||||
@@ -230,49 +114,39 @@ uint16_t* stack_restore(jq_state *jq){
|
||||
assert(path_len == 0);
|
||||
}
|
||||
jq->subexp_nest = fork->subexp_nest;
|
||||
jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));
|
||||
forkable_stack_pop(&jq->fork_stk);
|
||||
return retaddr;
|
||||
}
|
||||
|
||||
static void jq_reset(jq_state *jq) {
|
||||
while (stack_restore(jq)) {}
|
||||
|
||||
assert(jq->stk_top == 0);
|
||||
assert(jq->fork_top == 0);
|
||||
assert(jq->curr_frame == 0);
|
||||
stack_reset(&jq->stk);
|
||||
|
||||
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
|
||||
jv_free(jq->path);
|
||||
jq->path = jv_null();
|
||||
jq->subexp_nest = 0;
|
||||
static struct closure make_closure(struct forkable_stack* stk, frame_ptr fr, uint16_t* pc) {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t idx = *pc++;
|
||||
fr = frame_get_level(stk, fr, level);
|
||||
if (idx & ARG_NEWCLOSURE) {
|
||||
int subfn_idx = idx & ~ARG_NEWCLOSURE;
|
||||
assert(subfn_idx < frame_self(fr)->bc->nsubfunctions);
|
||||
struct closure cl = {frame_self(fr)->bc->subfunctions[subfn_idx],
|
||||
forkable_stack_to_idx(stk, fr)};
|
||||
return cl;
|
||||
} else {
|
||||
return *frame_closure_arg(fr, idx);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_error(jq_state *jq, jv value) {
|
||||
void print_error(jv value) {
|
||||
assert(!jv_is_valid(value));
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
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>");
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
|
||||
}
|
||||
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)
|
||||
|
||||
jv jq_next(jq_state *jq) {
|
||||
jv cfunc_input[MAX_CFUNCTION_ARGS];
|
||||
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
|
||||
uint16_t* pc = stack_restore(jq);
|
||||
assert(pc);
|
||||
|
||||
@@ -282,33 +156,30 @@ jv jq_next(jq_state *jq) {
|
||||
uint16_t opcode = *pc;
|
||||
|
||||
if (jq->debug_trace_enabled) {
|
||||
dump_operation(frame_current(jq)->bc, pc);
|
||||
dump_operation(frame_current_bytecode(&jq->frame_stk), pc);
|
||||
printf("\t");
|
||||
const struct opcode_description* opdesc = opcode_describe(opcode);
|
||||
stack_ptr param = 0;
|
||||
if (!backtracking) {
|
||||
int stack_in = opdesc->stack_in;
|
||||
if (stack_in == -1) stack_in = pc[1];
|
||||
for (int i=0; i<stack_in; i++) {
|
||||
if (i == 0) {
|
||||
param = jq->stk_top;
|
||||
} else {
|
||||
printf(" | ");
|
||||
param = *stack_block_next(&jq->stk, param);
|
||||
}
|
||||
if (!param) break;
|
||||
jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), 0);
|
||||
//printf("<%d>", jv_get_refcnt(param->val));
|
||||
//printf(" -- ");
|
||||
//jv_dump(jv_copy(jq->path), 0);
|
||||
data_stk_elem* param = 0;
|
||||
int stack_in = opdesc->stack_in;
|
||||
if (stack_in == -1) stack_in = pc[1];
|
||||
for (int i=0; i<stack_in; i++) {
|
||||
if (i == 0) {
|
||||
param = forkable_stack_peek(&jq->data_stk);
|
||||
} else {
|
||||
printf(" | ");
|
||||
param = forkable_stack_peek_next(&jq->data_stk, param);
|
||||
}
|
||||
} else {
|
||||
printf("\t<backtracking>");
|
||||
if (!param) break;
|
||||
jv_dump(jv_copy(param->val), 0);
|
||||
//printf("<%d>", jv_get_refcnt(param->val));
|
||||
//printf(" -- ");
|
||||
//jv_dump(jv_copy(jq->path), 0);
|
||||
}
|
||||
|
||||
if (backtracking) printf("\t<backtracking>");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (backtracking) {
|
||||
opcode = ON_BACKTRACK(opcode);
|
||||
backtracking = 0;
|
||||
@@ -319,7 +190,7 @@ jv jq_next(jq_state *jq) {
|
||||
default: assert(0 && "invalid instruction");
|
||||
|
||||
case LOADK: {
|
||||
jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);
|
||||
jv v = jv_array_get(jv_copy(frame_current_bytecode(&jq->frame_stk)->constants), *pc++);
|
||||
assert(jv_is_valid(v));
|
||||
jv_free(stack_pop(jq));
|
||||
stack_push(jq, v);
|
||||
@@ -369,7 +240,8 @@ jv jq_next(jq_state *jq) {
|
||||
jv v = stack_pop(jq);
|
||||
uint16_t level = *pc++;
|
||||
uint16_t vidx = *pc++;
|
||||
jv* var = frame_local_var(jq, vidx, level);
|
||||
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
|
||||
jv* var = frame_local_var(fp, vidx);
|
||||
assert(jv_get_kind(*var) == JV_KIND_ARRAY);
|
||||
*var = jv_array_append(*var, v);
|
||||
break;
|
||||
@@ -385,8 +257,8 @@ jv jq_next(jq_state *jq) {
|
||||
stack_push(jq, jv_object_set(objv, k, v));
|
||||
stack_push(jq, stktop);
|
||||
} else {
|
||||
print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
||||
jv_kind_name(jv_get_kind(k)))));
|
||||
print_error(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);
|
||||
@@ -400,11 +272,12 @@ jv jq_next(jq_state *jq) {
|
||||
case RANGE: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
|
||||
jv* var = frame_local_var(fp, v);
|
||||
jv max = stack_pop(jq);
|
||||
if (jv_get_kind(*var) != JV_KIND_NUMBER ||
|
||||
jv_get_kind(max) != JV_KIND_NUMBER) {
|
||||
print_error(jq, jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric")));
|
||||
print_error(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))) {
|
||||
@@ -414,10 +287,9 @@ jv jq_next(jq_state *jq) {
|
||||
jv curr = jv_copy(*var);
|
||||
*var = jv_number(jv_number_value(*var) + 1);
|
||||
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_save(jq, pc - 3);
|
||||
stack_push(jq, jv_copy(max));
|
||||
stack_save(jq, pc - 3, spos);
|
||||
|
||||
stack_switch(jq);
|
||||
stack_push(jq, curr);
|
||||
}
|
||||
break;
|
||||
@@ -427,7 +299,8 @@ jv jq_next(jq_state *jq) {
|
||||
case LOADV: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
|
||||
jv* var = frame_local_var(fp, v);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
jv_dump(jv_copy(*var), 0);
|
||||
@@ -442,7 +315,8 @@ jv jq_next(jq_state *jq) {
|
||||
case LOADVN: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
|
||||
jv* var = frame_local_var(fp, v);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
jv_dump(jv_copy(*var), 0);
|
||||
@@ -457,7 +331,8 @@ jv jq_next(jq_state *jq) {
|
||||
case STOREV: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
|
||||
jv* var = frame_local_var(fp, v);
|
||||
jv val = stack_pop(jq);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
@@ -473,7 +348,8 @@ jv jq_next(jq_state *jq) {
|
||||
jv v = stack_pop(jq);
|
||||
stack_push(jq, jq->path);
|
||||
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
stack_save(jq, pc - 1);
|
||||
stack_switch(jq);
|
||||
|
||||
stack_push(jq, jv_number(jq->subexp_nest));
|
||||
stack_push(jq, v);
|
||||
@@ -492,9 +368,9 @@ jv jq_next(jq_state *jq) {
|
||||
jv path = jq->path;
|
||||
jq->path = stack_pop(jq);
|
||||
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_save(jq, pc - 1);
|
||||
stack_push(jq, jv_copy(path));
|
||||
stack_save(jq, pc - 1, spos);
|
||||
stack_switch(jq);
|
||||
|
||||
stack_push(jq, path);
|
||||
jq->subexp_nest = old_subexp_nest;
|
||||
@@ -508,8 +384,7 @@ jv jq_next(jq_state *jq) {
|
||||
goto do_backtrack;
|
||||
}
|
||||
|
||||
case INDEX:
|
||||
case INDEX_OPT: {
|
||||
case INDEX: {
|
||||
jv t = stack_pop(jq);
|
||||
jv k = stack_pop(jq);
|
||||
path_append(jq, jv_copy(k));
|
||||
@@ -517,10 +392,7 @@ jv jq_next(jq_state *jq) {
|
||||
if (jv_is_valid(v)) {
|
||||
stack_push(jq, v);
|
||||
} else {
|
||||
if (opcode == INDEX)
|
||||
print_error(jq, v);
|
||||
else
|
||||
jv_free(v);
|
||||
print_error(v);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
@@ -545,18 +417,16 @@ 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_OPT): {
|
||||
case ON_BACKTRACK(EACH): {
|
||||
int idx = jv_number_value(stack_pop(jq));
|
||||
jv container = stack_pop(jq);
|
||||
|
||||
int keep_going, is_last = 0;
|
||||
jv key, value;
|
||||
if (jv_get_kind(container) == JV_KIND_ARRAY) {
|
||||
if (opcode == EACH || opcode == EACH_OPT) idx = 0;
|
||||
if (opcode == EACH) idx = 0;
|
||||
else idx = idx + 1;
|
||||
int len = jv_array_length(jv_copy(container));
|
||||
keep_going = idx < len;
|
||||
@@ -566,7 +436,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 || opcode == EACH_OPT) idx = jv_object_iter(container);
|
||||
if (opcode == EACH) idx = jv_object_iter(container);
|
||||
else idx = jv_object_iter_next(container, idx);
|
||||
keep_going = jv_object_iter_valid(container, idx);
|
||||
if (keep_going) {
|
||||
@@ -574,12 +444,9 @@ jv jq_next(jq_state *jq) {
|
||||
value = jv_object_iter_value(container, idx);
|
||||
}
|
||||
} else {
|
||||
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)))));
|
||||
}
|
||||
assert(opcode == EACH);
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
||||
jv_kind_name(jv_get_kind(container)))));
|
||||
keep_going = 0;
|
||||
}
|
||||
|
||||
@@ -592,10 +459,10 @@ jv jq_next(jq_state *jq) {
|
||||
path_append(jq, key);
|
||||
stack_push(jq, value);
|
||||
} else {
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_save(jq, pc - 1);
|
||||
stack_push(jq, container);
|
||||
stack_push(jq, jv_number(idx));
|
||||
stack_save(jq, pc - 1, spos);
|
||||
stack_switch(jq);
|
||||
path_append(jq, key);
|
||||
stack_push(jq, value);
|
||||
}
|
||||
@@ -613,7 +480,8 @@ jv jq_next(jq_state *jq) {
|
||||
}
|
||||
|
||||
case FORK: {
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
stack_save(jq, pc - 1);
|
||||
stack_switch(jq);
|
||||
pc++; // skip offset this time
|
||||
break;
|
||||
}
|
||||
@@ -627,96 +495,53 @@ jv jq_next(jq_state *jq) {
|
||||
case CALL_BUILTIN: {
|
||||
int nargs = *pc++;
|
||||
jv top = stack_pop(jq);
|
||||
jv* in = cfunc_input;
|
||||
in[0] = top;
|
||||
cfunc_input[0] = top;
|
||||
for (int i = 1; i < nargs; i++) {
|
||||
in[i] = stack_pop(jq);
|
||||
cfunc_input[i] = stack_pop(jq);
|
||||
}
|
||||
struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++];
|
||||
typedef jv (*func_1)(jv);
|
||||
typedef jv (*func_2)(jv,jv);
|
||||
typedef jv (*func_3)(jv,jv,jv);
|
||||
typedef jv (*func_4)(jv,jv,jv,jv);
|
||||
typedef jv (*func_5)(jv,jv,jv,jv,jv);
|
||||
switch (function->nargs) {
|
||||
case 1: top = ((func_1)function->fptr)(in[0]); break;
|
||||
case 2: top = ((func_2)function->fptr)(in[0], in[1]); break;
|
||||
case 3: top = ((func_3)function->fptr)(in[0], in[1], in[2]); break;
|
||||
case 4: top = ((func_4)function->fptr)(in[0], in[1], in[2], in[3]); break;
|
||||
case 5: top = ((func_5)function->fptr)(in[0], in[1], in[2], in[3], in[4]); break;
|
||||
default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));
|
||||
}
|
||||
|
||||
struct cfunction* func = &frame_current_bytecode(&jq->frame_stk)->globals->cfunctions[*pc++];
|
||||
top = cfunction_invoke(func, cfunc_input);
|
||||
if (jv_is_valid(top)) {
|
||||
stack_push(jq, top);
|
||||
} else {
|
||||
print_error(jq, top);
|
||||
print_error(top);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TAIL_CALL_JQ:
|
||||
case CALL_JQ: {
|
||||
/*
|
||||
* Bytecode layout here:
|
||||
*
|
||||
* CALL_JQ
|
||||
* <nclosures> (i.e., number of call arguments)
|
||||
* <callee closure> (what we're calling)
|
||||
* <nclosures' worth of closures> (frame reference + code pointer)
|
||||
*
|
||||
* <next instruction (to return to)>
|
||||
*
|
||||
* Each closure consists of two uint16_t values: a "level"
|
||||
* identifying the frame to be closed over, and an index.
|
||||
*
|
||||
* The level is a relative number of call frames reachable from
|
||||
* the currently one; 0 -> current frame, 1 -> previous frame, and
|
||||
* so on.
|
||||
*
|
||||
* The index is either an index of the closed frame's subfunctions
|
||||
* or of the closed frame's parameter closures. If the latter,
|
||||
* that closure will be passed, else the closed frame's pointer
|
||||
* and the subfunction's code will form the closure to be passed.
|
||||
*
|
||||
* See make_closure() for more information.
|
||||
*/
|
||||
jv input = stack_pop(jq);
|
||||
uint16_t nclosures = *pc++;
|
||||
uint16_t* retaddr = pc + 2 + nclosures*2;
|
||||
stack_ptr retdata = jq->stk_top;
|
||||
struct frame* new_frame;
|
||||
struct closure cl = make_closure(jq, pc);
|
||||
if (opcode == TAIL_CALL_JQ) {
|
||||
retaddr = frame_current(jq)->retaddr;
|
||||
retdata = frame_current(jq)->retdata;
|
||||
frame_pop(jq);
|
||||
frame_ptr new_frame = frame_push(&jq->frame_stk,
|
||||
make_closure(&jq->frame_stk, frame_current(&jq->frame_stk), pc),
|
||||
retaddr);
|
||||
pc += 2;
|
||||
frame_ptr old_frame = forkable_stack_peek_next(&jq->frame_stk, new_frame);
|
||||
assert(nclosures == frame_self(new_frame)->bc->nclosures);
|
||||
for (int i=0; i<nclosures; i++) {
|
||||
*frame_closure_arg(new_frame, i) = make_closure(&jq->frame_stk, old_frame, pc);
|
||||
pc += 2;
|
||||
}
|
||||
new_frame = frame_push(jq, cl, pc + 2, nclosures);
|
||||
new_frame->retdata = retdata;
|
||||
new_frame->retaddr = retaddr;
|
||||
pc = new_frame->bc->code;
|
||||
stack_push(jq, input);
|
||||
|
||||
pc = frame_current_bytecode(&jq->frame_stk)->code;
|
||||
break;
|
||||
}
|
||||
|
||||
case RET: {
|
||||
jv value = stack_pop(jq);
|
||||
assert(jq->stk_top == frame_current(jq)->retdata);
|
||||
uint16_t* retaddr = frame_current(jq)->retaddr;
|
||||
uint16_t* retaddr = *frame_current_retaddr(&jq->frame_stk);
|
||||
if (retaddr) {
|
||||
// function return
|
||||
pc = retaddr;
|
||||
frame_pop(jq);
|
||||
frame_pop(&jq->frame_stk);
|
||||
} else {
|
||||
// top-level return, yielding value
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
jv value = stack_pop(jq);
|
||||
stack_save(jq, pc - 1);
|
||||
stack_push(jq, jv_null());
|
||||
stack_save(jq, pc - 1, spos);
|
||||
stack_switch(jq);
|
||||
return value;
|
||||
}
|
||||
stack_push(jq, value);
|
||||
break;
|
||||
}
|
||||
case ON_BACKTRACK(RET): {
|
||||
@@ -727,60 +552,28 @@ jv jq_next(jq_state *jq) {
|
||||
}
|
||||
}
|
||||
|
||||
jq_state *jq_init(void) {
|
||||
jq_state *jq;
|
||||
jq = jv_mem_alloc_unguarded(sizeof(*jq));
|
||||
if (jq == NULL)
|
||||
return NULL;
|
||||
|
||||
jq->bc = 0;
|
||||
|
||||
stack_init(&jq->stk);
|
||||
jq->stk_top = 0;
|
||||
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;
|
||||
jq->nomem_handler_data = data;
|
||||
}
|
||||
|
||||
|
||||
void jq_start(jq_state *jq, jv input, int flags) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
jq_reset(jq);
|
||||
void jq_init(struct bytecode* bc, jv input, jq_state **jq, int flags) {
|
||||
jq_state *new_jq;
|
||||
new_jq = jv_mem_alloc(sizeof(*new_jq));
|
||||
memset(new_jq, 0, sizeof(*new_jq));
|
||||
new_jq->path = jv_null();
|
||||
forkable_stack_init(&new_jq->data_stk, sizeof(data_stk_elem) * 100);
|
||||
forkable_stack_init(&new_jq->frame_stk, 1024);
|
||||
forkable_stack_init(&new_jq->fork_stk, 1024);
|
||||
|
||||
struct closure top = {jq->bc, -1};
|
||||
struct frame* top_frame = frame_push(jq, top, 0, 0);
|
||||
top_frame->retdata = 0;
|
||||
top_frame->retaddr = 0;
|
||||
|
||||
stack_push(jq, input);
|
||||
stack_save(jq, jq->bc->code, stack_get_pos(jq));
|
||||
stack_push(new_jq, input);
|
||||
struct closure top = {bc, -1};
|
||||
frame_push(&new_jq->frame_stk, top, 0);
|
||||
stack_save(new_jq, bc->code);
|
||||
stack_switch(new_jq);
|
||||
if (flags & JQ_DEBUG_TRACE) {
|
||||
jq->debug_trace_enabled = 1;
|
||||
new_jq->debug_trace_enabled = 1;
|
||||
} else {
|
||||
jq->debug_trace_enabled = 0;
|
||||
new_jq->debug_trace_enabled = 0;
|
||||
}
|
||||
jq->initial_execution = 1;
|
||||
new_jq->initial_execution = 1;
|
||||
*jq = new_jq;
|
||||
}
|
||||
|
||||
void jq_teardown(jq_state **jq) {
|
||||
@@ -789,24 +582,25 @@ void jq_teardown(jq_state **jq) {
|
||||
return;
|
||||
*jq = NULL;
|
||||
|
||||
jq_reset(old_jq);
|
||||
bytecode_free(old_jq->bc);
|
||||
old_jq->bc = 0;
|
||||
while (stack_restore(old_jq)) {}
|
||||
|
||||
assert(forkable_stack_empty(&old_jq->fork_stk));
|
||||
assert(forkable_stack_empty(&old_jq->data_stk));
|
||||
assert(forkable_stack_empty(&old_jq->frame_stk));
|
||||
forkable_stack_free(&old_jq->fork_stk);
|
||||
forkable_stack_free(&old_jq->data_stk);
|
||||
forkable_stack_free(&old_jq->frame_stk);
|
||||
|
||||
jv_free(old_jq->path);
|
||||
jv_mem_free(old_jq);
|
||||
}
|
||||
|
||||
int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
struct bytecode* jq_compile_args(const char* str, jv args) {
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||
struct locfile locations;
|
||||
locfile_init(&locations, jq, str, strlen(str));
|
||||
locfile_init(&locations, str, strlen(str));
|
||||
block program;
|
||||
jq_reset(jq);
|
||||
if (jq->bc) {
|
||||
bytecode_free(jq->bc);
|
||||
jq->bc = 0;
|
||||
}
|
||||
struct bytecode* bc = 0;
|
||||
int nerrors = jq_parse(&locations, &program);
|
||||
if (nerrors == 0) {
|
||||
for (int i=0; i<jv_array_length(jv_copy(args)); i++) {
|
||||
@@ -816,31 +610,17 @@ 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);
|
||||
}
|
||||
nerrors = builtins_bind(jq, &program);
|
||||
if (nerrors == 0) {
|
||||
nerrors = block_compile(program, &locations, &jq->bc);
|
||||
}
|
||||
jv_free(args);
|
||||
program = builtins_bind(program);
|
||||
nerrors = block_compile(program, &locations, &bc);
|
||||
}
|
||||
jv_free(args);
|
||||
if (nerrors) {
|
||||
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);
|
||||
fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error");
|
||||
}
|
||||
locfile_free(&locations);
|
||||
return jq->bc != NULL;
|
||||
return bc;
|
||||
}
|
||||
|
||||
int jq_compile(jq_state *jq, const char* str) {
|
||||
return jq_compile_args(jq, str, jv_array());
|
||||
}
|
||||
|
||||
void jq_dump_disassembly(jq_state *jq, int indent) {
|
||||
dump_disassembly(indent, jq->bc);
|
||||
struct bytecode* jq_compile(const char* str) {
|
||||
return jq_compile_args(str, jv_array());
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
#include "bytecode.h"
|
||||
|
||||
|
||||
|
||||
struct bytecode* jq_compile(const char* str);
|
||||
|
||||
/* args must be an array of the form [{name:"foo", value:"thing"}, {name:"bar",value:3}] */
|
||||
struct bytecode* jq_compile_args(const char* str, jv args);
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
void jq_init(struct bytecode* bc, jv value, jq_state **, int flags);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(jq_state **);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef FORKABLE_STACK_H
|
||||
#define FORKABLE_STACK_H
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct forkable_stack_header {
|
||||
/* distance in bytes between this header and the header
|
||||
of the next object on the stack */
|
||||
int next_delta;
|
||||
};
|
||||
|
||||
#define FORKABLE_STACK_HEADER struct forkable_stack_header fk_header_
|
||||
|
||||
struct forkable_stack {
|
||||
// Stack grows down from stk+length
|
||||
char* stk;
|
||||
|
||||
// stk+length is just past end of allocated area
|
||||
// stk = jv_mem_alloc(length)
|
||||
int length;
|
||||
|
||||
// stk+pos is header of top object, or stk+length if empty
|
||||
int pos;
|
||||
|
||||
// everything after-or-including stk+savedlimit must be preserved
|
||||
int savedlimit;
|
||||
};
|
||||
|
||||
static void forkable_stack_check(struct forkable_stack* s) {
|
||||
assert(s->stk);
|
||||
assert(s->length > 0);
|
||||
assert(s->pos >= 0 && s->pos <= s->length);
|
||||
assert(s->savedlimit >= 0 && s->savedlimit <= s->length);
|
||||
}
|
||||
|
||||
static int forkable_stack_empty(struct forkable_stack* s) {
|
||||
return s->pos == s->length;
|
||||
}
|
||||
|
||||
static void forkable_stack_init(struct forkable_stack* s, size_t sz) {
|
||||
s->stk = jv_mem_alloc(sz);
|
||||
s->length = sz;
|
||||
s->pos = s->length;
|
||||
s->savedlimit = s->length;
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
static void forkable_stack_free(struct forkable_stack* s) {
|
||||
assert(forkable_stack_empty(s));
|
||||
assert(s->savedlimit == s->length);
|
||||
jv_mem_free(s->stk);
|
||||
s->stk = 0;
|
||||
}
|
||||
|
||||
static void* forkable_stack_push(struct forkable_stack* s, size_t sz_size) {
|
||||
assert(sz_size > 0 && sz_size % sizeof(struct forkable_stack_header) == 0);
|
||||
int size = (int)sz_size;
|
||||
forkable_stack_check(s);
|
||||
int curr = s->pos < s->savedlimit ? s->pos : s->savedlimit;
|
||||
if (curr - size < 0) {
|
||||
int oldlen = s->length;
|
||||
s->length = (size + oldlen + 1024) * 2;
|
||||
s->stk = jv_mem_realloc(s->stk, s->length);
|
||||
int shift = s->length - oldlen;
|
||||
memmove(s->stk + shift, s->stk, oldlen);
|
||||
s->pos += shift;
|
||||
s->savedlimit += shift;
|
||||
curr += shift;
|
||||
}
|
||||
int newpos = curr - size;
|
||||
assert(newpos < s->pos);
|
||||
void* ret = (void*)(s->stk + newpos);
|
||||
((struct forkable_stack_header*)ret)->next_delta = s->pos - newpos;
|
||||
s->pos = newpos;
|
||||
forkable_stack_check(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* forkable_stack_peek(struct forkable_stack* s) {
|
||||
assert(!forkable_stack_empty(s));
|
||||
forkable_stack_check(s);
|
||||
return (void*)(s->stk + s->pos);
|
||||
}
|
||||
|
||||
static void* forkable_stack_peek_next(struct forkable_stack* s, void* top) {
|
||||
forkable_stack_check(s);
|
||||
struct forkable_stack_header* elem = top;
|
||||
int elempos = (char*)top - s->stk;
|
||||
assert(elempos >= 0 && elempos < s->length);
|
||||
assert(elempos + elem->next_delta <= s->length);
|
||||
if (elempos + elem->next_delta < s->length) {
|
||||
return (void*)(s->stk + elempos + elem->next_delta);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns 1 if the next forkable_stack_pop will permanently remove an
|
||||
// object from the stack (i.e. the top object was not saved with a fork)
|
||||
static int forkable_stack_pop_will_free(struct forkable_stack* s) {
|
||||
return s->pos < s->savedlimit;
|
||||
}
|
||||
|
||||
static void forkable_stack_pop(struct forkable_stack* s) {
|
||||
forkable_stack_check(s);
|
||||
struct forkable_stack_header* elem = forkable_stack_peek(s);
|
||||
int reclaim_upto = s->pos + elem->next_delta < s->savedlimit ?
|
||||
s->pos + elem->next_delta : s->savedlimit;
|
||||
assert(reclaim_upto <= s->length);
|
||||
int reclaimed = reclaim_upto > s->pos ? reclaim_upto - s->pos : 0;
|
||||
assert(0 <= reclaimed && s->pos + reclaimed <= s->length);
|
||||
// s->pos <= i < reclaim_upto means that position i is to be freed
|
||||
assert((reclaimed > 0) == forkable_stack_pop_will_free(s));
|
||||
int new_pos = s->pos + elem->next_delta;
|
||||
jv_mem_invalidate(elem, reclaimed);
|
||||
s->pos = new_pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct forkable_stack_state {
|
||||
// We save the previous pos, savedlimit as
|
||||
// length-pos, length-savedlimit since these
|
||||
// values are stable across stack reallocations.
|
||||
int prev_pos_delta, prev_limit_delta;
|
||||
};
|
||||
|
||||
static void forkable_stack_save(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
state->prev_pos_delta = s->length - s->pos;
|
||||
state->prev_limit_delta = s->length - s->savedlimit;
|
||||
if (s->pos < s->savedlimit) s->savedlimit = s->pos;
|
||||
}
|
||||
|
||||
static void forkable_stack_switch(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
int curr_pos = s->pos;
|
||||
s->pos = s->length - state->prev_pos_delta;
|
||||
state->prev_pos_delta = s->length - curr_pos;
|
||||
|
||||
int curr_limit = s->savedlimit;
|
||||
if (curr_pos < curr_limit) s->savedlimit = curr_pos;
|
||||
//state->prevlimit = curr_limit; FIXME clean up
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
static void forkable_stack_restore(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
assert(s->savedlimit <= s->length - state->prev_pos_delta);
|
||||
assert(s->savedlimit <= s->length - state->prev_limit_delta);
|
||||
s->pos = s->length - state->prev_pos_delta;
|
||||
s->savedlimit = s->length - state->prev_limit_delta;
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
typedef int stack_idx;
|
||||
|
||||
static stack_idx forkable_stack_to_idx(struct forkable_stack* s, void* ptr) {
|
||||
char* item = ptr;
|
||||
int pos = item - s->stk;
|
||||
assert(pos >= 0 && pos < s->length);
|
||||
return s->length - pos;
|
||||
}
|
||||
|
||||
static void* forkable_stack_from_idx(struct forkable_stack* s, stack_idx idx) {
|
||||
assert(idx >= 1 && idx <= s->length);
|
||||
return &s->stk[s->length - idx];
|
||||
}
|
||||
|
||||
#endif
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
#ifndef FRAME_LAYOUT_H
|
||||
#include "forkable_stack.h"
|
||||
#include "bytecode.h"
|
||||
|
||||
struct closure {
|
||||
struct bytecode* bc;
|
||||
stack_idx env;
|
||||
};
|
||||
|
||||
struct continuation {
|
||||
struct bytecode* bc;
|
||||
stack_idx env;
|
||||
uint16_t* retaddr;
|
||||
};
|
||||
|
||||
typedef union frame_elem {
|
||||
FORKABLE_STACK_HEADER;
|
||||
struct continuation cont;
|
||||
struct closure closure;
|
||||
jv jsonval;
|
||||
} *frame_ptr;
|
||||
|
||||
/*
|
||||
* Frame layout
|
||||
* fr[0] - FORKABLE_STACK_HEADER (next pointer)
|
||||
* fr[1] - self (used to store return addresses, etc)
|
||||
* fr[2...nclosures+2] - closure params
|
||||
* fr[nclosures+2..nclosures+nlocals+2] - local variables
|
||||
*/
|
||||
|
||||
static int frame_size(struct bytecode* bc) {
|
||||
return sizeof(union frame_elem) * (bc->nclosures + bc->nlocals + 2);
|
||||
}
|
||||
|
||||
static struct continuation* frame_self(frame_ptr fr) {
|
||||
return &fr[1].cont;
|
||||
}
|
||||
|
||||
static struct closure* frame_closure_arg(frame_ptr fr, int closure) {
|
||||
assert(closure >= 0);
|
||||
assert(closure < frame_self(fr)->bc->nclosures);
|
||||
return &fr[2+closure].closure;
|
||||
}
|
||||
|
||||
static jv* frame_local_var(frame_ptr fr, int var) {
|
||||
assert(var >= 0);
|
||||
assert(var < frame_self(fr)->bc->nlocals);
|
||||
return &fr[2 + frame_self(fr)->bc->nclosures + var].jsonval;
|
||||
}
|
||||
|
||||
|
||||
static frame_ptr frame_current(struct forkable_stack* stk) {
|
||||
frame_ptr fp = forkable_stack_peek(stk);
|
||||
if (forkable_stack_peek_next(stk, fp)) {
|
||||
struct bytecode* bc = frame_self(forkable_stack_peek_next(stk, fp))->bc;
|
||||
assert(frame_self(fp)->retaddr >= bc->code && frame_self(fp)->retaddr < bc->code + bc->codelen);
|
||||
} else {
|
||||
assert(frame_self(fp)->retaddr == 0);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static struct bytecode* frame_current_bytecode(struct forkable_stack* stk) {
|
||||
return frame_self(frame_current(stk))->bc;
|
||||
}
|
||||
static uint16_t** frame_current_retaddr(struct forkable_stack* stk) {
|
||||
return &frame_self(frame_current(stk))->retaddr;
|
||||
}
|
||||
|
||||
static frame_ptr frame_get_parent(struct forkable_stack* stk, frame_ptr fr) {
|
||||
return forkable_stack_from_idx(stk, frame_self(fr)->env);
|
||||
}
|
||||
|
||||
static frame_ptr frame_get_level(struct forkable_stack* stk, frame_ptr fr, int level) {
|
||||
for (int i=0; i<level; i++) {
|
||||
fr = frame_get_parent(stk, fr);
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
static frame_ptr frame_push(struct forkable_stack* stk, struct closure cl, uint16_t* retaddr) {
|
||||
frame_ptr fp = forkable_stack_push(stk, frame_size(cl.bc));
|
||||
struct continuation* cc = frame_self(fp);
|
||||
cc->bc = cl.bc;
|
||||
cc->env = cl.env;
|
||||
cc->retaddr = retaddr;
|
||||
for (int i=0; i<cl.bc->nlocals; i++) {
|
||||
*frame_local_var(fp, i) = jv_invalid();
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void frame_pop(struct forkable_stack* stk) {
|
||||
frame_ptr fp = frame_current(stk);
|
||||
if (forkable_stack_pop_will_free(stk)) {
|
||||
int nlocals = frame_self(fp)->bc->nlocals;
|
||||
for (int i=0; i<nlocals; i++) {
|
||||
jv_free(*frame_local_var(fp, i));
|
||||
}
|
||||
}
|
||||
forkable_stack_pop(stk);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -6,12 +6,13 @@ 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 < len(t) else "};"))
|
||||
("," if i + 16 < 256 else "};"))
|
||||
|
||||
|
||||
def utf8info(c):
|
||||
@@ -29,4 +30,3 @@ 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])
|
||||
-2012
File diff suppressed because it is too large.
Load diff
@@ -1,23 +0,0 @@
|
||||
#ifndef _JQ_H_
|
||||
#define _JQ_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jv.h>
|
||||
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
typedef void (*jq_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);
|
||||
void jq_dump_disassembly(jq_state *, int);
|
||||
void jq_start(jq_state *, jv value, int flags);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(jq_state **);
|
||||
|
||||
#endif /* !_JQ_H_ */
|
||||
@@ -52,16 +52,11 @@ 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
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#ifndef JQ_PARSER_H
|
||||
#define JQ_PARSER_H
|
||||
#include "locfile.h"
|
||||
#include "compile.h"
|
||||
|
||||
int jq_parse(struct locfile* source, block* answer);
|
||||
int jq_parse_library(struct locfile* locations, block* answer);
|
||||
|
||||
@@ -3,23 +3,15 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
#include "execute.h"
|
||||
|
||||
static void jv_test();
|
||||
static void run_jq_tests();
|
||||
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]) {
|
||||
FILE *testdata = stdin;
|
||||
jv_test();
|
||||
if (argc > 2) {
|
||||
testdata = fopen(argv[2], "r");
|
||||
if (!testdata) {
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
run_jq_tests(testdata);
|
||||
run_jq_tests(stdin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -35,9 +27,6 @@ static void run_jq_tests(FILE *testdata) {
|
||||
int tests = 0, passed = 0, invalid = 0;
|
||||
jq_state *jq = NULL;
|
||||
|
||||
jq = jq_init();
|
||||
assert(jq);
|
||||
|
||||
while (1) {
|
||||
if (!fgets(buf, sizeof(buf), testdata)) break;
|
||||
if (skipline(buf)) continue;
|
||||
@@ -45,15 +34,15 @@ static void run_jq_tests(FILE *testdata) {
|
||||
printf("Testing %s\n", buf);
|
||||
int pass = 1;
|
||||
tests++;
|
||||
int compiled = jq_compile(jq, buf);
|
||||
if (!compiled) {invalid++; continue;}
|
||||
struct bytecode* bc = jq_compile(buf);
|
||||
if (!bc) {invalid++; continue;}
|
||||
printf("Disassembly:\n");
|
||||
jq_dump_disassembly(jq, 2);
|
||||
dump_disassembly(2, bc);
|
||||
printf("\n");
|
||||
if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; }
|
||||
fgets(buf, sizeof(buf), testdata);
|
||||
jv input = jv_parse(buf);
|
||||
if (!jv_is_valid(input)){ invalid++; continue; }
|
||||
jq_start(jq, input, JQ_DEBUG_TRACE);
|
||||
jq_init(bc, input, &jq, JQ_DEBUG_TRACE);
|
||||
|
||||
while (fgets(buf, sizeof(buf), testdata)) {
|
||||
if (skipline(buf)) break;
|
||||
@@ -92,9 +81,10 @@ static void run_jq_tests(FILE *testdata) {
|
||||
jv_free(extra);
|
||||
}
|
||||
}
|
||||
jq_teardown(&jq);
|
||||
bytecode_free(bc);
|
||||
passed+=pass;
|
||||
}
|
||||
jq_teardown(&jq);
|
||||
printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid);
|
||||
if (passed != tests) exit(1);
|
||||
}
|
||||
@@ -125,9 +115,9 @@ static void jv_test() {
|
||||
jv_free(a2);
|
||||
|
||||
|
||||
assert(jv_get_refcnt(a) == 1);
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
a = jv_array_append(a, jv_copy(a));
|
||||
assert(jv_get_refcnt(a) == 1);
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
@@ -160,9 +150,9 @@ static void jv_test() {
|
||||
|
||||
jv_free(subarray);
|
||||
|
||||
void* before = sub2.u.ptr;
|
||||
void* before = sub2.val.nontrivial.ptr;
|
||||
sub2 = jv_array_append(sub2, jv_number(200));
|
||||
void* after = sub2.u.ptr;
|
||||
void* after = sub2.val.nontrivial.ptr;
|
||||
assert(before == after);
|
||||
jv_free(sub2);
|
||||
|
||||
@@ -182,10 +172,8 @@ 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(jv_copy(a4)) == 2);
|
||||
assert(jv_array_length(jv_copy(a5)) == 2);
|
||||
jv_free(a4);
|
||||
jv_free(a5);
|
||||
assert(jv_array_length(a4) == 2);
|
||||
assert(jv_array_length(a5) == 2);
|
||||
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
@@ -203,10 +191,8 @@ 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(jv_copy(shortstr)) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(jv_copy(longstr)) == (int)sizeof(nasty));
|
||||
jv_free(shortstr);
|
||||
jv_free(longstr);
|
||||
assert(jv_string_length_bytes(shortstr) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(longstr) == (int)sizeof(nasty));
|
||||
|
||||
|
||||
char a1s[] = "hello", a2s[] = "hello", bs[] = "goodbye";
|
||||
@@ -252,11 +238,4 @@ static void jv_test() {
|
||||
|
||||
jv_free(o2);
|
||||
}
|
||||
|
||||
/// Compile errors
|
||||
{
|
||||
jq_state *jq = jq_init();
|
||||
jq_compile_args(jq, "}", jv_array());
|
||||
jq_teardown(&jq);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef JV_H
|
||||
#define JV_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
JV_KIND_INVALID,
|
||||
@@ -16,19 +18,21 @@ typedef enum {
|
||||
JV_KIND_OBJECT
|
||||
} jv_kind;
|
||||
|
||||
struct jv_refcnt;
|
||||
|
||||
/* All of the fields of this struct are private.
|
||||
Really. Do not play with them. */
|
||||
typedef struct {
|
||||
unsigned char kind_flags;
|
||||
unsigned char pad_;
|
||||
unsigned short offset; /* array offsets */
|
||||
int size;
|
||||
size_t count;
|
||||
} jv_refcnt;
|
||||
|
||||
typedef struct{
|
||||
jv_refcnt* ptr;
|
||||
int i[2];
|
||||
} jv_nontrivial;
|
||||
|
||||
typedef struct {
|
||||
jv_kind kind;
|
||||
union {
|
||||
struct jv_refcnt* ptr;
|
||||
double number;
|
||||
} u;
|
||||
jv_nontrivial nontrivial;
|
||||
} val;
|
||||
} jv;
|
||||
|
||||
/*
|
||||
@@ -43,27 +47,24 @@ static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; }
|
||||
jv jv_copy(jv);
|
||||
void jv_free(jv);
|
||||
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
int jv_equal(jv, jv);
|
||||
int jv_contains(jv, jv);
|
||||
|
||||
jv jv_invalid(void);
|
||||
jv jv_invalid();
|
||||
jv jv_invalid_with_msg(jv);
|
||||
jv jv_invalid_get_msg(jv);
|
||||
int jv_invalid_has_msg(jv);
|
||||
|
||||
|
||||
jv jv_null(void);
|
||||
jv jv_true(void);
|
||||
jv jv_false(void);
|
||||
jv jv_null();
|
||||
jv jv_true();
|
||||
jv jv_false();
|
||||
jv jv_bool(int);
|
||||
|
||||
jv jv_number(double);
|
||||
double jv_number_value(jv);
|
||||
int jv_is_integer(jv);
|
||||
|
||||
jv jv_array(void);
|
||||
jv jv_array();
|
||||
jv jv_array_sized(int);
|
||||
int jv_array_length(jv);
|
||||
jv jv_array_get(jv, int);
|
||||
@@ -71,7 +72,6 @@ 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) \
|
||||
@@ -82,30 +82,21 @@ jv jv_array_indexes(jv, jv);
|
||||
|
||||
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);
|
||||
uint32_t 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(void);
|
||||
jv jv_object();
|
||||
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);
|
||||
@@ -126,36 +117,20 @@ 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);
|
||||
enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2, JV_PRINT_COLOUR = 4 };
|
||||
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);
|
||||
jv jv_parse_sized(const char* string, int length);
|
||||
|
||||
typedef void (*jv_nomem_handler_f)(void *);
|
||||
void jv_nomem_handler(jv_nomem_handler_f, void *);
|
||||
|
||||
jv jv_load_file(const char *, int);
|
||||
|
||||
struct jv_parser;
|
||||
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*);
|
||||
|
||||
jv jv_get(jv, jv);
|
||||
jv jv_set(jv, jv, jv);
|
||||
jv jv_has(jv, jv);
|
||||
jv jv_setpath(jv, jv, jv);
|
||||
jv jv_getpath(jv, jv);
|
||||
jv jv_delpaths(jv, jv);
|
||||
jv jv_keys(jv /*object or array*/);
|
||||
int jv_cmp(jv, jv);
|
||||
jv jv_group(jv, jv);
|
||||
jv jv_sort(jv, jv);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
-114
@@ -2,121 +2,11 @@
|
||||
#include <stdio.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct nomem_handler {
|
||||
jv_nomem_handler_f handler;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#if !defined(HAVE_PTHREAD_KEY_CREATE) || \
|
||||
!defined(HAVE_PTHREAD_ONCE) || \
|
||||
!defined(HAVE_ATEXIT)
|
||||
|
||||
/* Try thread-local storage? */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Visual C++: yes */
|
||||
static __declspec(thread) struct nomem_handler nomem_handler;
|
||||
#define USE_TLS
|
||||
#else
|
||||
#ifdef HAVE___THREAD
|
||||
/* GCC and friends: yes */
|
||||
static __thread struct nomem_handler nomem_handler;
|
||||
#define USE_TLS
|
||||
#endif /* HAVE___THREAD */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#endif /* !HAVE_PTHREAD_KEY_CREATE */
|
||||
|
||||
#ifdef USE_TLS
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
if (nomem_handler.handler)
|
||||
nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
#else /* USE_TLS */
|
||||
|
||||
#ifdef HAVE_PTHREAD_KEY_CREATE
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_key_t nomem_handler_key;
|
||||
pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void tsd_fini(void) {
|
||||
struct nomem_handler *nomem_handler;
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler) {
|
||||
(void) pthread_setspecific(nomem_handler_key, NULL);
|
||||
free(nomem_handler);
|
||||
}
|
||||
}
|
||||
|
||||
static void tsd_init(void) {
|
||||
if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
|
||||
fprintf(stderr, "error: cannot create thread specific key");
|
||||
abort();
|
||||
}
|
||||
if (atexit(tsd_fini) != 0) {
|
||||
fprintf(stderr, "error: cannot set an exit handler");
|
||||
abort();
|
||||
}
|
||||
struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));
|
||||
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
fprintf(stderr, "error: cannot set thread specific data");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
pthread_once(&mem_once, tsd_init); // cannot fail
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler == NULL) {
|
||||
handler(data);
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
nomem_handler->handler = handler;
|
||||
nomem_handler->data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
pthread_once(&mem_once, tsd_init);
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler)
|
||||
nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* No thread-local storage of any kind that we know how to handle */
|
||||
|
||||
static struct nomem_handler nomem_handler;
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
nomem_handler.data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif /* HAVE_PTHREAD_KEY_CREATE */
|
||||
#endif /* USE_TLS */
|
||||
|
||||
|
||||
void* jv_mem_alloc(size_t sz) {
|
||||
void* p = malloc(sz);
|
||||
if (!p) {
|
||||
@@ -125,10 +15,6 @@ void* jv_mem_alloc(size_t sz) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void* jv_mem_alloc_unguarded(size_t sz) {
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
void jv_mem_free(void* p) {
|
||||
free(p);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define JV_ALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "jv.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
extern volatile char jv_mem_uninitialised;
|
||||
@@ -16,7 +15,6 @@ static void jv_mem_invalidate(void* mem, size_t n) {
|
||||
}
|
||||
|
||||
void* jv_mem_alloc(size_t);
|
||||
void* jv_mem_alloc_unguarded(size_t);
|
||||
void jv_mem_free(void*);
|
||||
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);
|
||||
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
#include "jv_aux.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
|
||||
static int parse_slice(jv array, 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);
|
||||
@@ -26,18 +22,13 @@ static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
|
||||
jv_free(end_jv);
|
||||
return 0;
|
||||
} else {
|
||||
double dstart = jv_number_value(start_jv);
|
||||
double dend = jv_number_value(end_jv);
|
||||
if (dstart < 0) dstart += len;
|
||||
if (dend < 0) dend += len;
|
||||
if (dstart < 0) dstart = 0;
|
||||
if (dstart > len) dstart = len;
|
||||
|
||||
int start = (int)dstart;
|
||||
int end = (dend > len) ? len : (int)dend;
|
||||
// Ends are exclusive but e.g. 1 < 1.5 so :1.5 should be :2 not :1
|
||||
if(end < dend) end += 1;
|
||||
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;
|
||||
|
||||
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);
|
||||
@@ -56,15 +47,10 @@ 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) {
|
||||
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);
|
||||
// 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);
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) {
|
||||
@@ -75,18 +61,6 @@ 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 ||
|
||||
@@ -95,20 +69,9 @@ jv jv_get(jv t, jv k) {
|
||||
jv_free(k);
|
||||
v = jv_null();
|
||||
} else {
|
||||
/*
|
||||
* If k is a short string it's probably from a jq .foo expression or
|
||||
* similar, in which case putting it in the invalid msg may help the
|
||||
* user. The length 30 is arbitrary.
|
||||
*/
|
||||
if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_string_value(k)));
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
}
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
}
|
||||
@@ -582,23 +545,21 @@ 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();
|
||||
if (n > 0) {
|
||||
jv curr_key = entries[0].key;
|
||||
jv group = jv_array_append(jv_array(), entries[0].object);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
|
||||
jv_free(entries[i].key);
|
||||
} else {
|
||||
jv_free(curr_key);
|
||||
curr_key = entries[i].key;
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array();
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
jv 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();
|
||||
}
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
}
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
jv_mem_free(entries);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef JV_AUX_H
|
||||
#define JV_AUX_H
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_get(jv t, jv k);
|
||||
jv jv_set(jv t, jv k, jv v);
|
||||
jv jv_has(jv t, jv k);
|
||||
jv jv_setpath(jv root, jv path, jv value);
|
||||
jv jv_getpath(jv root, jv path);
|
||||
jv jv_delpaths(jv root, jv paths);
|
||||
|
||||
jv jv_keys(jv /*object or array*/);
|
||||
int jv_cmp(jv, jv);
|
||||
|
||||
jv jv_group(jv objects, jv keys);
|
||||
jv jv_sort(jv objects, jv keys);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -187,6 +187,7 @@
|
||||
* used for input more than STRTOD_DIGLIM digits long (default 40).
|
||||
*/
|
||||
|
||||
#define IEEE_8087
|
||||
#define NO_ERRNO
|
||||
#define NO_HEX_FP
|
||||
#define No_Hex_NaN
|
||||
@@ -4224,7 +4225,7 @@ jvp_dtoa
|
||||
while((*b++ = *s++));
|
||||
goto done0;
|
||||
}
|
||||
if (decpt <= -4 || decpt > se - s + 15) {
|
||||
if (decpt <= -4 || decpt > se - s + 5) {
|
||||
*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 64
|
||||
#define JVP_DTOA_FMT_MAX_LEN 32
|
||||
char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);
|
||||
#endif
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_load_file(const char* filename, int raw) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
struct jv_parser* parser;
|
||||
jv data;
|
||||
if (!file) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
if (raw) {
|
||||
data = jv_string("");
|
||||
} else {
|
||||
data = jv_array();
|
||||
parser = jv_parser_new(0);
|
||||
}
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
if (raw) {
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
} else {
|
||||
jv_parser_set_buf(parser, buf, n, !feof(file));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(parser))))
|
||||
data = jv_array_append(data, value);
|
||||
jv_free(value);
|
||||
}
|
||||
}
|
||||
if (!raw)
|
||||
jv_parser_free(parser);
|
||||
int badread = ferror(file);
|
||||
fclose(file);
|
||||
if (badread) {
|
||||
jv_free(data);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",
|
||||
filename));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
+5
-45
@@ -1,12 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "jv.h"
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_parse.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "jv_dtoa.h"
|
||||
|
||||
typedef const char* presult;
|
||||
|
||||
@@ -17,35 +16,7 @@ typedef const char* presult;
|
||||
#define pfunc presult
|
||||
#endif
|
||||
|
||||
struct jv_parser {
|
||||
const char* curr_buf;
|
||||
int curr_buf_length;
|
||||
int curr_buf_pos;
|
||||
int curr_buf_is_partial;
|
||||
unsigned bom_strip_position;
|
||||
|
||||
jv* stack;
|
||||
int stackpos;
|
||||
int stacklen;
|
||||
jv next;
|
||||
|
||||
char* tokenbuf;
|
||||
int tokenpos;
|
||||
int tokenlen;
|
||||
|
||||
int line, column;
|
||||
|
||||
struct dtoa_context dtoa;
|
||||
|
||||
enum {
|
||||
JV_PARSER_NORMAL,
|
||||
JV_PARSER_STRING,
|
||||
JV_PARSER_STRING_ESCAPE
|
||||
} st;
|
||||
};
|
||||
|
||||
|
||||
static void parser_init(struct jv_parser* p) {
|
||||
void jv_parser_init(struct jv_parser* p) {
|
||||
p->stack = 0;
|
||||
p->stacklen = p->stackpos = 0;
|
||||
p->next = jv_invalid();
|
||||
@@ -60,7 +31,7 @@ static void parser_init(struct jv_parser* p) {
|
||||
jvp_dtoa_context_init(&p->dtoa);
|
||||
}
|
||||
|
||||
static void parser_free(struct jv_parser* p) {
|
||||
void jv_parser_free(struct jv_parser* p) {
|
||||
jv_free(p->next);
|
||||
for (int i=0; i<p->stackpos; i++)
|
||||
jv_free(p->stack[i]);
|
||||
@@ -370,17 +341,6 @@ static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
||||
return answer;
|
||||
}
|
||||
|
||||
struct jv_parser* jv_parser_new(int flags) {
|
||||
struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));
|
||||
parser_init(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void jv_parser_free(struct jv_parser* p) {
|
||||
parser_free(p);
|
||||
jv_mem_free(p);
|
||||
}
|
||||
|
||||
static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {
|
||||
@@ -444,7 +404,7 @@ jv jv_parser_next(struct jv_parser* p) {
|
||||
|
||||
jv jv_parse_sized(const char* string, int length) {
|
||||
struct jv_parser parser;
|
||||
parser_init(&parser);
|
||||
jv_parser_init(&parser);
|
||||
jv_parser_set_buf(&parser, string, length, 0);
|
||||
jv value = jv_parser_next(&parser);
|
||||
if (jv_is_valid(value)) {
|
||||
@@ -469,7 +429,7 @@ jv jv_parse_sized(const char* string, int length) {
|
||||
jv_free(value);
|
||||
value = jv_invalid_with_msg(jv_string("Expected JSON value"));
|
||||
}
|
||||
parser_free(&parser);
|
||||
jv_parser_free(&parser);
|
||||
|
||||
if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#ifndef JV_PARSE_H
|
||||
#define JV_PARSE_H
|
||||
#include "jv_dtoa.h"
|
||||
struct jv_parser {
|
||||
const char* curr_buf;
|
||||
int curr_buf_length;
|
||||
int curr_buf_pos;
|
||||
int curr_buf_is_partial;
|
||||
unsigned bom_strip_position;
|
||||
|
||||
jv* stack;
|
||||
int stackpos;
|
||||
int stacklen;
|
||||
jv next;
|
||||
|
||||
char* tokenbuf;
|
||||
int tokenpos;
|
||||
int tokenlen;
|
||||
|
||||
int line, column;
|
||||
|
||||
struct dtoa_context dtoa;
|
||||
|
||||
enum {
|
||||
JV_PARSER_NORMAL,
|
||||
JV_PARSER_STRING,
|
||||
JV_PARSER_STRING_ESCAPE
|
||||
} st;
|
||||
};
|
||||
|
||||
void jv_parser_init(struct jv_parser* p);
|
||||
void jv_parser_free(struct jv_parser* p);
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial);
|
||||
|
||||
jv jv_parser_next(struct jv_parser* p);
|
||||
#endif
|
||||
+3
-42
@@ -2,7 +2,6 @@
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_unicode.h"
|
||||
@@ -195,34 +194,7 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI
|
||||
put_space(indent+INDENT, F, S);
|
||||
}
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
jv keyset = jv_null();
|
||||
while (1) {
|
||||
jv key, value;
|
||||
if (flags & JV_PRINT_SORTED) {
|
||||
if (first) {
|
||||
keyset = jv_keys(jv_copy(x));
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
if (i >= jv_array_length(jv_copy(keyset))) {
|
||||
jv_free(keyset);
|
||||
break;
|
||||
}
|
||||
key = jv_array_get(jv_copy(keyset), i);
|
||||
value = jv_object_get(jv_copy(x), jv_copy(key));
|
||||
} else {
|
||||
if (first) {
|
||||
i = jv_object_iter(x);
|
||||
} else {
|
||||
i = jv_object_iter_next(x, i);
|
||||
}
|
||||
if (!jv_object_iter_valid(x, i)) break;
|
||||
key = jv_object_iter_key(x, i);
|
||||
value = jv_object_iter_value(x, i);
|
||||
}
|
||||
|
||||
jv_object_foreach(x, key, value) {
|
||||
if (!first) {
|
||||
if (flags & JV_PRINT_PRETTY){
|
||||
put_str(",\n", F, S);
|
||||
@@ -260,24 +232,13 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI
|
||||
}
|
||||
}
|
||||
|
||||
void jv_dumpf(jv x, FILE *f, int flags) {
|
||||
void jv_dump(jv x, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
jv_dump_term(&C, x, flags, 0, f, 0);
|
||||
jv_dump_term(&C, x, flags, 0, stdout, 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);
|
||||
|
||||
+17
-49
@@ -3,67 +3,35 @@
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_utf8_tables.h"
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {
|
||||
assert(in <= end);
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint) {
|
||||
if (in == end) {
|
||||
codepoint = 0;
|
||||
return 0;
|
||||
}
|
||||
int codepoint = -1;
|
||||
unsigned char first = (unsigned char)in[0];
|
||||
int length = utf8_coding_length[first];
|
||||
if ((first & 0x80) == 0) {
|
||||
/* Fast-path for ASCII */
|
||||
codepoint = first;
|
||||
length = 1;
|
||||
} else if (length == 0 || length == UTF8_CONTINUATION_BYTE) {
|
||||
/* Bad single byte - either an invalid byte or an out-of-place continuation byte */
|
||||
length = 1;
|
||||
} else if (in + length > end) {
|
||||
/* String ends before UTF8 sequence ends */
|
||||
length = end - in;
|
||||
} else {
|
||||
codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
unsigned ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[ch] != UTF8_CONTINUATION_BYTE){
|
||||
/* Invalid UTF8 sequence - not followed by the right number of continuation bytes */
|
||||
codepoint = -1;
|
||||
length = i;
|
||||
break;
|
||||
}
|
||||
codepoint = (codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
if (codepoint < utf8_first_codepoint[length]) {
|
||||
/* Overlong UTF8 sequence */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (0xD800 <= codepoint && codepoint <= 0xDFFF) {
|
||||
/* Surrogate codepoints can't be encoded in UTF8 */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (codepoint > 0x10FFFF) {
|
||||
/* Outside Unicode range */
|
||||
codepoint = -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
*codepoint = (*codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
assert(length > 0);
|
||||
*codepoint_ret = codepoint;
|
||||
return in + length;
|
||||
}
|
||||
|
||||
int jvp_utf8_is_valid(const char* in, const char* end) {
|
||||
int codepoint;
|
||||
int jvp_utf8_verify(const char* in, const char* end) {
|
||||
int codepoint = 0;
|
||||
while ((in = jvp_utf8_next(in, end, &codepoint))) {
|
||||
if (codepoint == -1) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int jvp_utf8_decode_length(char startchar) {
|
||||
if ((startchar & 0x80) == 0) return 1;
|
||||
else if ((startchar & 0xC0) == 0xC0) return 2;
|
||||
else if ((startchar & 0xE0) == 0xE0) return 3;
|
||||
else return 4;
|
||||
return codepoint != -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,5 +33,3 @@ 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};
|
||||
@@ -1,5 +1,4 @@
|
||||
%{
|
||||
#include <assert.h>
|
||||
#include "jv_alloc.h"
|
||||
#include "compile.h"
|
||||
|
||||
@@ -57,12 +56,10 @@ struct lexer_param;
|
||||
"-=" { return SETMINUS; }
|
||||
"*=" { return SETMULT; }
|
||||
"/=" { return SETDIV; }
|
||||
"%=" { return SETMOD; }
|
||||
"//=" { return SETDEFINEDOR; }
|
||||
"<=" { return LESSEQ; }
|
||||
">=" { return GREATEREQ; }
|
||||
".." { return REC; }
|
||||
"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}
|
||||
"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$"|"<"|">" { return yytext[0];}
|
||||
|
||||
"["|"{"|"(" {
|
||||
return enter(yytext[0], YY_START, yyscanner);
|
||||
@@ -111,7 +108,6 @@ struct lexer_param;
|
||||
|
||||
|
||||
[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
|
||||
\.[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext+1); return FIELD;}
|
||||
|
||||
[ \n\t]+ {}
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
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,103 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jq.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "locfile.h"
|
||||
|
||||
|
||||
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;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') l->nlines++;
|
||||
}
|
||||
l->linemap = jv_mem_alloc(sizeof(int) * (l->nlines + 1));
|
||||
l->linemap[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') {
|
||||
l->linemap[line] = i+1; // at start of line, not of \n
|
||||
line++;
|
||||
}
|
||||
}
|
||||
l->linemap[l->nlines] = length+1; // virtual last \n
|
||||
}
|
||||
|
||||
void locfile_free(struct locfile* l) {
|
||||
jv_mem_free(l->linemap);
|
||||
}
|
||||
|
||||
static int locfile_get_line(struct locfile* l, int pos) {
|
||||
assert(pos < l->length);
|
||||
int line = 1;
|
||||
while (l->linemap[line] <= pos) line++; // == if pos at start (before, never ==, because pos never on \n)
|
||||
assert(line-1 < l->nlines);
|
||||
return line-1;
|
||||
}
|
||||
|
||||
static int locfile_line_length(struct locfile* l, int line) {
|
||||
assert(line < l->nlines);
|
||||
return l->linemap[line+1] - l->linemap[line] -1; // -1 to omit \n
|
||||
}
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
jq_err_cb cb;
|
||||
void *cb_data;
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
int startline;
|
||||
int offset;
|
||||
|
||||
if (loc.start != -1) {
|
||||
startline = locfile_get_line(l, loc.start);
|
||||
offset = l->linemap[startline];
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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,8 +1,10 @@
|
||||
#ifndef _LOCFILE_H
|
||||
#define _LOCFILE_H
|
||||
|
||||
#include "jq.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include "jv_alloc.h"
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} location;
|
||||
@@ -14,14 +16,64 @@ struct locfile {
|
||||
int length;
|
||||
int* linemap;
|
||||
int nlines;
|
||||
char *error;
|
||||
jq_state *jq;
|
||||
};
|
||||
|
||||
void locfile_init(struct locfile* l, jq_state *jq, const char* data, int length);
|
||||
static void locfile_init(struct locfile* l, const char* data, int length) {
|
||||
l->data = data;
|
||||
l->length = length;
|
||||
l->nlines = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') l->nlines++;
|
||||
}
|
||||
l->linemap = jv_mem_alloc(sizeof(int) * (l->nlines + 1));
|
||||
l->linemap[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') {
|
||||
l->linemap[line] = i;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
l->linemap[l->nlines] = length;
|
||||
}
|
||||
|
||||
void locfile_free(struct locfile* l);
|
||||
static void locfile_free(struct locfile* l) {
|
||||
jv_mem_free(l->linemap);
|
||||
}
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...);
|
||||
static int locfile_get_line(struct locfile* l, int pos) {
|
||||
assert(pos < l->length);
|
||||
int line = 0;
|
||||
while (l->linemap[line+1] < pos) line++;
|
||||
assert(line < l->nlines);
|
||||
return line;
|
||||
}
|
||||
|
||||
static int locfile_line_length(struct locfile* l, int line) {
|
||||
assert(line < l->nlines);
|
||||
return l->linemap[line+1] - l->linemap[line];
|
||||
}
|
||||
|
||||
static void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
vfprintf(stderr, fmt, fmtargs);
|
||||
va_end(fmtargs);
|
||||
fprintf(stderr, "\n");
|
||||
if (loc.start == -1) {
|
||||
fprintf(stderr, "<unknown location>\n");
|
||||
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, "^");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
@@ -6,44 +5,29 @@
|
||||
#include <unistd.h>
|
||||
#include "compile.h"
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
#include "jv_parse.h"
|
||||
#include "execute.h"
|
||||
#include "config.h" /* Autoconf generated header file */
|
||||
#include "jv_alloc.h"
|
||||
#include "version.h"
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]);
|
||||
|
||||
static const char* progname;
|
||||
|
||||
/*
|
||||
* For a longer help message we could use a better option parsing
|
||||
* strategy, one that lets stack options.
|
||||
*/
|
||||
static void usage(int code) {
|
||||
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", JQ_VERSION);
|
||||
static void usage() {
|
||||
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", PACKAGE_VERSION);
|
||||
fprintf(stderr, "Usage: %s [options] <jq filter> [file...]\n\n", progname);
|
||||
fprintf(stderr, "\tjq is a tool for processing JSON inputs, applying the\n");
|
||||
fprintf(stderr, "\tgiven filter to its JSON text inputs and producing the\n");
|
||||
fprintf(stderr, "\tfilter's results as JSON on standard output.\n");
|
||||
fprintf(stderr, "\tThe simplest filter is ., which is the identity filter,\n");
|
||||
fprintf(stderr, "\tcopying jq's input to its output.\n");
|
||||
fprintf(stderr, "\tFor more advanced filters see the jq(1) manpage (\"man jq\")\n");
|
||||
fprintf(stderr, "\tand/or http://stedolan.github.com/jq\n\n");
|
||||
fprintf(stderr, "\tSome of the options include:\n");
|
||||
fprintf(stderr, "\t -h\t\tthis message;\n");
|
||||
fprintf(stderr, "\t -c\t\tcompact instead of pretty-printed output;\n");
|
||||
fprintf(stderr, "\t -s\t\tread all inputs into an array; apply filter to it;\n");
|
||||
fprintf(stderr, "\t -r\t\toutput raw strings, not JSON texts;\n");
|
||||
fprintf(stderr, "\t -R\t\tread raw strings, not JSON texts;\n");
|
||||
fprintf(stderr, "\t -arg a v\tset variable $a to value <v>;\n");
|
||||
fprintf(stderr, "\t -argfile a f\tset variable $a to JSON texts read from <f>;\n");
|
||||
fprintf(stderr, "\tSee the manpage for more options.\n");
|
||||
exit(code);
|
||||
fprintf(stderr, "For a description of the command line options and\n");
|
||||
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.io/jq\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void die() {
|
||||
fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
|
||||
fprintf(stderr, "or see the jq manpage, or online docs at http://stedolan.github.com/jq\n");
|
||||
exit(2);
|
||||
fprintf(stderr, "or see the jq documentation at http://stedolan.github.io/jq\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,51 +37,39 @@ static int isoptish(const char* text) {
|
||||
return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
|
||||
}
|
||||
|
||||
static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {
|
||||
if (text[0] != '-' || text[1] == '-')
|
||||
*short_opts = 0;
|
||||
static int isoption(const char* text, char shortopt, const char* longopt) {
|
||||
if (text[0] != '-') return 0;
|
||||
|
||||
// check long option
|
||||
if (strlen(text) == 2 && text[1] == shortopt) return 1;
|
||||
if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
|
||||
else if (text[1] == '-') return 0;
|
||||
|
||||
// must be short option; check it and...
|
||||
if (!shortopt) return 0;
|
||||
if (strchr(text, shortopt) != NULL) {
|
||||
(*short_opts)++; // ...count it (for option stacking)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
SLURP = 1,
|
||||
RAW_INPUT = 2,
|
||||
PROVIDE_NULL = 4,
|
||||
RAW_OUTPUT = 8,
|
||||
COMPACT_OUTPUT = 16,
|
||||
ASCII_OUTPUT = 32,
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
SORTED_OUTPUT = 256,
|
||||
FROM_FILE = 512,
|
||||
RAW_NO_LF = 1024,
|
||||
UNBUFFERED_OUTPUT = 2048,
|
||||
EXIT_STATUS = 4096,
|
||||
SLURP = 1,
|
||||
RAW_INPUT = 2,
|
||||
PROVIDE_NULL = 4,
|
||||
|
||||
RAW_OUTPUT = 8,
|
||||
COMPACT_OUTPUT = 16,
|
||||
ASCII_OUTPUT = 32,
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
|
||||
FROM_FILE = 256,
|
||||
|
||||
/* debugging only */
|
||||
DUMP_DISASM = 8192,
|
||||
DUMP_DISASM = 2048,
|
||||
};
|
||||
static int options = 0;
|
||||
static struct bytecode* bc;
|
||||
|
||||
static int process(jq_state *jq, jv value, int flags) {
|
||||
int ret = 14; // No valid results && -e -> exit(4)
|
||||
jq_start(jq, value, flags);
|
||||
static void process(jv value, int flags) {
|
||||
jq_state *jq = NULL;
|
||||
jq_init(bc, value, &jq, 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;
|
||||
@@ -108,28 +80,43 @@ static int process(jq_state *jq, jv value, int flags) {
|
||||
#else
|
||||
dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0;
|
||||
#endif
|
||||
if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
|
||||
if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY;
|
||||
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);
|
||||
}
|
||||
if (!(options & RAW_NO_LF))
|
||||
printf("\n");
|
||||
if (options & UNBUFFERED_OUTPUT)
|
||||
fflush(stdout);
|
||||
printf("\n");
|
||||
}
|
||||
jv_free(result);
|
||||
return ret;
|
||||
jq_teardown(&jq);
|
||||
}
|
||||
|
||||
static jv slurp_file(const char* filename) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
jv data = jv_string("");
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
}
|
||||
int badread = ferror(file);
|
||||
fclose(file);
|
||||
if (badread) {
|
||||
jv_free(data);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",
|
||||
filename));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
FILE* current_input;
|
||||
const char** input_filenames = NULL;
|
||||
const char** input_filenames;
|
||||
int ninput_files;
|
||||
int next_input_idx;
|
||||
static int read_more(char* buf, size_t size) {
|
||||
@@ -157,21 +144,11 @@ static int read_more(char* buf, size_t size) {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
jq_state *jq = NULL;
|
||||
int ret = 0;
|
||||
int compiled = 0;
|
||||
|
||||
if (argc) progname = argv[0];
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "--run-tests")) {
|
||||
return jq_testsuite(argc, argv);
|
||||
}
|
||||
|
||||
jq = jq_init();
|
||||
if (jq == NULL) {
|
||||
perror("malloc");
|
||||
ret = 2;
|
||||
goto out;
|
||||
return jq_testsuite(argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
const char* program = 0;
|
||||
@@ -179,13 +156,12 @@ int main(int argc, char* argv[]) {
|
||||
ninput_files = 0;
|
||||
int further_args_are_files = 0;
|
||||
int jq_flags = 0;
|
||||
size_t short_opts = 0;
|
||||
jv program_arguments = jv_array();
|
||||
for (int i=1; i<argc; i++, short_opts = 0) {
|
||||
for (int i=1; i<argc; i++) {
|
||||
if (further_args_are_files) {
|
||||
input_filenames[ninput_files++] = argv[i];
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (!program) usage(2);
|
||||
if (!program) usage();
|
||||
further_args_are_files = 1;
|
||||
} else if (!isoptish(argv[i])) {
|
||||
if (program) {
|
||||
@@ -193,131 +169,49 @@ int main(int argc, char* argv[]) {
|
||||
} else {
|
||||
program = argv[i];
|
||||
}
|
||||
} else {
|
||||
if (isoption(argv[i], 's', "slurp", &short_opts)) {
|
||||
options |= SLURP;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'r', "raw-output", &short_opts)) {
|
||||
options |= RAW_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'c', "compact-output", &short_opts)) {
|
||||
options |= COMPACT_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'C', "color-output", &short_opts)) {
|
||||
options |= COLOUR_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'a', "ascii-output", &short_opts)) {
|
||||
options |= ASCII_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
|
||||
options |= UNBUFFERED_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
|
||||
options |= SORTED_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'R', "raw-input", &short_opts)) {
|
||||
options |= RAW_INPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'n', "null-input", &short_opts)) {
|
||||
options |= PROVIDE_NULL;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'f', "from-file", &short_opts)) {
|
||||
options |= FROM_FILE;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'j', "join-output", &short_opts)) {
|
||||
options |= RAW_OUTPUT | RAW_NO_LF;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'e', "exit-status", &short_opts)) {
|
||||
options |= EXIT_STATUS;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "arg", &short_opts)) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts)) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
jv data = jv_load_file(argv[i+2], 0);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: Bad JSON in --argfile %s %s: %s\n", progname,
|
||||
argv[i+1], argv[i+2], jv_string_value(data));
|
||||
jv_free(data);
|
||||
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);
|
||||
arg = jv_object_set(arg, jv_string("value"), data);
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {
|
||||
options |= DUMP_DISASM;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-trace", &short_opts)) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'h', "help", &short_opts)) {
|
||||
usage(0);
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'V', "version", &short_opts)) {
|
||||
printf("jq-%s\n", JQ_VERSION);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
// check for unknown options... if this argument was a short option
|
||||
if (strlen(argv[i]) != short_opts + 1) {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
} else if (isoption(argv[i], 's', "slurp")) {
|
||||
options |= SLURP;
|
||||
} else if (isoption(argv[i], 'r', "raw-output")) {
|
||||
options |= RAW_OUTPUT;
|
||||
} else if (isoption(argv[i], 'c', "compact-output")) {
|
||||
options |= COMPACT_OUTPUT;
|
||||
} else if (isoption(argv[i], 'C', "color-output")) {
|
||||
options |= COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'M', "monochrome-output")) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'a', "ascii-output")) {
|
||||
options |= ASCII_OUTPUT;
|
||||
} else if (isoption(argv[i], 'R', "raw-input")) {
|
||||
options |= RAW_INPUT;
|
||||
} else if (isoption(argv[i], 'n', "null-input")) {
|
||||
options |= PROVIDE_NULL;
|
||||
} else if (isoption(argv[i], 'f', "from-file")) {
|
||||
options |= FROM_FILE;
|
||||
} 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);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
} else if (isoption(argv[i], 0, "debug-dump-disasm")) {
|
||||
options |= DUMP_DISASM;
|
||||
} else if (isoption(argv[i], 0, "debug-trace")) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
} else if (isoption(argv[i], 'h', "help")) {
|
||||
usage();
|
||||
} else if (isoption(argv[i], 'V', "version")) {
|
||||
fprintf(stderr, "jq version %s\n", PACKAGE_VERSION);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
#if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY)
|
||||
|
||||
#if defined(HAVE__ISATTY) && defined(isatty)
|
||||
#undef isatty
|
||||
#define isatty _isatty
|
||||
#endif
|
||||
|
||||
if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO))
|
||||
program = ".";
|
||||
#endif
|
||||
|
||||
if (!program) usage(2);
|
||||
if (!program) usage();
|
||||
if (ninput_files == 0) current_input = stdin;
|
||||
|
||||
if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) {
|
||||
@@ -326,31 +220,27 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
if (options & FROM_FILE) {
|
||||
jv data = jv_load_file(program, 1);
|
||||
jv data = slurp_file(program);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
|
||||
jv_free(data);
|
||||
ret = 2;
|
||||
goto out;
|
||||
return 1;
|
||||
}
|
||||
compiled = jq_compile_args(jq, jv_string_value(data), program_arguments);
|
||||
bc = jq_compile_args(jv_string_value(data), program_arguments);
|
||||
jv_free(data);
|
||||
} else {
|
||||
compiled = jq_compile_args(jq, program, program_arguments);
|
||||
}
|
||||
if (!compiled){
|
||||
ret = 3;
|
||||
goto out;
|
||||
bc = jq_compile_args(program, program_arguments);
|
||||
}
|
||||
if (!bc) return 1;
|
||||
|
||||
if (options & DUMP_DISASM) {
|
||||
jq_dump_disassembly(jq, 0);
|
||||
dump_disassembly(0, bc);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (options & PROVIDE_NULL) {
|
||||
ret = process(jq, jv_null(), jq_flags);
|
||||
process(jv_null(), jq_flags);
|
||||
} else {
|
||||
jv slurped;
|
||||
if (options & SLURP) {
|
||||
@@ -360,7 +250,8 @@ int main(int argc, char* argv[]) {
|
||||
slurped = jv_array();
|
||||
}
|
||||
}
|
||||
struct jv_parser* parser = jv_parser_new(0);
|
||||
struct jv_parser parser;
|
||||
jv_parser_init(&parser);
|
||||
char buf[4096];
|
||||
while (read_more(buf, sizeof(buf))) {
|
||||
if (options & RAW_INPUT) {
|
||||
@@ -370,43 +261,39 @@ int main(int argc, char* argv[]) {
|
||||
slurped = jv_string_concat(slurped, jv_string(buf));
|
||||
} else {
|
||||
if (buf[len-1] == '\n') buf[len-1] = 0;
|
||||
ret = process(jq, jv_string(buf), jq_flags);
|
||||
process(jv_string(buf), jq_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin));
|
||||
jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(parser)))) {
|
||||
while (jv_is_valid((value = jv_parser_next(&parser)))) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_array_append(slurped, value);
|
||||
} else {
|
||||
ret = process(jq, value, jq_flags);
|
||||
process(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 = 4;
|
||||
ret = 1;
|
||||
break;
|
||||
} else {
|
||||
jv_free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
jv_parser_free(parser);
|
||||
jv_parser_free(&parser);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
if (options & SLURP) {
|
||||
ret = process(jq, slurped, jq_flags);
|
||||
process(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;
|
||||
bytecode_free(bc);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "opcode.h"
|
||||
|
||||
// flags, length
|
||||
#define NONE 0, 1
|
||||
#define CONSTANT OP_HAS_CONSTANT, 2
|
||||
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
||||
#define BRANCH OP_HAS_BRANCH, 2
|
||||
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
||||
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
||||
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
||||
|
||||
#define OP(name, imm, in, out) \
|
||||
{name, #name, imm, in, out},
|
||||
|
||||
static const struct opcode_description opcode_descriptions[] = {
|
||||
#include "opcode_list.h"
|
||||
};
|
||||
|
||||
static const struct opcode_description invalid_opcode_description = {
|
||||
-1, "#INVALID", 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op) {
|
||||
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
||||
return &opcode_descriptions[op];
|
||||
} else {
|
||||
return &invalid_opcode_description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef OPCODE_H
|
||||
#define OPCODE_H
|
||||
#include <assert.h>
|
||||
|
||||
typedef enum {
|
||||
#define OP(name, imm, in, out) name,
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
} opcode;
|
||||
|
||||
enum {
|
||||
NUM_OPCODES =
|
||||
#define OP(name, imm, in, out) +1
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
enum {
|
||||
OP_HAS_CONSTANT = 2,
|
||||
OP_HAS_VARIABLE = 4,
|
||||
OP_HAS_BRANCH = 8,
|
||||
OP_HAS_CFUNC = 32,
|
||||
OP_HAS_UFUNC = 64,
|
||||
OP_IS_CALL_PSEUDO = 128,
|
||||
OP_HAS_BINDING = 1024,
|
||||
};
|
||||
struct opcode_description {
|
||||
opcode op;
|
||||
const char* name;
|
||||
|
||||
int flags;
|
||||
|
||||
// length in 16-bit units
|
||||
int length;
|
||||
|
||||
int stack_in, stack_out;
|
||||
};
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op);
|
||||
|
||||
#endif
|
||||
@@ -6,9 +6,7 @@ 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)
|
||||
@@ -27,7 +25,6 @@ OP(CALL_BUILTIN, CFUNC, -1, 1)
|
||||
|
||||
OP(CALL_JQ, UFUNC, 1, 1)
|
||||
OP(RET, NONE, 1, 1)
|
||||
OP(TAIL_CALL_JQ, UFUNC, 1, 1)
|
||||
|
||||
OP(CLOSURE_PARAM, DEFINITION, 0, 0)
|
||||
OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "compile.h"
|
||||
#include "jv_alloc.h"
|
||||
#define YYMALLOC jv_mem_alloc
|
||||
#define YYFREE jv_mem_free
|
||||
|
||||
struct lexer_param;
|
||||
|
||||
%}
|
||||
%code requires {
|
||||
#include "locfile.h"
|
||||
struct lexer_param;
|
||||
|
||||
#define YYLTYPE location
|
||||
#define YYLLOC_DEFAULT(Loc, Rhs, N) \
|
||||
do { \
|
||||
@@ -47,12 +47,8 @@ struct lexer_param;
|
||||
|
||||
%token INVALID_CHARACTER
|
||||
%token <literal> IDENT
|
||||
%token <literal> FIELD
|
||||
%token <literal> LITERAL
|
||||
%token <literal> FORMAT
|
||||
%token Q "?"
|
||||
%token REC ".."
|
||||
%token SETMOD "%="
|
||||
%token EQ "=="
|
||||
%token NEQ "!="
|
||||
%token DEFINEDOR "//"
|
||||
@@ -86,12 +82,12 @@ struct lexer_param;
|
||||
%right '|'
|
||||
%left ','
|
||||
%right "//"
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETMOD SETDEFINEDOR
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV 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
|
||||
@@ -116,19 +112,25 @@ void yyerror(YYLTYPE* loc, block* answer, int* errors,
|
||||
int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
struct locfile* locations, struct lexer_param* lexer_param_ptr) {
|
||||
yyscan_t lexer = lexer_param_ptr->lexer;
|
||||
int tok = jq_yylex(yylval, yylloc, lexer);
|
||||
if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
FAIL(*yylloc, jv_string_value(msg));
|
||||
while (1) {
|
||||
int tok = jq_yylex(yylval, yylloc, lexer);
|
||||
if (tok == INVALID_CHARACTER) {
|
||||
FAIL(*yylloc, "Invalid character");
|
||||
} else {
|
||||
FAIL(*yylloc, "Invalid literal");
|
||||
if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
FAIL(*yylloc, jv_string_value(msg));
|
||||
} else {
|
||||
FAIL(*yylloc, "Invalid literal");
|
||||
}
|
||||
jv_free(msg);
|
||||
jv_free(yylval->literal);
|
||||
yylval->literal = jv_null();
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
jv_free(msg);
|
||||
jv_free(yylval->literal);
|
||||
yylval->literal = jv_null();
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
static block gen_dictpair(block k, block v) {
|
||||
@@ -139,11 +141,7 @@ static block gen_index(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_index_opt(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT));
|
||||
}
|
||||
|
||||
static block gen_slice_index(block obj, block start, block end, opcode idx_op) {
|
||||
static block gen_slice_index(block obj, block start, block end) {
|
||||
block key = BLOCK(gen_subexp(gen_const(jv_object())),
|
||||
gen_subexp(gen_const(jv_string("start"))),
|
||||
gen_subexp(start),
|
||||
@@ -151,7 +149,7 @@ static block gen_slice_index(block obj, block start, block end, opcode idx_op) {
|
||||
gen_subexp(gen_const(jv_string("end"))),
|
||||
gen_subexp(end),
|
||||
gen_op_simple(INSERT));
|
||||
return BLOCK(key, obj, gen_op_simple(idx_op));
|
||||
return BLOCK(key, obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_binop(block a, block b, int op) {
|
||||
@@ -161,7 +159,6 @@ 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;
|
||||
@@ -179,22 +176,24 @@ static block gen_format(block a, jv fmt) {
|
||||
}
|
||||
|
||||
static block gen_definedor_assign(block object, block val) {
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
block tmp = block_bind(gen_op_var_unbound(STOREV, "tmp"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val, tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_definedor(gen_noop(),
|
||||
gen_op_bound(LOADV, tmp))))));
|
||||
gen_op_var_bound(LOADV, tmp))))));
|
||||
}
|
||||
|
||||
static block gen_update(block object, block val, int optype) {
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
block tmp = block_bind(gen_op_var_unbound(STOREV, "tmp"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val,
|
||||
tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_binop(gen_noop(),
|
||||
gen_op_bound(LOADV, tmp),
|
||||
gen_op_var_bound(LOADV, tmp),
|
||||
optype)))));
|
||||
}
|
||||
|
||||
@@ -235,8 +234,8 @@ Term "as" '$' IDENT '|' Exp {
|
||||
"if" Exp "then" Exp ElseBody {
|
||||
$$ = gen_cond($2, $4, $5);
|
||||
} |
|
||||
"if" Exp "then" error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statement");
|
||||
"if" Exp error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statment");
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
@@ -304,18 +303,10 @@ 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);
|
||||
} |
|
||||
@@ -352,7 +343,7 @@ FuncDef:
|
||||
|
||||
"def" IDENT '(' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
gen_param(jv_string_value($4)),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($4)),
|
||||
$7);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
@@ -360,75 +351,16 @@ FuncDef:
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6))),
|
||||
BLOCK(gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($4)),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($6))),
|
||||
$9);
|
||||
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:
|
||||
QQSTRING_START { $<literal>$ = jv_string("text"); } QQString QQSTRING_END {
|
||||
$$ = $3;
|
||||
@@ -464,9 +396,7 @@ ExpD:
|
||||
ExpD '|' ExpD {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
'-' ExpD {
|
||||
$$ = BLOCK($2, gen_call("_negate", gen_noop()));
|
||||
} |
|
||||
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
@@ -476,72 +406,27 @@ Term:
|
||||
'.' {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
REC {
|
||||
$$ = gen_call("recurse", gen_noop());
|
||||
Term '.' IDENT {
|
||||
$$ = gen_index($1, gen_const($3));
|
||||
} |
|
||||
Term FIELD '?' {
|
||||
$$ = gen_index_opt($1, gen_const($2));
|
||||
'.' IDENT {
|
||||
$$ = gen_index(gen_noop(), 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));
|
||||
} |
|
||||
FIELD {
|
||||
$$ = gen_index(gen_noop(), gen_const($1));
|
||||
} |
|
||||
Term '.' String {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
'.' String {
|
||||
$$ = gen_index(gen_noop(), $2);
|
||||
} |
|
||||
'.' error {
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
'.' IDENT error {
|
||||
jv_free($2);
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
/* FIXME: string literals */
|
||||
Term '[' Exp ']' '?' {
|
||||
$$ = gen_index_opt($1, $3);
|
||||
} |
|
||||
Term '[' Exp ']' {
|
||||
$$ = 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, INDEX);
|
||||
$$ = gen_slice_index($1, $3, $5);
|
||||
} |
|
||||
Term '[' Exp ':' ']' {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX);
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()));
|
||||
} |
|
||||
Term '[' ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX);
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4);
|
||||
} |
|
||||
LITERAL {
|
||||
$$ = gen_const($1);
|
||||
@@ -565,7 +450,7 @@ FORMAT {
|
||||
$$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP));
|
||||
} |
|
||||
'$' IDENT {
|
||||
$$ = gen_location(@$, gen_op_unbound(LOADV, jv_string_value($2)));
|
||||
$$ = gen_location(@$, gen_op_var_unbound(LOADV, jv_string_value($2)));
|
||||
jv_free($2);
|
||||
} |
|
||||
IDENT {
|
||||
@@ -582,26 +467,6 @@ 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; } |
|
||||
|
||||
+8
-19
@@ -13,30 +13,19 @@
|
||||
set -e
|
||||
cd `dirname "$0"`/../build
|
||||
|
||||
jobs=-j4
|
||||
case "X$1" in
|
||||
X-j*) jobs=$1; shift;;
|
||||
esac
|
||||
|
||||
plat="$1"
|
||||
[ -z "$plat" ] && exit 1
|
||||
shift
|
||||
|
||||
case "$plat" in
|
||||
*/*) echo "platform name must not be a path"; exit 1;;
|
||||
*..*) echo "platform name must not be a path"; exit 1;;
|
||||
*) plat=$PWD/$plat;;
|
||||
esac
|
||||
|
||||
[ -d "$plat" ] || mkdir "$plat"
|
||||
rm -rf "$plat/tmp"
|
||||
mkdir "$plat/tmp"
|
||||
cd "$plat/tmp"
|
||||
../../../configure "$@"
|
||||
make "$jobs" DESTDIR=$plat install
|
||||
set -x
|
||||
for jq in `find . -type f \( -name jq -o -name jq.exe \) -print`; do
|
||||
cp "$jq" ..
|
||||
done
|
||||
rm -rf tmp
|
||||
|
||||
mkdir install_other
|
||||
../../../configure \
|
||||
--prefix="`pwd`/install_other" \
|
||||
--bindir="`pwd`/.." \
|
||||
"$@"
|
||||
make install-binaries
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd `dirname "$0"`
|
||||
git rev-parse --verify -q jq-1.0 > /dev/null 2>&1
|
||||
git describe --tags --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
@@ -9,14 +9,12 @@
|
||||
|
||||
if [ "clean" == "$1" ]; then
|
||||
make distclean || echo "continuing..."
|
||||
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 -rf tmp autom4te.cache config
|
||||
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=$(scripts/version|tr -d '\n')
|
||||
ver=$(tr -d '\n' <VERSION)
|
||||
if [ "x${ver}" != "x" ]; then
|
||||
if [ -d jq-${ver} ]; then
|
||||
chmod -R u+w jq-${ver}
|
||||
@@ -28,7 +26,6 @@ else
|
||||
autoreconf --install
|
||||
./configure --prefix=/opt/junk
|
||||
make check
|
||||
[ -d tmp ] && mv tmp tmp-
|
||||
mkdir tmp
|
||||
make DESTDIR=./tmp install
|
||||
make distcheck
|
||||
|
||||
+3
-310
@@ -36,10 +36,6 @@ 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.
|
||||
@@ -67,27 +63,18 @@ null
|
||||
"\"<>&'\\\"\""
|
||||
"1,\"<>&'\"\"\""
|
||||
"<>&'""
|
||||
"%3C%3E%26'%22"
|
||||
"%3c%3e%26'%22"
|
||||
"'<>&'\\''\"'"
|
||||
"PD4mJyI="
|
||||
|
||||
# regression test for #436
|
||||
@base64
|
||||
"foóbar\n"
|
||||
"Zm/Ds2Jhcgo="
|
||||
|
||||
@uri
|
||||
"\u03bc"
|
||||
"%CE%BC"
|
||||
"%ce%bc"
|
||||
|
||||
@html "<b>\(.)</b>"
|
||||
"<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
|
||||
#
|
||||
@@ -128,29 +115,6 @@ null
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
."foo"."bar"
|
||||
{"foo": {"bar": 20}}
|
||||
20
|
||||
|
||||
[.[]|.foo?]
|
||||
[1,[2],{"foo":3,"bar":4},{},{"foo":5}]
|
||||
[3,null,5]
|
||||
|
||||
[.[]|.foo?.bar?]
|
||||
[1,[2],[],{"foo":3},{"foo":{"bar":4}},{}]
|
||||
[4,null]
|
||||
|
||||
[..]
|
||||
[1,[[2]],{ "a":[1]}]
|
||||
[[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1]
|
||||
|
||||
[.[]|.[]?]
|
||||
[1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]]
|
||||
[1,[2,[[3]]],{},{"a":[1,[2]]}]
|
||||
|
||||
[.[]|.[1:3]?]
|
||||
[1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]]
|
||||
[null,"bc",[],[2,3],[2]]
|
||||
|
||||
#
|
||||
# Multiple outputs, iteration
|
||||
@@ -210,10 +174,6 @@ 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]
|
||||
@@ -325,14 +285,6 @@ null
|
||||
null
|
||||
[2, 8, 10, 14]
|
||||
|
||||
25 % 7
|
||||
null
|
||||
4
|
||||
|
||||
49732 % 472
|
||||
null
|
||||
172
|
||||
|
||||
1 + tonumber + ("10" | tonumber)
|
||||
4
|
||||
15
|
||||
@@ -380,10 +332,6 @@ 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]
|
||||
@@ -402,36 +350,6 @@ 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]]]
|
||||
@@ -439,11 +357,6 @@ def f(x): x | x; f([.], . + [42])
|
||||
[[1,2,3,42]]
|
||||
[1,2,3,42,42]
|
||||
|
||||
# test multiple function arities and redefinition
|
||||
def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f]
|
||||
1
|
||||
[33,101]
|
||||
|
||||
# test closures and lexical scoping
|
||||
def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x)
|
||||
"more testing"
|
||||
@@ -487,14 +400,6 @@ 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"
|
||||
@@ -543,17 +448,12 @@ 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}
|
||||
@@ -645,119 +545,6 @@ 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]
|
||||
|
||||
# match builtin
|
||||
[match("( )*"; "g")]
|
||||
"abc"
|
||||
[{"offset":0, "length":0, "string":"", "captures":[]},{"offset":1, "length":0, "string":"", "captures":[]},{"offset":2, "length":0, "string":"", "captures":[]}]
|
||||
|
||||
[match("( )*"; "gn")]
|
||||
"abc"
|
||||
[]
|
||||
|
||||
[match("a"; "gi")]
|
||||
"āáàä"
|
||||
[]
|
||||
|
||||
[match(["(bar)"])]
|
||||
"foo bar"
|
||||
[{"offset": 4, "length": 3, "string": "bar", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": null}]}]
|
||||
|
||||
# offsets account for combining codepoints and multi-byte UTF-8
|
||||
[match("bar")]
|
||||
"ā bar with a combining codepoint U+0304"
|
||||
[{"offset": 3, "length": 3, "string": "bar", "captures":[]}]
|
||||
|
||||
# matches with combining codepoints still count them in their length
|
||||
[match("bār")]
|
||||
"a bār"
|
||||
[{"offset": 2, "length": 4, "string": "bār", "captures":[]}]
|
||||
|
||||
[match(".+?\\b")]
|
||||
"ā two-codepoint grapheme"
|
||||
[{"offset": 0, "length": 2, "string": "ā", "captures":[]}]
|
||||
|
||||
[match(["foo (?<bar123>bar)? foo", "ig"])]
|
||||
"foo bar foo foo foo"
|
||||
[{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}]
|
||||
|
||||
#test builtin
|
||||
[test("( )*"; "gn")]
|
||||
"abc"
|
||||
[false]
|
||||
|
||||
[test("ā")]
|
||||
"ā"
|
||||
[true]
|
||||
|
||||
[.[]|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]
|
||||
@@ -794,10 +581,6 @@ 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"]]
|
||||
@@ -817,93 +600,3 @@ 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]
|
||||
|
||||
flatten
|
||||
[0, [1], [[2]], [[[3]]]]
|
||||
[0, 1, 2, 3]
|
||||
|
||||
flatten(2)
|
||||
[0, [1], [[2]], [[[3]]]]
|
||||
[0, 1, 2, [3]]
|
||||
|
||||
flatten(2)
|
||||
[0, [1, [2]], [1, [[3], 2]]]
|
||||
[0, 1, 2, 1, [3], 2]
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
onig node recycling
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:onig_parse_make_tree
|
||||
...
|
||||
}
|
||||
{
|
||||
onig unicode case insensitivity 1
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:setup_tree
|
||||
...
|
||||
}
|
||||
{
|
||||
onig unicode case insensitivity 2
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:onig*unicode*
|
||||
...
|
||||
}
|
||||
@@ -1,9 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
if which valgrind > /dev/null; then
|
||||
VALGRIND='valgrind --error-exitcode=1 -q --leak-check=full --suppressions=tests/onig.supp'
|
||||
else
|
||||
VALGRIND=
|
||||
fi
|
||||
|
||||
cat $@ | $VALGRIND ./jq --run-tests
|
||||
cat $@ | valgrind --error-exitcode=1 -q --leak-check=full ./jq --run-tests
|
||||
Reference in new issue
Block a user