Compare commits

..
3 Commits
Author SHA1 Message Date
Stephen Dolan 6ed2da6d43 Merge pull request #132 from dolmen/fix/github-urls
Fix Github URLs: stedolan.github.com -> stedolan.github.io
2013-05-23 09:16:34 -07:00
Olivier Mengué 444f0aacca Fix Github URLs: stedolan.github.com -> stedolan.github.io 2013-05-23 09:36:54 +02:00
Stephen Dolan 22006172ba fix typo and clean up docs build 2013-05-20 13:24:48 +01:00
35 changed files with 805 additions and 996 deletions

No files matched your search

-5
View File
@@ -1,7 +1,5 @@
*.o
*~
.*.sw[a-p]
tags
jq
jq.1
@@ -28,6 +26,3 @@ jq-*.tar.gz
configure
aclocal.m4
Makefile.in
version.h
.remake-version-h
config.cache
+17 -28
View File
@@ -2,12 +2,12 @@
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_file.h jv_parse.h jv_unicode.h locfile.h opcode_list.h parser.y \
jv_parse.h jv_unicode.h locfile.h opcode.h opcode_list.h parser.y \
jv_utf8_tables.h lexer.l
JQ_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 ${JQ_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
@@ -20,9 +20,9 @@ AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
# 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.
@@ -33,18 +33,12 @@ AM_YFLAGS = --warnings=all -d
### 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 = ${JQ_SRC} main.c
### Tests (make check)
TESTS = tests/all.test
@@ -62,16 +56,16 @@ 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; }
( 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; }
( 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.default
$(AM_V_GEN) cp $^ $@
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
@@ -99,16 +93,18 @@ 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 = config.h.in $(man_MANS) $(TESTS) \
EXTRA_DIST = config.h.in $(BUILT_SOURCES) $(man_MANS) $(TESTS) \
$(TEST_LOG_COMPILER) gen_utf8_tables.py jq.spec \
$(DOC_FILES) scripts/version \
parser.h parser.c lexer.h lexer.c
$(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: jq
@echo "Packaging jq as an RPM ..."
@@ -123,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
+6 -11
View File
@@ -9,20 +9,15 @@ documentation is generated from the docs/ folder of this repository.
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
./configure
make
sudo make install (optionally)
After make finishes, you'll be able to use `./jq`. You can also
install it using:
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:
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.
autoreconf
+32 -44
View File
@@ -1,15 +1,29 @@
#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_file.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)),
@@ -512,18 +526,20 @@ 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));
}
@@ -550,44 +566,16 @@ static const char* const jq_builtins[] = {
};
int builtins_bind_one(block* bb, const char* code) {
struct locfile src;
locfile_init(&src, 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;
}
int slurp_lib(block* bb) {
int nerrors = 0;
char* home = getenv("HOME");
if (home) { // silently ignore no $HOME
jv filename = jv_string_append_str(jv_string(home), "/.jq");
jv data = jv_load_file(jv_string_value(filename), 1);
if (jv_is_valid(data)) {
nerrors = builtins_bind_one(bb, jv_string_value(data) );
}
jv_free(filename);
jv_free(data);
}
return nerrors;
}
int builtins_bind(block* bb) {
int nerrors = slurp_lib(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(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);
}
+14 -2
View File
@@ -1,9 +1,21 @@
#ifndef BUILTIN_H
#define BUILTIN_H
#include "bytecode.h"
#include "compile.h"
int builtins_bind(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
+17 -49
View File
@@ -3,39 +3,9 @@
#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;
}
}
static int bytecode_operation_length(uint16_t* codeptr) {
int length = opcode_describe(*codeptr)->length;
if (*codeptr == CALL_JQ) {
@@ -44,22 +14,6 @@ static int bytecode_operation_length(uint16_t* codeptr) {
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;
@@ -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 -44
View File
@@ -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,8 +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);
void symbol_table_free(struct symbol_table* syms);
void bytecode_free(struct bytecode* bc);
#endif
+36 -28
View File
@@ -2,6 +2,7 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "opcode.h"
#include "compile.h"
#include "bytecode.h"
#include "locfile.h"
@@ -152,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);
@@ -272,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;
}
@@ -302,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),
@@ -330,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),
@@ -353,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,
@@ -392,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));
}
@@ -467,7 +475,7 @@ 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;
}
actual_args++;
+8 -5
View File
@@ -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);
+6 -13
View File
@@ -39,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 *
@@ -53,12 +50,8 @@ 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])
+11 -3
View File
@@ -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]
+3 -3
View File
@@ -31,7 +31,7 @@ body:
### 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 [64-bit binaries](osx64/jq) or [32-bit
binaries](osx32/jq)
@@ -47,7 +47,7 @@ body:
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
@@ -72,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
+2 -20
View File
@@ -113,11 +113,7 @@ sections:
"\u03bc"). Using this option, you can force jq to produce pure
ASCII output with every non-ASCII character replaced with the
equivalent escape sequence.
* `--sort-keys` / `-S`:
Output the fields of each object with the keys in sorted order.
* `--raw-output` / `-r`:
With this option, if the filter's result is a string then it
@@ -131,14 +127,6 @@ sections:
variable. If you run jq with `--arg foo bar`, then `$foo` is
available in the program and has the value `"bar"`.
* `--argfile name filename`:
This option passes the first value from the named file as a
value to the jq program as a predefined variable. If you run jq
with `--argfile foo bar`, then `$foo` is available in the
program and has the value resulting from parsing the content of
the file named `bar`.
- title: Basic filters
entries:
- title: "`.`"
@@ -160,13 +148,10 @@ sections:
- title: "`.foo`"
body: |
The simplest *useful* filter is `.foo`. When given a
The simplest *useful* filter is .foo. When given a
JSON object (aka dictionary or hash) as input, it produces
the value at the key "foo", or null if there's none present.
If the key contains special characters, you need to surround
it with double quotes like this: `."foo$"`.
examples:
- program: '.foo'
input: '{"foo": 42, "bar": "less interesting data"}'
@@ -174,9 +159,6 @@ sections:
- program: '.foo'
input: '{"notfoo": true, "alsonotfoo": false}'
output: ['null']
- program: '."foo"'
input: '{"foo": 42}'
output: [42]
- title: "`.[foo]`, `.[2]`, `.[10:15]`"
body: |
-82
View File
@@ -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
+147 -290
View File
@@ -5,9 +5,12 @@
#include "execute.h"
#include "exec_stack.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"
@@ -17,171 +20,61 @@
#include "builtin.h"
struct jq_state {
void (*nomem_handler)(void *);
void *nomem_handler_data;
struct bytecode* bc;
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;
stack_ptr env;
};
union frame_entry {
struct closure closure;
jv localvar;
};
struct frame {
struct bytecode* bc;
stack_ptr env;
stack_ptr retdata;
uint16_t* retaddr;
/* bc->nclosures closures followed by bc->nlocals local variables */
union frame_entry entries[0];
};
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) {
int subfn_idx = idx & ~ARG_NEWCLOSURE;
assert(subfn_idx < fr->bc->nsubfunctions);
struct closure cl = {fr->bc->subfunctions[subfn_idx],
fridx};
return cl;
} else {
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) {
@@ -196,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);
@@ -222,27 +114,26 @@ 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);
}
}
void print_error(jv value) {
assert(!jv_is_valid(value));
jv msg = jv_invalid_get_msg(value);
@@ -256,8 +147,6 @@ void print_error(jv value) {
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);
@@ -267,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;
@@ -304,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);
@@ -354,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,7 +272,8 @@ 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) {
@@ -399,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;
@@ -412,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);
@@ -427,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);
@@ -442,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);
@@ -458,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);
@@ -477,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;
@@ -568,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);
}
@@ -589,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;
}
@@ -603,26 +495,12 @@ 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 {
@@ -633,34 +511,37 @@ jv jq_next(jq_state *jq) {
}
case CALL_JQ: {
jv input = stack_pop(jq);
uint16_t nclosures = *pc++;
uint16_t* retaddr = pc + 2 + nclosures*2;
struct frame* new_frame = frame_push(jq, make_closure(jq, pc),
pc + 2, nclosures);
new_frame->retdata = jq->stk_top;
new_frame->retaddr = retaddr;
pc = new_frame->bc->code;
stack_push(jq, input);
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;
}
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): {
@@ -671,47 +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->path = jv_null();
return jq;
}
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) {
@@ -720,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, 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++) {
@@ -748,22 +611,16 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) {
jv_free(name);
}
jv_free(args);
nerrors = builtins_bind(&program);
if (nerrors == 0) {
nerrors = block_compile(program, &locations, &jq->bc);
}
program = builtins_bind(program);
nerrors = block_compile(program, &locations, &bc);
}
if (nerrors) {
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());
}
+6 -7
View File
@@ -2,20 +2,19 @@
#define EXECUTE_H
#include "bytecode.h"
typedef struct jq_state jq_state;
int jq_compile(jq_state *, const char* str);
struct bytecode* jq_compile(const char* str);
/* args must be an array of the form [{name:"foo", value:"thing"}, {name:"bar",value:3}] */
int jq_compile_args(jq_state *, const char* str, jv args);
struct bytecode* jq_compile_args(const char* str, jv args);
typedef struct jq_state jq_state;
enum {JQ_DEBUG_TRACE = 1};
jq_state *jq_init(void);
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
void jq_start(jq_state *, jv value, int flags);
void jq_init(struct bytecode* bc, jv value, jq_state **, int flags);
jv jq_next(jq_state *);
void jq_teardown(jq_state **);
void jq_dump_disassembly(jq_state *, int);
#endif
+172
View File
@@ -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
View File
@@ -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
-2
View File
@@ -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);
+8 -18
View File
@@ -10,16 +10,8 @@ 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);
}
+1 -1
View File
@@ -117,7 +117,7 @@ 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 };
enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2, JV_PRINT_COLOUR = 4 };
void jv_dump(jv, int flags);
jv jv_dump_string(jv, int flags);
-82
View File
@@ -2,89 +2,11 @@
#include <stdio.h>
#include "jv_alloc.h"
struct nomem_handler {
jv_nomem_handler_f handler;
void *data;
};
#ifndef USE_PTHREAD_KEY
#ifdef _MSC_VER
static __declspec(thread) struct nomem_handler nomem_handler;
#else
static __thread struct nomem_handler nomem_handler;
#endif
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
#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_init(void) {
if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
fprintf(stderr, "error: cannot create thread specific key");
abort();
}
}
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
pthread_once(&mem_once, tsd_init); // cannot fail
struct nomem_handler *nomem_handler = calloc(1, sizeof (nomem_handler));
if (nomem_handler == NULL) {
handler(data);
fprintf(stderr, "error: cannot allocate memory\n");
abort();
}
nomem_handler.handler = handler;
nomem_handler.data = data;
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
handler(data);
fprintf(stderr, "error: cannot set thread specific data");
abort();
}
}
static void memory_exhausted() {
jv_nomem_handler_f handler;
pthread_once(&mem_once, tsd_init);
handler = pthread_getspecific(nomem_handler_key);
if (handler)
handler(); // Maybe handler() will longjmp() to safety
// Or not
fprintf(stderr, "error: cannot allocate memory\n");
abort();
}
#else
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_PTHREAD_KEY */
void* jv_mem_alloc(size_t sz) {
void* p = malloc(sz);
if (!p) {
@@ -93,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);
}
-3
View File
@@ -14,10 +14,7 @@ static void jv_mem_invalidate(void* mem, size_t n) {
#endif
}
typedef void (*jv_nomem_handler_f)(void *);
void jv_nomem_handler(jv_nomem_handler_f, void *);
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);
-45
View File
@@ -1,45 +0,0 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jv.h"
#include "jv_aux.h"
#include "jv_parse.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();
jv_parser_init(&parser);
}
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, strlen(buf), !feof(file));
jv value;
while (jv_is_valid((value = jv_parser_next(&parser))))
data = jv_array_append(data, value);
}
}
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;
}
-8
View File
@@ -1,8 +0,0 @@
#ifndef JV_FILE_H
#define JV_FILE_H
#include "jv.h"
jv jv_load_file(const char *, int);
#endif
+1 -29
View File
@@ -5,7 +5,6 @@
#include "jv_dtoa.h"
#include "jv_unicode.h"
#include "jv_aux.h"
#define ESC "\033"
#define COL(c) (ESC "[" c "m")
@@ -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);
-1
View File
@@ -108,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]+ {}
-65
View File
@@ -1,65 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include "jv_alloc.h"
#include "locfile.h"
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+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, ...) {
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");
}
+60 -4
View File
@@ -1,6 +1,10 @@
#ifndef _LOCFILE_H
#define _LOCFILE_H
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include "jv_alloc.h"
typedef struct {
int start, end;
} location;
@@ -14,10 +18,62 @@ struct locfile {
int nlines;
};
void locfile_init(struct locfile* l, 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
+44 -54
View File
@@ -1,4 +1,3 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
@@ -6,30 +5,28 @@
#include <unistd.h>
#include "compile.h"
#include "jv.h"
#include "jv_file.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;
static void usage() {
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", JQ_VERSION);
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", PACKAGE_VERSION);
fprintf(stderr, "Usage: %s [options] <jq filter> [file...]\n\n", progname);
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.com/jq\n\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 documentation at http://stedolan.github.com/jq\n");
fprintf(stderr, "or see the jq documentation at http://stedolan.github.io/jq\n");
exit(1);
}
@@ -57,17 +54,18 @@ enum {
ASCII_OUTPUT = 32,
COLOUR_OUTPUT = 64,
NO_COLOUR_OUTPUT = 128,
SORTED_OUTPUT = 256,
FROM_FILE = 512,
FROM_FILE = 256,
/* debugging only */
DUMP_DISASM = 2048,
};
static int options = 0;
static struct bytecode* bc;
static void process(jq_state *jq, jv value, int flags) {
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) {
@@ -82,7 +80,6 @@ static void 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;
@@ -92,6 +89,30 @@ static void process(jq_state *jq, jv value, int flags) {
printf("\n");
}
jv_free(result);
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;
@@ -123,20 +144,11 @@ static int read_more(char* buf, size_t size) {
}
int main(int argc, char* argv[]) {
jq_state *jq;
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");
return 1;
return jq_testsuite(argc - 1, argv + 1);
}
const char* program = 0;
@@ -169,8 +181,6 @@ int main(int argc, char* argv[]) {
options |= NO_COLOUR_OUTPUT;
} else if (isoption(argv[i], 'a', "ascii-output")) {
options |= ASCII_OUTPUT;
} else if (isoption(argv[i], 'S', "sort-keys")) {
options |= SORTED_OUTPUT;
} else if (isoption(argv[i], 'R', "raw-input")) {
options |= RAW_INPUT;
} else if (isoption(argv[i], 'n', "null-input")) {
@@ -187,26 +197,6 @@ int main(int argc, char* argv[]) {
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, "argfile")) {
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);
return 1;
}
if (jv_array_length(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
} else if (isoption(argv[i], 0, "debug-dump-disasm")) {
options |= DUMP_DISASM;
} else if (isoption(argv[i], 0, "debug-trace")) {
@@ -214,7 +204,7 @@ int main(int argc, char* argv[]) {
} else if (isoption(argv[i], 'h', "help")) {
usage();
} else if (isoption(argv[i], 'V', "version")) {
printf("jq-%s\n", JQ_VERSION);
fprintf(stderr, "jq version %s\n", PACKAGE_VERSION);
return 0;
} else {
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
@@ -230,27 +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);
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);
bc = jq_compile_args(program, program_arguments);
}
if (!compiled) return 1;
if (!bc) return 1;
if (options & DUMP_DISASM) {
jq_dump_disassembly(jq, 0);
dump_disassembly(0, bc);
printf("\n");
}
if (options & PROVIDE_NULL) {
process(jq, jv_null(), jq_flags);
process(jv_null(), jq_flags);
} else {
jv slurped;
if (options & SLURP) {
@@ -271,7 +261,7 @@ int main(int argc, char* argv[]) {
slurped = jv_string_concat(slurped, jv_string(buf));
} else {
if (buf[len-1] == '\n') buf[len-1] = 0;
process(jq, jv_string(buf), jq_flags);
process(jv_string(buf), jq_flags);
}
}
} else {
@@ -281,7 +271,7 @@ int main(int argc, char* argv[]) {
if (options & SLURP) {
slurped = jv_array_append(slurped, value);
} else {
process(jq, value, jq_flags);
process(value, jq_flags);
}
}
if (jv_invalid_has_msg(jv_copy(value))) {
@@ -299,11 +289,11 @@ int main(int argc, char* argv[]) {
if (ret != 0)
goto out;
if (options & SLURP) {
process(jq, slurped, jq_flags);
process(slurped, jq_flags);
}
}
out:
jv_mem_free(input_filenames);
jq_teardown(&jq);
bytecode_free(bc);
return ret;
}
+31
View File
@@ -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;
}
}
+41
View File
@@ -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
+34 -41
View File
@@ -5,11 +5,12 @@
#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 { \
@@ -46,7 +47,6 @@ struct lexer_param;
%token INVALID_CHARACTER
%token <literal> IDENT
%token <literal> FIELD
%token <literal> LITERAL
%token <literal> FORMAT
%token EQ "=="
@@ -112,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) {
@@ -170,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)))));
}
@@ -226,7 +234,7 @@ Term "as" '$' IDENT '|' Exp {
"if" Exp "then" Exp ElseBody {
$$ = gen_cond($2, $4, $5);
} |
"if" Exp "then" error {
"if" Exp error {
FAIL(@$, "Possibly unterminated 'if' statment");
$$ = $2;
} |
@@ -335,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);
@@ -343,8 +351,8 @@ 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);
@@ -398,27 +406,12 @@ Term:
'.' {
$$ = gen_noop();
} |
Term FIELD {
$$ = gen_index($1, gen_const($2));
Term '.' IDENT {
$$ = gen_index($1, gen_const($3));
} |
FIELD {
$$ = gen_index(gen_noop(), gen_const($1));
'.' IDENT {
$$ = gen_index(gen_noop(), gen_const($2));
} |
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($1, $3);
@@ -457,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 {
-5
View File
@@ -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 --always --match 'jq-*' --dirty | sed 's/^jq-//'
-4
View File
@@ -115,10 +115,6 @@ null
{"foo": {"bar": 42}, "bar": "badvalue"}
42
."foo"."bar"
{"foo": {"bar": 20}}
20
#
# Multiple outputs, iteration