[racket] Evaluating code written in non-SEXP language
Matthew,
> 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?
In a separate file:
$ cat lang/reader.rkt
(module reader syntax/module-reader
#:language 'slon/slon-language
#:read slon-read
#:read-syntax slon-read-syntax
#:whole-body-readers? #t
#:language-info '#(slon/lang/lang-info get-info #f)
#:info (lambda (key defval default)
(case key
((color-lexer)
(dynamic-require 'slon/tool/syntax-color 'get-syntax-token))
(else (default key defval))))
(require slon/slon-parser))
I believe I used that approach before submodules appeared
in Racket... why does it matter anyway?
Regards,
Dmitry
>
> 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))))
>>>
>>>
>>>
>
>