I have modified the `run` function from the embedding example here (https://docs.racket-lang.org/inside/embedding.html) as follows:
|static int run(Scheme_Env *e, int argc, char *argv[]) {
| Scheme_Object *curout;
| int i;
| Scheme_Thread *th;
|
| mz_jmp_buf * volatile save, fresh;
|
| /* Declare embedded modules in "base.hpp": */
| declare_modules(e);
| /*** Alternatively (see email commentary below):
| scheme_set_collects_path(scheme_make_path(argv[1]));
| scheme_init_collection_paths(e, scheme_null);
| */
| curout = scheme_get_param(scheme_current_config(),
| MZCONFIG_OUTPUT_PORT);
|
| th = scheme_get_current_thread();
| for (i = 2; i < argc; i++) {
| save = th->error_buf;
| th->error_buf = &fresh;
|
| if (scheme_setjmp(*th->error_buf)) {
| th->error_buf = save;
| return -1; /* There was an error */
| } else {
| Scheme_Object *v, *a[2];
| v = scheme_load(argv[i]);
| scheme_display(v, curout);
| scheme_display(scheme_make_char('\n'), curout);
| th->error_buf = save;
| }
| }
|
| return 0;
|}
When I run my program, so that the file to be executed is in `argv[2]`, I get an error message that begins as follows:
|../libraries/dsl-exec/racket-src/gpsm-test.gpsm:1:0: #%top-interaction: unbound identifier;
| also, no #%app syntax transformer is bound
| at: #%top-interaction
Note that when I run programs written in my DSL with a standalone interpreter, this error does not appear, and I *explicitly* export `#%top-interaction`, `#%app`, `#%module-begin`, and `#%datum`.
I noticed some oddities when using `raco ctool`. For example, the documentation claims that a module specified for embedding with ++lib will embed all of its dependencies, but my #lang's reader submodule needed to be explicitly specified with its own ++lib argument. To see if this was a contributing factor, I also tried using a manual `scheme_set_collects_path` and `scheme_init_collections_paths` combo (with `argv[1]` pointing to the location of my collects dir on disk), but got the same error as with the embedded versions of the modules.
As a follow-up, using the manually-specified collections path variant of the code above, I also tried to execute the following simple Racket program (to isolate if the use of my #lang specifically was a contributing factor):
|#lang racket
|
|(display (+ 5 6))
|(newline)
The same error as before was immediately raised:
|racket-test.rkt:1:0: #%top-interaction: unbound identifier;
| also, no #%app syntax transformer is bound
| at: #%top-interaction
I assume I'm missing some crucial piece of the embedded Racket puzzle, but I have no idea what it is.
(I might also recommend that whatever this missing piece is be *clearly* documented in future editions of the documentation, since it seems like a fairly basic use-case).
Thanks,
~Thomas
You appear to be correct, but it strikes me as really odd that
(a) In a language which prides itself on metaprogramming, the interpreter namespace needs to be manually bootstrapped with a specific module.
(b) That the form the error takes is not an error with the requires/provides in the `main.rkt` for my #lang, but as an error in a module which is using it.
This code:
|#lang racket
|(require "Value-Forms.rkt"
| "Operator-Forms.rkt"
| "Variable-Forms.rkt")
|
|(provide
| #%module-begin
| #%top-interaction
| #%datum
| #%app
| (all-from-out "Value-Forms.rkt"
| "Operator-Forms.rkt"
| "Variable-Forms.rkt"))
Should definitely complain if its imports aren't available.
> * Dependencies for main.rkt and lang/reader.rkt: I don't think there's
> a conflict. The dependencies seem to refer to `require`s, like
> racket/base -> racket/list, racket/...
Relatedly, even if `main.rkt` and `lang/reader.rkt` are independent, it's really odd that my module beginning with `#lang racket` is insufficient for raco to pick up the racket dependencies.
> * load: instead of using load, I would probably try to implement a
> function in Racket that use `dynamic-require` to load other DSL
> scripts. Then in the C part, we can `dynamic-require` that function to
> load argv[1]. Of course, remember to add that module in raco ctool. An
> example is given in load-file.rkt and call_racket_from_load.cpp in the
> link.
>
> I am not familiar with this; there could be a better way to
> dynamically load a file.
>
Thanks, I'll think some more about this.
> # Commands:
> $ raco ctool --c-mods base.c ++lib racket/base ++lib
> racket/base/lang/reader ++lib racket/runtime-config
This appears to be precisely the necessary set of minimum ++libs. It *really* ought to be documented somewhere, in big bold letters.
Thanks for the help!
~Thomas
On Wednesday, July 26, 2017 at 9:15:12 AM UTC-4, Matthew Flatt wrote:
> You don't have to populate the top-level environment with
> `racket/base`. You could instead use
>
> scheme_namespace_require(scheme_intern_symbol("my-lang"))
>
> where "my-lang" is your language's main module to populate the
> environment from there. Still, this isn't really the right direction.
I actually tried this without much success, but I might still have been missing something.
> Overall, I think you want to `scheme_dynamic_require` instead of
> `scheme_load`, just as Shu-Hung suggests. Using
> `scheme_dynamic_require` corresponds to what `racket` does where you
> provide it a file on the command line, and it avoids the top-level
> environment and `load`.
Just tried this, and it worked great, thanks! I was having a little trouble getting the `scheme_eval` variant that constructed a `dynamic-require` form manually to work correctly, so knowing about `scheme_dynamic_require` is helpful.
> I can see how the "Inside Racket" example lead you astray by showing a
> traditional REPL, where `eval` and `load` come into play, and I'll look
> at ways to improve the docs. But if your goal is to run modules, then
> you can avoid all that trouble.
Thanks!
One more thing: in terms of repeatedly executing scripts, does it make sense to set up and tear down the interpreter every time? Or just swap in a fresh namespace?
Good to know.
> The escape-catching pattern is needed anywhere that you don't want to
> just exit/crash.
>
> You can certainly call multiple `scheme_...` functions within a single
> escape-handling block, including multiple calls to `scheme_eval_string`.
Also good to know, thanks.
On Wednesday, July 26, 2017 at 11:09:48 AM UTC-4, Matthew Flatt wrote:
> At Wed, 26 Jul 2017 07:54:32 -0700 (PDT), Thomas Dickerson wrote:
> > One more thing: in terms of repeatedly executing scripts, does it make sense
> > to set up and tear down the interpreter every time? Or just swap in a fresh
> > namespace?
>
> Between those two options, a fresh namespace is almost certainly
> better.
For posterity, it's worth noting that you can use the Boost Coroutine library to implement this in C++ in a nice object-oriented fashion to make a loop that executes Racket tasks in a way that doesn't require bouncing your `main` through `scheme_main_setup` or equivalent, which is great if you're embedding a racket interpreter to be reused as an API rather than a one-off program.