Antirez' tiny JSON selector library
notes.billmill.org
notes.billmill.org
Btw please let's not argue about this program being written in C—it's not the spirit of the OP and it's mostly a boring digression.
On https://notes.billmill.org/programming/json/jq.html the author says that
> jq appears to be largely unmaintained at this point, which is sad
They might be happy to learn that after several years of halted development and unreleased improvements & bugfixes, jq ownership got transferred from the original author to a new organization, which allowed to resume operations:
1: https://notes.billmill.org/link_blog/2023/09/jq_is_alive_aga...
Languages like Lisp make this easy. Ruby a little less so. C-type languages way less so!
DSLs in Java, C or JavaScript are so atrociously ugly and illegible that it almost defeats the purpose of a custom grammar, IMO.
C actually wins the family DSL award because it has a preprocessor with macros, a foot gun if I ever saw one!
My pet theory is that LLMs will soon be competent enough to build the tools for a little language without much hassle, allowing for software to better express the task at hand.
It's old code — still using google's java-based compiler — so be gentle please :)
There's also a live editor to try it out: http://tjoekbezoer.github.io/jsq/parser.html
[1] https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple
----
ADDED (just in case that the comment sounds like a mindless C bashing): I wanted to highlight the point that this particular design is not general, but stems from the language choice in many ways. Those weaknesses largely overlap with those of DSLs in general, so another way to phrase this is that C kinda fosters a creation of DSLs, which is an interesting observation! By comparison the language choice itself is out of concern for me---I wrote a number of sizable softwares in C after all.
I find the choice of building and bundling yet-another-thing that specifically is already "there" (not sure if by mistake or intentionally) on a minimalist system very strange.
I also trust things in the SQLite3 codebase way more that my own code.
https://docs.python.org/dev/library/sqlite3.html#sqlite3-cli
Python uses an SQLite3 shared library, all you need to do to upgrade the SQLite3 engine is replace the shared library (.DLL on windows, .so.0 or something like that on Linux). You've never been locked into a version of the SQLite engine (well, if SQLite4 came out, you'd probably have to wait for Python to support that instead of only SQLite3, but...)
> Can I now trust modern python installations to have native-json enabled sqlite built in?
JSON has been compiled in by default since 3.38, my Python 3.10 installation (3.12 is current python) has 3.40 bundled, so, depending on what you mean by "modern" that seems reasonably safe (but, again, you aren't limited by what is bundled.)
OTOH, the JSON5 support is only from 3.42 onward, so even reasonably modern Python might not have that built in.
There is no absolute guarantee because distros can use any supported SQLite version to build Python (though Debian [1] and thus Ubuntu has long enabled -DSQLITE_ENABLE_JSON1), but you can expect that any Python version released shortly after 2022-03-16 [2] supports JSON functions in the Windows and macOS builds.
[1] https://sources.debian.org/src/sqlite3/3.16.2-5%2Bdeb9u1/deb...
In jq I believe you can get the asterisk behaviour with ‘map(...)’, but using significantly more characters.
for (i = n; i--; )
and I wouldn't say this idiom is that clear. You can get used to it, of course. Another interesting case is printing a list of items with separators. C loop constructs are biased cases of frequently used loops but they do not cover all the possible cases. 'goto' is unbiased.A similar case are Python loops. There are “Pythonic” ways:
for item in iterable
...
They look sweet but are useful only in some cases. E.g. if you need to modify `iterable`, the Pythonic way won’t work. There are Pythonic workarounds, but they may involve copying a whole iterable, which is hardly a good idea. But there is also a less Pythonic way: i = 0; n = len(iterable)
while i < n:
...
This works all the time and serves all possible cases. for i in range(len(iterable)):
... for i, item in enumerate(iterable):
...With an integer cursor (`i`) there are no unique numbers for each item and no temporary tuples. This is slightly cheaper. And also more flexible, which is the main point I'm trying to make. Other loop constructs are meant to be used in certain way and will not work otherwise. Integer cursors are not meant to be used in any special way, you can combine them as you wish. They give the maximum possible flexibility from the beginning. You can loop in any direction, by multiple items or by a variable number of items, over many iterables at once, changing the iterables as you go, and so on.
(A special case in Python are list comprehensions; these are useful because most of that “comprehending” happens inside CPython and thus they are faster.)
squared = (x*x for x in iterable)
In general, a generator function can be used if necessary: def squared(iterable):
for x in iterable:
yield x*xThese kinds of choice don't come alone, but together with the 'step' keyword, the opiniated absence of the 'continue' keyword and the availability or inner/embedded functions/procedures.
For me classic foreach iterators that mask the actual iterator fall apart when I need some kind of 'join' operation (do something specific on each first, last and neither elements of a collection).
Unless you want the check to be always against the initial length of your iterable, rather than its modified state, you'd have to repeat the `n = len(iterable)` inside the loop, or introduce some syntactic caramel of the walrus kind (though I'm not sure if it would work here).
while i < n:
if shouldDelete(iterable[i]):
del iterable[i]; n -= 1
else:
i += 1