[racket] Evaluating code written in non-SEXP language

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

At Fri, 13 Sep 2013 17:21:09 +0400, Dmitry Pavlov wrote:
>  > 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?

Assuming that this module is `slon/lang/reader`, does it help to add

    ++lib slon/lang/reader

to the `raco exe` command, along with the `++lib` argument that you
have already?

(I think that `raco exe` should probably have a `++lang` option that can
tracks down the reader module for you.)



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