[racket] Evaluating code written in non-SEXP language
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))))
>
>
>