home: hub: zuo

Download patch

ref: 244cb2a15ce8e48cde9bd7080526840d296c5b5c
parent: 66e9533beb55468e14d91738780d15c70c509f8a
author: Matthew Flatt <mflatt@racket-lang.org>
date: Sat Nov 12 05:01:17 CST 2022

Zuo: add 'dynamic-require to `zuo` module hash tables

The 'dynamic-require key at the Zuo primitive module layer provides a
way into the `zuo` module layer.

Also, fix `zuo_ext_apply` for embedding applications.

--- a/lib/zuo/private/base-common/entry.zuo
+++ b/lib/zuo/private/base-common/entry.zuo
@@ -19,7 +19,9 @@
       (kernel-eval (cons 'begin (cons '(void) body)))
       (hash 'macromod-provides outs
             'submodules submods
-            merge-bindings-export-key (make-export-merge-binds ctx (state-binds state))))))
+            merge-bindings-export-key (make-export-merge-binds ctx (state-binds state))
+            ;; for getting into this module world from the `zuo/kernel` module world:
+           'dynamic-require dynamic-require))))
 
 (hash
  ;; makes `#lang zuo/private/base[-hygienic] work:
@@ -26,7 +28,7 @@
  'read-and-eval (make-read-and-eval (lambda (ctx)
                                       (make-state (binds-create top-provides ctx)
                                                   (initial-nominals language-mod-path top-provides))))
- ;; makes `(require zuo/private/base[hygienic])` work:
+ ;; makes `(require zuo/private/base[-hygienic])` work:
  'macromod-provides top-provides
  ;; for making a new `#lang` with initial imports from `mod-path`:
  'make-language
@@ -42,4 +44,5 @@
               (merge-binds (make-state (binds-create provides ctx)
                                        (initial-nominals mod-path provides))
                            m-binds)))
-           'macromod-provides (hash-ref (module->hash mod-path) 'macromod-provides #f)))))
+           'macromod-provides (hash-ref (module->hash mod-path) 'macromod-provides #f)
+           'dynamic-require dynamic-require))))
--- a/zuo-doc/fake-zuo.rkt
+++ b/zuo-doc/fake-zuo.rkt
@@ -192,6 +192,7 @@
     build-module-path
     kernel-env
     kernel-eval
+    dynamic-require
 
     runtime-env
     dump-image-and-exit
--- a/zuo-doc/lang-zuo.scrbl
+++ b/zuo-doc/lang-zuo.scrbl
@@ -21,6 +21,17 @@
 @racketmodname[zuo/build], @racketmodname[zuo/shell], @racketmodname[zuo/thread],
 @racketmodname[zuo/glob], or @racketmodname[zuo/config].
 
+When using @racket[module->hash] on @racketmodname[zuo/base],
+@racketmodname[zuo], or a module implemented with one of those
+languages, the resulting hash table includes @racket['dynamic-require]
+mapped to the @racket[dynamic-require] function. Getting
+@racket[dynamic-require] that way provides a path from the primitive
+@seclink["module-protocol"]{Zuo kernel module protocol} to
+@racketmodname[zuo/base] module exports.
+
+@history[#:changed "1.2" @elem{Added the @racket['dynamic-require] key
+        for @racketmodname[zuo] and related languages.}]
+
 @section{Syntax and Evaluation Model}
 
 A @racketmodname[zuo] module consists of a sequence of definitions
--- a/zuo-doc/overview.scrbl
+++ b/zuo-doc/overview.scrbl
@@ -271,6 +271,15 @@
 looks for a @racket['main] submodule and runs it (i.e., calls the
 thunk) if present.
 
+The @racketmodname[zuo], @racketmodname[zuo/base], and
+@racketmodname[zuo/hygienic] languages do not specify how their
+provided-variable information is represented in a module hash table,
+but they do specify that @racket['dynamic-require] is mapped to the
+@racket[dynamic-require] function, and then @racket[dynamic-require]
+can be used to access provided values.
+
+@history[#:changed "1.2" @elem{Added the @racket['dynamic-require] key
+        for @racketmodname[zuo] and related languages.}]
 
 @section[#:tag "paths"]{Path Handling}
 
--- a/zuo.c
+++ b/zuo.c
@@ -3,7 +3,7 @@
    declarations. */
 
 #define ZUO_VERSION 1
-#define ZUO_MINOR_VERSION 1
+#define ZUO_MINOR_VERSION 2
 
 #if defined(_MSC_VER) || defined(__MINGW32__)
 # define ZUO_WINDOWS
@@ -7464,13 +7464,18 @@
 
 zuo_ext_t *zuo_ext_kernel_env() { return z.o_top_env; }
 zuo_ext_t *zuo_ext_apply(zuo_ext_t *proc, zuo_ext_t *args) {
-  /* special-case primtives, so this cna be used to perform primitive
+  /* special-case primtives, so this can be used to perform primitive
      operations without triggering a GC */
   if (proc->tag == zuo_primitive_tag) {
     zuo_primitive_t *f = (zuo_primitive_t *)proc;
     return f->dispatcher(f->proc, args);
-  } else
-    return zuo_kernel_eval(zuo_cons(proc, args));
+  } else {
+    /* quote arguments */
+    zuo_t *l, *quoted = z.o_null, *quote = zuo_symbol("quote");
+    for (l = zuo_cons(proc, args); l != z.o_null; l = _zuo_cdr(l))
+      quoted = zuo_cons(zuo_cons(quote, zuo_cons(_zuo_car(l), z.o_null)), quoted);
+    return zuo_kernel_eval(zuo_reverse(quoted));
+  }
 }
 
 void zuo_ext_runtime_init(zuo_ext_t *lib_path, zuo_ext_t *runtime_env) { zuo_runtime_init(lib_path, runtime_env); }
