]> granicus.if.org Git - jq/commitdiff
Add `module` directive, `modulemeta` builtin
authorNicolas Williams <nico@cryptonector.com>
Sun, 10 Aug 2014 21:52:03 +0000 (16:52 -0500)
committerNicolas Williams <nico@cryptonector.com>
Thu, 14 Aug 2014 08:26:26 +0000 (03:26 -0500)
    Fix #425.

12 files changed:
builtin.c
compile.c
compile.h
execute.c
jq.h
lexer.l
linker.c
linker.h
main.c
opcode_list.h
parser.y
tests/run

index 698ad403b12cff475aef54194fc88502576b2f13..711bc473c433b0ea14c80a568b9d105202585d91 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -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
 
index 92189687ac54a98a8e6ed2756dbd2b0d204df3ed..57a26d54dc7f45cdfb33583baa559f816b2b07bd 100644 (file)
--- a/compile.c
+++ b/compile.c
@@ -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);
 }
 
index b0162ae21baa4775b74f664e6b8ac5587d08acc5..f9cd238ccc1508e08e58e14720c5086e6813c482 100644 (file)
--- a/compile.h
+++ b/compile.h
@@ -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);
index 53b67c18df71a2f03370e10761bc0dba35cc2665..97b33d3d3fea2472d7bdd2e3cb6f44414642b2d9 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -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);
 }
diff --git a/jq.h b/jq.h
index 1849f661cdb15426677c7672d6a4007705e49cb7..b517c529894205a4c378792390e1541105a8ee85 100644 (file)
--- a/jq.h
+++ b/jq.h
@@ -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_ */
diff --git a/lexer.l b/lexer.l
index 8205e69e722ef0e052331cb6fbe819599f11f72b..7813850f0431f7c5617d6432f1f09464a0c70dbd 100644 (file)
--- a/lexer.l
+++ b/lexer.l
@@ -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]+  {}
index 016870d54e84c5caa76d2a8291b657d6849588d7..6aaa7d79bd156aba313532a929151c08a75d93d8 100644 (file)
--- a/linker.c
+++ b/linker.c
@@ -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;
index 4e9f2adf95b6e564d5f9e47ec11775d592ef276c..3f682ca28bfc0704efea783ada2a996b7104d8eb 100644 (file)
--- a/linker.h
+++ b/linker.h
@@ -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
diff --git a/main.c b/main.c
index 5008f8b2cd09cace19ebaf415c027a3a9baae527..dec1f9f08d5418b6f9d003198a772b8cbdbea16c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -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)
index 119a70d32e8e61cce300a31d3954a024d62ec948..db9242f964a03a6de55d821d017bdb7fc4fde4fb 100644 (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)
index c2b1b478dfc95752ac09362f75cc96e7d723550b..44ae009690b61fafde0784ba1f9e406e97d36385 100644 (file)
--- a/parser.y
+++ b/parser.y
@@ -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 "as" IDENT ';' {
-  $$ = gen_import(jv_string_value($2), jv_string_value($4), NULL);
+"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);
-  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 ';' {
+  $$ = gen_import(jv_string_value($2), gen_noop(), 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));
+"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($5);
+  jv_free($4);
 }
 
 FuncDef:
index 878128de971c54e79ebae61fcb8dfafe58abab91..8a4ef0e74af12a2887b52aa1101ec54192d01ea9 100755 (executable)
--- a/tests/run
+++ b/tests/run
@@ -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
+cat > "$d/any/c/d.jq" <<EOF
+def meh: "meh";
+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";
+def c: foo::a + "c" + d::meh;
 EOF
 
-cat > "$d/syntaxerror.jq" <<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