Add module directive, modulemeta builtin

Fix #425.
This commit is contained in:
Nicolas Williams committed 2014-08-14 03:26:26 -05:00
1 parent d654724fef
commit 1ba8c2cfa6
12 files changed
+265 -97

No files matched your search

+4
View File
@@ -8,6 +8,7 @@
#include "compile.h"
#include "jq_parser.h"
#include "bytecode.h"
#include "linker.h"
#include "locfile.h"
#include "jv_unicode.h"
@@ -812,6 +813,8 @@ static jv f_getpath(jq_state *jq, jv a, jv b) { return jv_getpath(a, b); }
static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); }
static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); }
static jv f_modulemeta(jq_state *jq, jv a) { return load_module_meta(jq, a); }
#define LIBM_DD(name) \
{(cfunction_ptr)f_ ## name, "_" #name, 1},
@@ -859,6 +862,7 @@ static const struct cfunction function_list[] = {
{(cfunction_ptr)f_format, "format", 2},
{(cfunction_ptr)f_env, "env", 1},
{(cfunction_ptr)f_match, "_match_impl", 4},
{(cfunction_ptr)f_modulemeta, "modulemeta", 1},
};
#undef LIBM_DD
+42 -16
View File
@@ -232,7 +232,7 @@ block block_join(block a, block b) {
int block_has_only_binders_and_imports(block binders, int bindflags) {
bindflags |= OP_HAS_BINDING;
for (inst* curr = binders.first; curr; curr = curr->next) {
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != DEPS) {
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != DEPS && curr->op != MODULEMETA) {
return 0;
}
}
@@ -242,7 +242,7 @@ int block_has_only_binders_and_imports(block binders, int bindflags) {
int block_has_only_binders(block binders, int bindflags) {
bindflags |= OP_HAS_BINDING;
for (inst* curr = binders.first; curr; curr = curr->next) {
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags) {
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != MODULEMETA) {
return 0;
}
}
@@ -341,10 +341,13 @@ block block_bind_library(block binder, block body, int bindflags, const char* li
assert(block_has_only_binders(binder, bindflags));
bindflags |= OP_HAS_BINDING;
int nrefs = 0;
int matchlen = strlen(libname)+2;
char* matchname = malloc(matchlen+1);
strcpy(matchname,libname);
strcpy(matchname+matchlen-2,"::");
int matchlen = strlen(libname);
char* matchname = calloc(1,matchlen+2+1);
if (libname[0] != '\0') {
strcpy(matchname,libname);
strcpy(matchname+matchlen,"::");
matchlen += 2;
}
for (inst *curr = binder.first; curr; curr = curr->next) {
char* cname = curr->symbol;
char* tname = malloc(strlen(curr->symbol)+matchlen+1);
@@ -425,11 +428,13 @@ jv block_take_imports(block* body) {
if (body->first->op == TOP) {
top = block_take(body);
}
while (body->first && body->first->op == DEPS) {
while (body->first && (body->first->op == MODULEMETA || body->first->op == DEPS)) {
inst* dep = block_take(body);
jv opts = jv_copy(dep->imm.constant);
opts = jv_object_set(opts,jv_string("name"),jv_string(dep->symbol));
imports = jv_array_append(imports, opts);
if (dep->op == DEPS) {
jv opts = jv_copy(dep->imm.constant);
opts = jv_object_set(opts,jv_string("name"),jv_string(dep->symbol));
imports = jv_array_append(imports, opts);
}
inst_free(dep);
}
if (top) {
@@ -438,15 +443,36 @@ jv block_take_imports(block* body) {
return imports;
}
block gen_import(const char* name, const char* as, const char* search) {
block gen_module(const char* name, block metadata) {
inst* i = inst_new(MODULEMETA);
i->symbol = strdup(name);
i->imm.constant = block_const(metadata);
if (jv_get_kind(i->imm.constant) != JV_KIND_OBJECT)
i->imm.constant = jv_object_set(jv_object(), jv_string("metadata"), i->imm.constant);
i->imm.constant = jv_object_set(i->imm.constant, jv_string("name"), jv_string(name));
block_free(metadata);
return inst_block(i);
}
jv block_module_meta(block b) {
if (b.first != NULL && b.first->op == MODULEMETA)
return jv_copy(b.first->imm.constant);
return jv_null();
}
block gen_import(const char* name, block metadata, const char* as) {
assert(metadata.first == NULL || block_is_const(metadata));
inst* i = inst_new(DEPS);
i->symbol = strdup(name);
jv opts = jv_object();
jv meta;
if (block_is_const(metadata))
meta = block_const(metadata);
else
meta = jv_object();
if (as)
opts = jv_object_set(opts, jv_string("as"), jv_string(as));
if (search)
opts = jv_object_set(opts, jv_string("search"), jv_string(search));
i->imm.constant = opts;
meta = jv_object_set(meta, jv_string("as"), jv_string(as));
i->imm.constant = meta;
block_free(metadata);
return inst_block(i);
}
+3 -1
View File
@@ -28,7 +28,9 @@ 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_import(const char* name, const char *as, const char *search);
block gen_module(const char* name, block metadata);
jv block_module_meta(block b);
block gen_import(const char* name, block metadata, const char *as);
block gen_function(const char* name, block formals, block body);
block gen_param_regular(const char* name);
block gen_param(const char* name);
+17 -21
View File
@@ -984,38 +984,34 @@ int jq_compile(jq_state *jq, const char* str) {
return jq_compile_args(jq, str, jv_array());
}
void jq_set_lib_origin(jq_state *jq, jv origin) {
assert(jq);
assert(jv_get_kind(origin) == JV_KIND_STRING);
jq_set_attr(jq, jv_string("ORIGIN"), origin);
}
jv jq_get_lib_origin(jq_state *jq) {
assert(jq);
return jq_get_attr(jq, jv_string("ORIGIN"));
return jq_get_attr(jq, jv_string("ORIGIN"));
}
void jq_set_lib_dirs(jq_state *jq, jv dirs) {
assert(jq);
assert(jv_get_kind(dirs) == JV_KIND_ARRAY);
jq_set_attr(jq, jv_string("LIB_DIRS"), dirs);
}
jv jq_get_lib_dirs(jq_state *jq) {
assert(jq);
return jq_get_attr(jq, jv_string("LIB_DIRS"));
return jq_get_attr(jq, jv_string("LIB_DIRS"));
}
jv jq_get_version_dir(jq_state *jq) {
jv d = jq_get_attr(jq, jv_string("VERSION_DIR"));
assert(jv_is_valid(d));
return d;
}
void jq_set_attrs(jq_state *jq, jv attrs) {
assert(jv_get_kind(attrs) == JV_KIND_OBJECT);
jv_free(jq->attrs);
jq->attrs = attrs;
}
void jq_set_attr(jq_state *jq, jv attr, jv val) {
assert(jq);
assert(jv_get_kind(attr) == JV_KIND_STRING);
assert(jv_is_valid(val));
jq->attrs = jv_object_set(jq->attrs, attr, val);
jq->attrs = jv_object_set(jq->attrs, attr, val);
}
jv jq_get_attr(jq_state *jq, jv attr) {
assert(jq);
assert(jv_get_kind(attr) == JV_KIND_STRING);
return jv_object_get(jv_copy(jq->attrs), attr);
return jv_object_get(jv_copy(jq->attrs), attr);
}
void jq_dump_disassembly(jq_state *jq, int indent) {
dump_disassembly(indent, jq->bc);
}
+8 -7
View File
@@ -15,17 +15,18 @@ void jq_get_error_cb(jq_state *, jq_err_cb *, void **);
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
jv jq_format_error(jv msg);
void jq_report_error(jq_state *, jv);
int jq_compile(jq_state *, const char* str);
int jq_compile_args(jq_state *, const char* str, jv args);
int jq_compile(jq_state *, const char*);
int jq_compile_args(jq_state *, const char*, jv);
void jq_dump_disassembly(jq_state *, int);
void jq_start(jq_state *, jv value, int flags);
void jq_start(jq_state *, jv value, int);
jv jq_next(jq_state *);
void jq_teardown(jq_state **);
void jq_set_lib_origin(jq_state *, jv origin);
void jq_set_attrs(jq_state *, jv);
jv jq_get_attrs(jq_state *);
jv jq_get_lib_origin(jq_state *);
void jq_set_lib_dirs(jq_state *, jv dirs);
jv jq_get_lib_dirs(jq_state *);
void jq_set_attr(jq_state *, jv attr, jv val);
jv jq_get_attr(jq_state *, jv attr);
jv jq_get_version_dir(jq_state *);
void jq_set_attr(jq_state *, jv, jv);
jv jq_get_attr(jq_state *, jv);
#endif /* !_JQ_H_ */
+2 -3
View File
@@ -42,8 +42,8 @@ struct lexer_param;
"!=" { return NEQ; }
"==" { return EQ; }
"as" { return AS; }
"search" { return SEARCH; }
"import" { return IMPORT; }
"module" { return MODULE; }
"def" { return DEF; }
"if" { return IF; }
"then" { return THEN; }
@@ -115,8 +115,7 @@ 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]*::[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
([a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
\.[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext+1); return FIELD;}
[ \n\t]+ {}
+113 -19
View File
@@ -1,4 +1,5 @@
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
@@ -54,35 +55,95 @@ jv build_lib_search_chain(jq_state *jq, jv lib_path) {
return out_paths;
}
static jv find_lib(jq_state *jq, jv lib_name, jv lib_search_path) {
static jv name2relpath(jv name) {
jv components = jv_string_split(jv_copy(name), jv_string("::"));
jv rp = jv_array_get(jv_copy(components), 0);
components = jv_array_slice(components, 1, jv_array_length(jv_copy(components)));
jv_array_foreach(components, i, x) {
if (i > 0 && jv_equal(jv_copy(x), jv_array_get(jv_copy(components), i - 1))) {
jv_free(x);
jv_free(rp);
jv_free(components);
jv res = jv_invalid_with_msg(jv_string_fmt("module names must not have equal consecutive components: %s",
jv_string_value(name)));
jv_free(name);
return res;
}
rp = jv_string_concat(rp, jv_string_concat(jv_string("/"), x));
}
jv_free(components);
jv_free(name);
return rp;
}
static jv find_lib(jq_state *jq, jv lib_name, jv lib_search_path, int use_vers_dir) {
assert(jv_get_kind(lib_search_path) == JV_KIND_STRING);
assert(jv_get_kind(lib_name) == JV_KIND_STRING);
lib_search_path = expand_path(lib_search_path);
jv rel_path = name2relpath(jv_copy(lib_name));
if (!jv_is_valid(rel_path)) {
jv_free(lib_name);
return rel_path;
}
jv version_dirs;
if (use_vers_dir) {
version_dirs = JV_ARRAY(jv_string("any/"),
jv_string_concat(jq_get_version_dir(jq),
jv_string("/")));
} else {
version_dirs = JV_ARRAY(jv_string(""));
}
struct stat st;
int ret;
jv lib_search_paths = build_lib_search_chain(jq, lib_search_path);
jv lib_search_paths = build_lib_search_chain(jq, expand_path(lib_search_path));
jv_array_foreach(lib_search_paths, i, spath) {
jv testpath = jq_realpath(jv_string_fmt("%s/%s.jq",jv_string_value(spath),jv_string_value(lib_name)));
jv_free(spath);
ret = stat(jv_string_value(testpath),&st);
if (ret == 0) {
jv_free(lib_name);
jv_free(lib_search_paths);
return testpath;
jv vds = jv_copy(version_dirs);
jv_array_foreach(vds, k, vd) {
jv testpath = jq_realpath(jv_string_fmt("%s/%s%s.jq",
jv_string_value(spath),
jv_string_value(vd),
jv_string_value(rel_path)));
ret = stat(jv_string_value(testpath),&st);
if (ret == -1 && errno == ENOENT) {
jv_free(testpath);
testpath = jq_realpath(jv_string_fmt("%s/%s%s/%s.jq",
jv_string_value(spath),
jv_string_value(vd),
jv_string_value(rel_path),
jv_string_value(lib_name)));
ret = stat(jv_string_value(testpath),&st);
}
jv_free(vd);
if (ret == 0) {
jv_free(spath);
jv_free(vds);
jv_free(version_dirs);
jv_free(rel_path);
jv_free(lib_name);
jv_free(lib_search_paths);
return testpath;
}
jv_free(testpath);
}
jv_free(testpath);
jv_free(vds);
jv_free(spath);
}
jv output = jv_invalid_with_msg(jv_string_fmt("could not find library: %s", jv_string_value(lib_name)));
jv output = jv_invalid_with_msg(jv_string_fmt("module not found: %s", jv_string_value(lib_name)));
jv_free(version_dirs);
jv_free(rel_path);
jv_free(lib_name);
jv_free(lib_search_paths);
return output;
}
static int version_matches(jq_state *jq, block importer, block module) {
return 1;
}
static int process_dependencies(jq_state *jq, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {
jv deps = block_take_imports(src_block);
block bk = *src_block;
@@ -93,19 +154,20 @@ static int process_dependencies(jq_state *jq, jv lib_origin, block *src_block, s
jv as = jv_object_get(jv_copy(dep), jv_string("as"));
if (!jv_is_valid(as)) {
jv_free(as);
as = jv_copy(name);
as = jv_string("");
}
jv search = jv_object_get(dep, jv_string("search"));
if (!jv_is_valid(search)) {
jv_free(search);
search = jv_string("");
}
if (strncmp("$ORIGIN/",jv_string_value(search),8) == 0) {
int has_origin = (strncmp("$ORIGIN/",jv_string_value(search),8) == 0);
if (has_origin) {
jv tsearch = jv_string_fmt("%s/%s",jv_string_value(lib_origin),jv_string_value(search)+8);
jv_free(search);
search = tsearch;
}
jv lib_path = find_lib(jq, name, search);
jv lib_path = find_lib(jq, name, search, !has_origin);
if (!jv_is_valid(lib_path)) {
jv emsg = jv_invalid_get_msg(lib_path);
jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg)));
@@ -121,13 +183,23 @@ static int process_dependencies(jq_state *jq, jv lib_origin, block *src_block, s
break;
}
if (state_idx < lib_state->ct) { // Found
bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, jv_string_value(as));
// XXX Check version matching here!
if (version_matches(jq, bk, lib_state->defs[state_idx]))
bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, jv_string_value(as));
else
// XXX Would be nice to have the dependent's name here too
jq_report_error(jq, jv_string_fmt("jq: error: version mismatch for %s", jv_string_value(name)));
jv_free(lib_path);
} else { // Not found. Add it to the table before binding.
block dep_def_block = gen_noop();
nerrors += load_library(jq, lib_path, &dep_def_block, lib_state);
if (nerrors == 0)
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, jv_string_value(as));
if (nerrors == 0) {
// XXX Check version matching here!
if (version_matches(jq, bk, dep_def_block))
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, jv_string_value(as));
else
jq_report_error(jq, jv_string_fmt("jq: error: version mismatch for %s", jv_string_value(name)));
}
}
jv_free(as);
}
@@ -165,6 +237,28 @@ static int load_library(jq_state *jq, jv lib_path, block *out_block, struct lib_
return nerrors;
}
jv load_module_meta(jq_state *jq, jv modname) {
jv lib_path = find_lib(jq, modname, jv_string(""), 1);
jv meta = jv_null();
jv data = jv_load_file(jv_string_value(lib_path), 1);
if (jv_is_valid(data)) {
block program;
struct locfile* src = locfile_init(jq, jv_string_value(data), jv_string_length_bytes(jv_copy(data)));
int nerrors = jq_parse_library(src, &program);
if (nerrors == 0) {
meta = block_module_meta(program);
if (jv_get_kind(meta) == JV_KIND_NULL)
meta = jv_object();
meta = jv_object_set(meta, jv_string("deps"), block_take_imports(&program));
}
locfile_free(src);
block_free(program);
}
jv_free(lib_path);
jv_free(data);
return meta;
}
int load_program(jq_state *jq, struct locfile* src, block *out_block) {
int nerrors = 0;
block program;
+1 -3
View File
@@ -2,8 +2,6 @@
#define LINKER_H
int load_program(jq_state *jq, struct locfile* src, block *out_block);
jv load_module_meta(jq_state *jq, jv modname);
#endif
+7 -2
View File
@@ -352,16 +352,21 @@ int main(int argc, char* argv[]) {
lib_search_paths = jv_array_concat(lib_search_paths,jv_string_split(jv_string(penv),jv_string(PATH_ENV_SEPARATOR)));
#undef PATH_ENV_SEPARATOR
}
jq_set_lib_dirs(jq,lib_search_paths);
jq_set_attr(jq, jv_string("LIB_DIRS"), lib_search_paths);
char *origin = strdup(argv[0]);
if (origin == NULL) {
fprintf(stderr, "Error: out of memory\n");
exit(1);
}
jq_set_lib_origin(jq,jv_string(dirname(origin)));
jq_set_attr(jq, jv_string("ORIGIN"), jv_string(dirname(origin)));
free(origin);
if (strchr(JQ_VERSION, '-') == NULL)
jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION));
else
jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string("next"));
#if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY)
#if defined(HAVE__ISATTY) && defined(isatty)
+1
View File
@@ -38,3 +38,4 @@ OP(CLOSURE_CREATE_C, DEFINITION, 0, 0)
OP(TOP, NONE, 0, 0)
OP(CLOSURE_PARAM_REGULAR, DEFINITION, 0, 0)
OP(DEPS, CONSTANT, 0, 0)
OP(MODULEMETA, CONSTANT, 0, 0)
+38 -16
View File
@@ -56,8 +56,8 @@ struct lexer_param;
%token NEQ "!="
%token DEFINEDOR "//"
%token AS "as"
%token SEARCH "search"
%token DEF "def"
%token MODULE "module"
%token IMPORT "import"
%token IF "if"
%token THEN "then"
@@ -104,7 +104,7 @@ struct lexer_param;
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString
%type <blk> FuncDef FuncDefs String Import Imports Param Params
%type <blk> Arg Args
%type <blk> Arg Args Module
%{
#include "lexer.h"
struct lexer_param {
@@ -263,13 +263,27 @@ static block gen_update(block object, block val, int optype) {
%%
TopLevel:
Imports Exp {
*answer = BLOCK($1, gen_op_simple(TOP), $2);
Module Imports Exp {
*answer = BLOCK($1, $2, gen_op_simple(TOP), $3);
} |
Imports FuncDefs {
*answer = BLOCK($1, $2);
Module Imports FuncDefs {
*answer = BLOCK($1, $2, $3);
}
Module:
%empty {
$$ = gen_noop();
} |
"module" IDENT Exp ';' {
if (!block_is_const($3)) {
FAIL(@$, "Module metadata must be constant.");
$$ = gen_noop();
} else {
$$ = gen_module(jv_string_value($2), $3);
}
jv_free($2);
}
Imports:
%empty {
$$ = gen_noop();
@@ -447,24 +461,32 @@ Term {
Import:
"import" IDENT ';' {
$$ = gen_import(jv_string_value($2), NULL, NULL);
$$ = gen_import(jv_string_value($2), gen_noop(), NULL);
jv_free($2);
} |
"import" IDENT Exp ';' {
if (!block_is_const($3)) {
FAIL(@$, "Module metadata must be constant.");
$$ = gen_noop();
} else {
$$ = gen_import(jv_string_value($2), $3, NULL);
}
jv_free($2);
} |
"import" IDENT "as" IDENT ';' {
$$ = gen_import(jv_string_value($2), jv_string_value($4), NULL);
$$ = gen_import(jv_string_value($2), gen_noop(), jv_string_value($4));
jv_free($2);
jv_free($4);
} |
"import" IDENT "as" IDENT "search" QQSTRING_START QQSTRING_TEXT QQSTRING_END ';' {
$$ = gen_import(jv_string_value($2), jv_string_value($4), jv_string_value($7));
"import" IDENT "as" IDENT Exp ';' {
if (!block_is_const($5)) {
FAIL(@$, "Module metadata must be constant.");
$$ = gen_noop();
} else {
$$ = gen_import(jv_string_value($2), $5, jv_string_value($4));
}
jv_free($2);
jv_free($4);
jv_free($7);
} |
"import" IDENT "search" QQSTRING_START QQSTRING_TEXT QQSTRING_END ';' {
$$ = gen_import(jv_string_value($2), NULL, jv_string_value($5));
jv_free($2);
jv_free($5);
}
FuncDef:
+29 -9
View File
@@ -71,6 +71,12 @@ if [ $n -ne 5 ]; then
exit 1
fi
v=`scripts/version`
case "$v" in
*-*) v=next;;
*) true;;
esac
## Test library/module system
cat > "$d/.jq" <<EOF
@@ -81,22 +87,31 @@ def g: "bar";
def fg: f+g;
EOF
cat > "$d/a.jq" <<EOF
mkdir -p "$d/$v/b" "$d/any/c" "$d/any/syntaxerror"
cat > "$d/$v/a.jq" <<EOF
module a {version:1.7};
def a: "a";
EOF
cat > "$d/b.jq" <<EOF
cat > "$d/$v/b/b.jq" <<EOF
def a: "b";
def b: "c";
EOF
cat > "$d/c.jq" <<EOF
import a as foo;
def a: 0;
def c: foo::a + "c";
cat > "$d/any/c/d.jq" <<EOF
def meh: "meh";
EOF
cat > "$d/syntaxerror.jq" <<EOF
cat > "$d/any/c/c.jq" <<"EOF"
module c {whatever:null};
import a as foo;
import d as d {search:"$ORIGIN/"};
def a: 0;
def c: foo::a + "c" + d::meh;
EOF
cat > "$d/any/syntaxerror/syntaxerror.jq" <<EOF
wat;
EOF
@@ -110,16 +125,21 @@ if [ `HOME=$d $VALGRIND $Q ./jq --debug-dump-disasm -n fg | grep '^[a-z]' | wc -
exit 1
fi
if ! $VALGRIND $Q ./jq -ner -L $d 'import a as foo; import b as bar; import a as foobar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a, foobar::a] | . == ["a","b","c","a","a"]' > /dev/null; then
if ! $VALGRIND $Q ./jq -ner -L $d 'import a as foo; import b as bar; import a; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a, a] | . == ["a","b","c","a","a"]' > /dev/null; then
echo "Module system appears to be broken" 1>&2
exit 1
fi
if ! $VALGRIND $Q ./jq -ner -L $d 'import c as foo; [foo::a, foo::c] | . == [0,"ac"]' > /dev/null; then
if ! $VALGRIND $Q ./jq -ner -L $d 'import c as foo; [foo::a, foo::c] | . == [0,"acmeh"]' > /dev/null; then
echo "Module system appears to be broken" 1>&2
exit 1
fi
if [ "`$VALGRIND $Q ./jq -cner -L $d '\"c\" | modulemeta'`" != '{"whatever":null,"name":"c","deps":[{"as":"foo","name":"a"},{"search":"$ORIGIN/","as":"d","name":"d"}]}' ]; then
echo "modulemeta builtin appears to be broken" 1>&2
exit 1
fi
if $VALGRIND ./jq -ner -L $d 'import syntaxerror; .' > $d/out 2>&1; then
echo "Module system appears to be broken" 1>&2
exit 1