--- a/zuo.h
+++ b/zuo.h
@@ -78,6 +78,11 @@
    procedure. Arguments are in a list created with `zuo_ext_cons` and
    `zuo_ext_null`: */
 ZUO_EXPORT zuo_ext_t *zuo_ext_apply(zuo_ext_t *proc, zuo_ext_t *args);
+/* Note: Prior to version 1.2, this function was broken and evaluated
+   the procedure and arguments as expressions instead of using them as
+   values when `proc` is not a promitive. The following `#define`
+   reflects the repair: */
+#define ZUO_EXT_APPLY_ALWAYS_EXPECTS_VALUES 1
 
 /* ======================================================================== */
 /*
@@ -93,9 +98,15 @@
 /* After `zuo_ext_runtime_init`, all functionality is available. You
    can load a module from a file by extracting `module->hash` from the
    kernel env. Or you can declare and run a module directly from
-   source text, giveing it a module path that is eiter a symbolic
-   library path or a file path. */
+   source text, giving it a module path that is eiter a symbolic
+   library path or a file path.
 
+   Note that the result of the kernel's `module->hash` function or the
+   `zuo_ext_eval_module` is just a hash table. If the module is
+   implemented in `zuo` or a related language, you can use the symbol
+   `'dynamic-require` to get the `dynamic-require` function, and then
+   you can use that function to access provided values. */
+
 ZUO_EXPORT zuo_ext_t *zuo_ext_eval_module(zuo_ext_t *as_module_path, const char *content, long long len);
 
 /* For saving and retriving a value across an evaluation, which is
@@ -105,7 +116,9 @@
 
 #endif
 
-/* Here's a simple example embedding application: */
+/* ======================================================================== */
+/* Here's a simple example embedding application that makes an extra
+   primitive `random-five` available: */
 #if 0
 
 #include <stdio.h>
@@ -143,6 +156,57 @@
     printf("The answer was %d\n", (int)zuo_ext_integer_value(v));
   else
     printf("Something went wrong!\n");
+
+  return 0;
+}
+
+#endif
+
+/* ======================================================================== */
+/* Here's a example embedding application that doesn't need a new
+   primitive, but where the module is implemented with `#lang zuo`, so
+   we need to go through `dynamic-require` to get provided values: */
+#if 0
+
+#include <stdio.h>
+#include <string.h>
+#include "zuo.h"
+
+/* Link with a copy of "zuo.c" created by `zuo local/image.zuo` so
+   that the `zuo` module is available. */
+
+int main() {
+  const char *prog = "#lang zuo (provide main) (define (main) (+ 1 2))";
+  zuo_ext_t *ht, *dynamic_require, *main, *v;
+
+  /* Step 1 */
+  zuo_ext_primitive_init();
+
+  /* Step 2 */
+  zuo_ext_image_init(NULL);
+
+  /* Step 3 */
+  zuo_ext_runtime_init(zuo_ext_false(), zuo_ext_empty_hash());
+
+  /* Run `prog`: */
+  ht = zuo_ext_eval_module(zuo_ext_symbol("main-app"), prog, strlen(prog));
+
+  dynamic_require = zuo_ext_hash_ref(ht,
+                                     zuo_ext_symbol("dynamic-require"),
+                                     zuo_ext_false());
+
+  main = zuo_ext_apply(dynamic_require,
+                       zuo_ext_cons(zuo_ext_symbol("main-app"),
+                                    zuo_ext_cons(zuo_ext_symbol("main"),
+                                                 zuo_ext_null())));
+
+  v = zuo_ext_apply(main, zuo_ext_null());
+
+  printf("%s\n",
+         zuo_ext_string_ptr(zuo_ext_apply(zuo_ext_hash_ref(zuo_ext_kernel_env(),
+                                                           zuo_ext_symbol("~s"),
+                                                           zuo_ext_false()),
+                                          zuo_ext_cons(v, zuo_ext_null()))));
 
   return 0;
 }