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.