Hi Thomas,

I think the program is more of Racket programming setup rather than
embedding Racket. When start a Racket program on our own, we have to
setup the environment correctly.

Here is my test program that can load an external program and the
accompanying commands (also attached at the end). It has been tested
the program on Mac OS X 10.11 with Racket head.
https://gist.github.com/shhyou/e424424236d1bc685fa002192221c29b

The program can also work for user-defined languages by adding ++lib
some-lang ++lib some-lang/lang/reader as usual.

Here are some thoughts:



* That error message looks like trying to run a program from an empty
namespace. For example,

$ cat a-module.rkt
#lang racket/base

123

$ cat run-from-empty-ns.rkt
#lang racket

(define host-ns (current-namespace))
(define ns (make-empty-namespace))

(parameterize ([current-namespace ns])
  (namespace-attach-module host-ns ''#%builtin)
  (load "a-module.rkt"))

$ racket run-from-empty-ns.rkt
a-module.rkt:2:0: #%top-interaction: unbound identifier;
 also, no #%app syntax transformer is bound
  at: #%top-interaction
  in: <DELETED>

As we can see, `scheme_namespace_require(scheme_intern_symbol("racket/base"));`
is actually missing from your setup program. This line requires racket
base and the usual #%app bindings. It's probably needed even if you're
loading your own DSL -- I suspect at some point racket/base will
install the right `load` function to make things work, and that this
will also affect loading custom #langs.



* 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/...



* 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.



Cheers,
Shu-Hung


# Commands:
$ raco ctool --c-mods base.c ++lib racket/base ++lib
racket/base/lang/reader ++lib racket/runtime-config

$ g++ -framework Foundation -I<RACKET-HEAD-REPO>/racket/include
-L<RACKET-HEAD-REPO>/racket/src/build/racket
-L<RACKET-HEAD-REPO>/racket/src/build/rktio call_racket.cpp -o
call_racket -lracket -lmzgc -lrktio -liconv
ld: warning: could not create compact unwind for _ffi_call_unix64:
does not use RBP or RSP based frame

$ ./call_racket "<RACKET-HEAD-REPO>/racket/collects"
In cpp main
Trying to load file from "a-module.rkt"
#<void>

In cpp: Instantiating the loaded module, `a-module`:
In Racket: module instantiated: ("this is in a function with" arg
"being the first argument")
12354

In cpp: get the value of `a-value`:
a-value is 12354



;; a-module.rkt
#lang racket/base

(provide (all-defined-out))

(define (a-binding-to-a-function x)
  (list "this is in a function with" x "being the first argument"))

(printf "In Racket: module instantiated: ~s\n" (a-binding-to-a-function 'arg))

(define a-value 12354)

;; By default, this should print 12354
a-value


/* call_racket.cpp */
/*
raco ctool --c-mods base.c ++lib racket/base ++lib
racket/base/lang/reader ++lib racket/runtime-config
g++ -framework Foundation -I<RACKET-HEAD-REPO>/racket/include
-L<RACKET-HEAD-REPO>/racket/src/build/racket
-L<RACKET-HEAD-REPO>/racket/src/build/rktio call_racket.cpp -o
call_racket -lracket -lmzgc -lrktio -liconv
./call_racket "<RACKET-HEAD-REPO>/racket/collects"
 */
#include <scheme.h>
#include <iostream>

#include "base.c"

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.c": */
  declare_modules(e);

  scheme_namespace_require(scheme_intern_symbol("racket/base"));

  curout = scheme_get_param(scheme_current_config(),
      MZCONFIG_OUTPUT_PORT);

  th = scheme_get_current_thread();

  Scheme_Object *path = scheme_make_path(argv[1]);
  {
    scheme_set_collects_path(path);

    /*
    // init collection paths with `((dynamic-require 'racket/base 'list) argv-1)
    Scheme_Object *rkt_list_sym[2], *args[1];
    rkt_list_sym[0] = scheme_intern_symbol("racket/base");
    rkt_list_sym[1] = scheme_intern_symbol("list");
    args[0] = path;
    Scheme_Object *paths = scheme_apply(scheme_dynamic_require(2,
rkt_list_sym), 1, args);
    scheme_init_collection_paths(e, paths);
    */
    scheme_init_collection_paths(e, scheme_null);
  }

  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 {
    std::cout << "Trying to load file from \"a-module.rkt\"\n";
    Scheme_Object *load_result = scheme_load("a-module.rkt");
    scheme_display(load_result, curout);
    scheme_display(scheme_make_char('\n'), curout);

    // Instantiate the loaded module
    std::cout << "\nIn cpp: Instantiating the loaded module, `a-module`:\n";
    scheme_eval_string("(dynamic-require ''a-module #f)", e);

    // Instead of scheme_eval_string, we can also built the S-expression of
    // (list 'quote 'a-module) then use scheme_dynamic_require
    std::cout << "\nIn cpp: get the value of `a-value`:\n";
    Scheme_Object *a_value = scheme_eval_string("(dynamic-require
''a-module 'a-value)", e);
    std::cout << "a-value is " << SCHEME_INT_VAL(a_value) << "\n";
    th->error_buf = save;
  }
  return 0;
}


int main(int argc, char **argv) {
  std::cout << "In cpp main\n";
  return scheme_main_setup(1, run, argc, argv);
}

On Tue, Jul 25, 2017 at 12:48 PM, Thomas Dickerson
<thomas_dicker...@brown.edu> wrote:
> I've been working on building a DSL in Racket (as a #lang collection), and 
> have reached the stage where it's functioning as expected and ready to be 
> embedded in my application (which is written in C++).
>
> 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 received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to