【问题标题】:How do I use a reader macro directly in Racket?如何在 Racket 中直接使用阅读器宏?
【发布时间】:2021-02-01 14:23:56
【问题描述】:

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

  • |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 代码中使用阅读器宏,而不必将字符串输入到(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

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

【问题讨论】:

    标签: macros racket reader-macro


    【解决方案1】:

    这是不可能的。 Racket 中编译/评估的管道是:

    1. 阅读
    2. 扩展宏
    3. 评估扩展计划

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

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

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

    【讨论】:

    • 如果我想定义和使用自己的阅读器宏,这是否意味着必须创建一个新的#lang ...
    • 为什么我可以修改stackoverflow.com/a/57215040中的readtable,但是这里不行?
    • 我发现你可以更改repl中的readtable,但是对于源文件你需要使用#lang。这是因为在您更换阅读器时,源文件已经被读取。我有一个创建#lang 来修改可读表的简单示例。如果你愿意,我可以将它发布到 github。请告诉我。
    • 正如@RogerKeays 所说,您在哪里使用#u#s?在 REPL 中还是在实际代码中(这是您在这个问题中提出的问题)?
    • @RogerKeays 一个简单的例子会很有帮助。谢谢你的提议。
    猜你喜欢
    • 2012-12-10
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    相关资源
    最近更新 更多