[racket] Evaluating code written in non-SEXP language

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Sep 13 09:07:04 EDT 2013

You have a reader for your language that is triggered by "#lang",
right?

Is that reader in a `reader` submodule, or is it in a separate
".../lang/reader.rkt" module file?

At Fri, 13 Sep 2013 17:01:36 +0400, Dmitry Pavlov wrote:
> Matthew,
> 
> Many thanks! Your code works perfectly for my program.
> 
> Now I would like to describe my tries to make a standalone
> executable out of my program.
> 
> Most obvious way fails:
> 
> $ raco exe slon-main.rkt
> $ ./slon-main
> standard-module-name-resolver: collection not found
>    collection: "slon"
>    in collection directories:
>    context...:
>     standard-module-name-resolver
> 
> Not surprising, as the "slon" collection is not mentioned
> in the slon-main.rkt itself, but is required dynamically.
> 
> So my second thought was that maybe ++lib would help.
> It did not. (Did I use the flag incorrectly?)
> 
> $ raco exe ++lib slon/slon-language slon-main.rkt
> $ ./slon-main
> <same error message>
> 
> 
> Seeking for the solution, I upgraded from raco to the Racket level:
> 
> #lang racket
> (require compiler/embed)
> 
> (create-embedding-executable "slon-main"
>       #:modules '((#f slon/slon-main) (#f slon/slon-language))
>       #:literal-expression
>         (parameterize ([current-namespace (make-base-namespace)])
>                 (compile `(namespace-require 'slon/slon-main)))
>       #:configure-via-first-module? #t
>       #:verbose? #t)
> 
> That worked great. "slon-main" was able to run independently
> of its own location in the file system, and indenendeltly
> of the presence of the original files in the slon/ collection
> on disk.
> 
> But there was another thing. In my implementation, I have
> some binary data files that are loaded via
> 
> (define-runtime-path eop.era "../eop/eop.era")
> 
> The resulting program depended on those files. When I renamed
> one of them, I got an error message:
> 
> $ ./slon-main
> with-input-from-file: cannot open input file
>    path: /home/dpavlov/era/slon/../eop/eop.era
> 
> 
> "OK", I thought, "raco distribute is supposed to fix that".
> 
> 
> $ raco distribute slon-distr slon-main
> $ ls ./slon-distr/lib/plt/slon-main/exts/ert/home/dpavlov/era/eop/
> eop.era
> $ ./slon-distr/bin/slon-main
> standard-module-name-resolver: collection not found
>    collection: "slon"
>    in collection directories:
>     /home/dpavlov/.racket/5.3.4/collects
>     /home/dpavlov/era/slon/slon-distr/lib/plt/slon-main/collects
> 
> The executable "slon-main" produced by raco distrubute is
> different from the original one (although the size is the same).
> I guess, raco distribute have eliminated the dependency of the
> binary files with absolute paths, but it reintroduced the
> dependency on the "slon" collection, which I previously got
> rid of with the help of (create-embedding-executable)!
> 
> And here I am stuck, asking for help.
> 
> 
> Regards,
> 
> Dmitry
> 
> 
> 
> 
> On 09/13/2013 05:33 AM, Matthew Flatt wrote:
> > I'm not sure I understand what you want, but here are some ideas about
> > evaluating a text that would a module if only a "#lang" line were
> > added.
> >
> > To start, here's a function to `require` an input port that contains a
> > module's source. It uses the current namespace, and it gensyms a name
> > for the module if you don't provide one. The part that I think is least
> > obvious is using `current-module-declare-name` to set the name of the
> > module to that it can be found by `dynamic-require`:
> >
> >   (require syntax/modread)
> >
> >   (define (require-input-port p [name (gensym)])
> >     (define module-name (make-resolved-module-path name))
> >     (parameterize ([current-module-declare-name module-name])
> >       (eval-syntax (check-module-form ; ensures that `module` is bound
> >                     (with-module-reading-parameterization
> >                      (lambda ()
> >                        (read-syntax (object-name p) p)))
> >                     'ignored
> >                     #f)))
> >     (dynamic-require module-name #f))
> >
> > The `require-input-port` function assumes that the source starts with
> > "#lang". You could use `input-port-append`, as others have suggested,
> > to add a "#lang" line:
> >
> >   (define p (input-port-append #t
> >                                (open-input-string "#lang racket/base\n")
> >                                (open-input-file "body.rktd")))
> >   (port-count-lines! p)
> >   (require-input-port p)
> >
> > A problem with `input-port-append` is that line numbers are off by one
> > for error reporting, and positions are off by the length of the first
> > line. That's an annoyingly difficult problem to fix, but
> > `prefix-input-port` below is my attempt (and maybe `input-port-append`
> > should just work better along similar lines).
> >
> >   (define p (prefix-input-port #"#lang racket/base\n"
> >                                (open-input-file "body.rktd")))
> >   (port-count-lines! p)
> >   (require-input-port p)
> >
> > ----------------------------------------
> >
> > ;; prefix-input-port : bytes input-port -> input-port
> > ;;  Directs position requests to the given port after the
> > ;;  prefix is read.
> > ;;  Closes the given input port when the result port is closed.
> > (define (prefix-input-port prefix base-p)
> >    (define-values (prefix-i prefix-o) (make-pipe))
> >    (write-bytes prefix prefix-o)
> >    (close-output-port prefix-o)
> >    (define (prefix-done?)
> >      (zero? (pipe-content-length prefix-i)))
> >
> >    (make-input-port
> >     (object-name base-p)
> >     ;; read
> >     (lambda (bstr)
> >       (define n (read-bytes-avail!* bstr
> >                                     (if (prefix-done?)
> >                                         base-p
> >                                         prefix-i)))
> >       (if (equal? n 0)
> >           (wrap-evt base-p (lambda (v) 0))
> >           n))
> >     ;; peek
> >     (lambda (bstr offset evt)
> >       (define pre-n (pipe-content-length prefix-i))
> >       (define n (if (offset . >= . pre-n)
> >                     (peek-bytes-avail!* bstr
> >                                         (- offset pre-n)
> >                                         #f
> >                                         base-p)
> >                     (peek-bytes-avail!* bstr
> >                                         offset
> >                                         #f
> >                                         prefix-i)))
> >       (if (equal? n 0)
> >           (wrap-evt base-p (lambda (v) 0))
> >           n))
> >     ;; close
> >     (lambda ()
> >       (close-input-port base-p))
> >     ;; get-progress-evt
> >     ;;  Difficult (impossible?) to support at the
> >     ;;  prefix--base boundary.
> >     #f
> >     ;; commit
> >     #f
> >     ;; get-location
> >     (lambda ()
> >       (if (prefix-done?)
> >           (port-next-location base-p)
> >           (port-next-location prefix-i)))
> >     ;; count-lines!
> >     (lambda ()
> >       (port-count-lines! prefix-i)
> >       (port-count-lines! base-p))))
> >
> >
> >

Posted on the users mailing list.