I am trying to define a reader macro that reads the length of a string. The string will be enclosed in vertical bars (pipes). For example:
|yes|
->3
||
->0
|The quick brown fox|
->19
|\||
->1
— The pipe character can be escaped by preceding it with a backslash.(let ((x |world|)) (+ |hello| x))
->10
I managed to write this:
#lang racket
(define (handle-pipe in [count 0])
(define cur-char (read-char in))
(cond [(eqv? cur-char #\|)
count]
[else
(when (and (eqv? cur-char #\\) ; Handle escape ("\|").
(eqv? (peek-char in) #\|))
(read-char in)) ; Consume |.
(handle-pipe in (+ count 1))]))
(parameterize ([current-readtable
(make-readtable (current-readtable)
#\|
'terminating-macro
(lambda (char in src-name line col pos)
(handle-pipe in)))])
(eval (read (open-input-string "(let ((x |world|)) (+ |hello| x))"))
(make-base-namespace)))
This returns 10
, as expected.
The problem now is: I would like to use the reader macro directly in my Racket code instead of having to feed a string into (eval (read (open-input-string ...)))
. For example, I would like to use the reader macro like this:
#lang racket
(define (handle-pipe in [count 0])
(define cur-char (read-char in))
(cond [(eqv? cur-char #\|)
count]
[else
(when (and (eqv? cur-char #\\) ; Handle escape ("\|").
(eqv? (peek-char in) #\|))
(read-char in)) ; Consume |.
(handle-pipe in (+ count 1))]))
(current-readtable
(make-readtable (current-readtable)
#\|
'terminating-macro
(lambda (char in src-name line col pos)
(handle-pipe in))))
(let ((x |world|)) ; Using the reader macro directly in Racket.
(+ |hello| x))
However, there is an error message when I run the program above:
my-program.rkt:20:9: world: unbound identifier
in: world
location...:
my-program.rkt:20:9
context...:
do-raise-syntax-error
for-loop
[repeats 1 more time]
finish-bodys
lambda-clause-expander
for-loop
loop
[repeats 3 more times]
module-begin-k
expand-module16
expand-capturing-lifts
temp118_0
temp91_0
compile15
temp85_0
standard-module-name-resolver
What did I do wrong? How do I use the reader macro in my code?
That's not possible. The pipeline of compilation/evaluation in Racket is:
Your configuration of
current-readtable
is done in step 3, so it cannot influence things that happened already in step 1.The reason your first code works is that
eval
starts the pipeline again on the datum you provided it to.Note that the reader macro is actually intended to be used when you create a new
#lang
. But since you want it to work with#lang racket
, it's not applicable.