Trouble using `make-at-reader`-generated function with `include/reader`

59 views Asked by At

Edit: I've identified the problem, and I've found a workaround (see Update 2 at the end), but it seems like there should be a more elecant way to do this — I hope someone can come up with one.

I'm running into trouble when trying to use a function I've generated using Racket/Scribble's make-at-reader as the reader-expr for include/reader or include-at/relative-to/reader (from racket/include), and I'm hoping someone can explain what I'm doing wrong.

I have used some of the arguments to make-at-reader to define a substantially different use for @-forms (basically for a template system), basically like this:

(define cmd-readtable
  (make-readtable #f #f 'non-terminating-macro <<myExtensionProcHere>>))
(define at-template-reader
  (make-at-reader #:syntax? #t               
                  #:inside? #t
                  #:command-char #\ƒ
                  #:command-readtable cmd-readtable
                  #:syntax-post-processor <<myFunctionHere>>))

I can get this to work ok, if not super elegantly, at run-time with call-with-input-file. For example, if I do,

((lambda (stx)
   (syntax-case stx ()
     [(a ...)
      #'(string-append a ...)]))
 (syntax->list
  (call-with-input-file
      "/path/to/my/file"
   (lambda (in)
     (at-template-reader 'test in)))))

I get something like,

'(string-append "string foo"
                "\n"
                (expression "bar")
                "string baz")

which, using eval and namespace-anchor->namespace, evaluates just as it should. To be explicit, all of the syntax objects correspond either to literal strings or expressions that evaluate to strings; the expressions from the external file do, however, need to have access to a parameter and some other definitions from the module's context.

However, what I would prefer to do is use something like include/reader or include-at/relative-to/reader (maybe even more similar to include/template from web-server/templates) so that the work of interpreting the external file can be done at compile-time. My attempts to use my at-template-reader in one of those forms, though, have been totally unsuccessful: worse, they simply run with no results until they run out of memory, so I'm not even sure how to debug them. I think I'm in a bit over my head here, and I'd appreciate anyone's ideas on how to get this working.


Update 1: Asumu Takikawa asked in a comment if it works with #:inside? #f. I have now tried that, and it does — or at lease a much closer approximation of working that using #:inside? #t, which just goes off into never-never land until I kill it or it runs out of memory. The #:inside? #f version succeeds and returns values. (It does behave oddly with some of the logic I've defined, sometimes only returning only the last value, but I think that may be because I made some assumption that only holds for #:inside? #t-mode; I'm looking into that now.)

Does this give anyone an idea of what might be going wrong with the #:inside? #t version? I'm wondering if it might have something to do with the fact that, with #:inside? #t, it returns only one syntax object representing the whole external file …


Update 2:

I've identified the problem, and I've now got a very hack-ish workaround, but I hope someone can explain to me what the right way to do this is.

First, let me give a name to the #:inside? #f function I tried at Asumu Takikawa's suggestion so I can refer to it concisely:

(define at-template-reader-OUTSIDE
  (make-at-reader #:syntax? #t               
                  #:inside? #f
                  #:command-char #\ƒ
                  #:command-readtable cmd-readtable
                  #:syntax-post-processor <<myFunctionHere>>))

There is a crucial difference between the read-syntax-like functions I'm calling at-template-reader and at-template-reader-OUTSIDE, which I discovered by playing with (port->list [r in])docs (which "returns a list whose elements are produced by calling r on in until it produces eof"). Each call to at-template-reader-OUTSIDE produces a single syntax object corresponding to the next chunk of the source file, as expected, until finally one produces eof. However, each call to at-template-reader produces a single syntax object corresponding to the whole source file, and never produces eof — calls after the first just return the empty list (as a syntax object). This means calls to port->list — and, it seems, include/reader and friends — never return.

I've gotten this working by using a wrapper function, like this: (in a module where everything is at the right phase level)

(include/reader (file "/path/to/source-file")
                (lambda (stx in)
                  (cond [(eof-object? (peek-char in))
                         eof]
                        [else
                         ((lambda (stx)
                            (syntax-case stx ()
                              [(a ...)
                               #'(string-append a ...)]))
                          (syntax->list
                           (at-template-reader stx in)))])))

However, this feels clumsy — I hope someone can tell me the "right" way to do this.

0

There are 0 answers