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.

Thanks. 

> Depending on how much you need to reset, you might be even better off
> with `dynamic-rerequire` from `racket/rerequire`.

Interesting. When would a rerequire be necessary, over a require? 
`dynamic-require` documentation says "Dynamically instantiates the module 
specified by mod in the current namespace’s registry at the namespace’s base 
phase, if it is not yet instantiated."
Is a module instantiation local to the current namespace? If so, it sounds like 
wiping the namespace is sufficient.

On Wednesday, July 26, 2017 at 12:27:09 PM UTC-4, Shu-Hung You wrote:
> Hi Thomas,
> 
> I tried following Matthew's suggestions to attach my-lang and use
> dynamic-require. Turns out that works pretty well! And then there's
> even no need to setup collection path since everything loads from
> compiled byte-code.
> https://gist.github.com/shhyou/aa2adaf7e1b7d548783cee352c3230a9
> 
> The code is basically calling (dynamic-require `(file ,argv-1) #f) to
> run the external script. At line 35, the code builds (list 'file
> argv-1) and at line 41 the code dynamic-requires the script.
> 
> /* custom_dsl.cpp
> 
> $ raco ctool --cgc --c-mods base.c ++lib racket/base ++lib
> racket/base/lang/reader ++lib racket/runtime-config ++lib my-lang
> ++lib my-lang/lang/reader
> 
> $ 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 custom_dsl.cpp -o
> custom_dsl -lracket -lmzgc -lrktio -liconv
> 
> $ ./custom_dsl ./a-module.rkt
> 
>  */
> #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("my-lang"));
> 
>   curout = scheme_get_param(scheme_current_config(),
>       MZCONFIG_OUTPUT_PORT);
> 
>   th = scheme_get_current_thread();
> 
>   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 \"" << argv[1] << "\"\n";
>     Scheme_Object *custom_dsl_path[2];
>     custom_dsl_path[0] = scheme_make_pair(
>                            scheme_intern_symbol("file"),
>                            scheme_make_pair(
>                              scheme_make_utf8_string(argv[1]),
>                              scheme_make_null()));
>     custom_dsl_path[1] = scheme_make_false();
>     Scheme_Object *custom_dsl = scheme_dynamic_require(2, custom_dsl_path);
>     th->error_buf = save;
>   }
>   return 0;
> }

Thanks for the continued help with this! I found that once I switched to using 
`dynamic-require` I didn't even need the initial `scheme_namespace_require`

The core of my script execution code now looks like this:

|               ret = boost::none;
|               Scheme_Object *drArgs[2] = {scheme_make_path(filename), 
resultSym_};
|               Scheme_Object *loadResult = scheme_dynamic_require(2, drArgs);
|               if(SCHEME_CPTRP(loadResult)){
|                       Scheme_Object *resT = SCHEME_CPTR_TYPE(loadResult);
|                       if(acceptedTypeTag_ == resT){
|                               ret = 
(AcceptedType*)SCHEME_CPTR_VAL(loadResult);
|                       } else {
|                               std::cerr << "Wrong type-tag received" << 
std::endl;
|                               Scheme_Object *curout = 
scheme_get_param(scheme_current_config(), MZCONFIG_ERROR_PORT);
|                               std::cerr << "Got: ";
|                               scheme_display(resT, curout);
|                               scheme_display(scheme_make_char('\n'), curout);
|                               std::cerr << "Wanted: ";
|                               scheme_display(acceptedTypeTag_, curout);
|                               scheme_display(scheme_make_char('\n'), curout);
|                       }
|               } else {
|                       std::cerr << "Result was not a cptr" << std::endl;
|               }



I'm now working to understand the exception handling mechanism (reading through 
https://docs.racket-lang.org/inside/exceptions.html and the example code).
The documentation appears to be mostly centered on what to do if your C code is 
being called from Racket (rather than vice-versa).

Looking at this code specifically:

|  mz_jmp_buf * volatile save, fresh;
|  save = scheme_current_thread->error_buf;
|  scheme_current_thread->error_buf = &fresh;
|
|  if (scheme_setjmp(scheme_error_buf)) {
|    /* There was an error or continuation invocation */
|    if (scheme_jumping_to_continuation) {
|      /* It was a continuation jump */
|      scheme_longjmp(*save, 1);
|
|      /* To block the jump, instead: scheme_clear_escape(); */
|    } else {
|      /* It was a primitive error escape */
|    }
|  } else {
|    scheme_eval_string("x", scheme_env);
|  }
|  scheme_current_thread->error_buf = save;

I have questions about a few things:
(a) Why does the example check for an error *before* calling 
`scheme_eval_string`, rather than afterward? Is this just something weird with 
setjmp/longjmp?
(b) How do I handle the "primitive error escape" branch, assuming I want to 
print a useful error message, and continue executing gracefully? It looks like 
`scheme_clear_escape` may be useful for preventing the exception from 
propagating, but I'm not sure about that from the documentation (or how I'd be 
able to capture the error message from the exception).
(c) Is this style of error handling only necessary around blocks that are 
evaluating actual Racket code (via, e.g., `scheme_eval_string` or 
`scheme_dynamic_require`)? What about the `scheme_register_embedded_load` and 
`scheme_embedded_load` pair at the bottom of `declare_modules`? I ran my binary 
through the Unix `strings` command and the embedded data looks an awful lot 
like expanded S-expressions in plain text. Additionally, at what level of 
granularity should I structure the error handling? Does it need to be for 
*every* evaluation, individually? Or could I surround a whole block of 
evaluations with the same error handling code?

Thanks again,
~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.

Reply via email to