0

我正在尝试定义一个读取字符串长度的读取器宏。该字符串将包含在垂直条(管道)中。例如:

  • |yes|->3
  • ||->0
  • |The quick brown fox|->19
  • |\||-> 1— 管道字符可以通过在它前面加一个反斜杠来转义。
  • (let ((x |world|)) (+ |hello| x))->10

我设法写了这个:

#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)))

10正如预期的那样,这将返回。

现在的问题是:我想直接在我的 Racket 代码中使用 reader 宏,而不必将字符串输入(eval (read (open-input-string ...))). 例如,我想像这样使用阅读器宏:

#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))

但是,当我运行上面的程序时出现错误消息:

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

我做错了什么?如何在我的代码中使用阅读器宏?

4

1 回答 1

1

那是不可能的。Racket 中的编译/评估流程是:

  1. 展开宏
  2. 评估扩展程序

您的配置current-readtable是在步骤 3 中完成的,因此它不会影响步骤 1 中已经发生的事情。

您的第一个代码起作用的原因是eval在您提供给它的数据上再次启动管道。

请注意,阅读器宏实际上是在您创建新的#lang. 但是由于您希望它与 一起使用#lang racket,因此它不适用。

于 2021-02-03T15:36:52.907 回答