0
votes

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?

1

1 Answers

1
votes

That's not possible. The pipeline of compilation/evaluation in Racket is:

  1. Read
  2. Expand macros
  3. Evaluate expanded program

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.