【问题标题】:Scheme/Racket: How to add keyword to define-syntax-parser方案/球拍:如何将关键字添加到定义语法分析器
【发布时间】:2020-02-13 01:07:24
【问题描述】:

我可以这样定义for循环的语法:

(require syntax/parse/define)
(define-syntax-parser myfor
  [(_ Binding Form1 ...)
   #'(for (Binding) Form1 ...)]
)

(myfor (I (range 0 10))
  (displayln I)
)

它可以工作,但是当我向它添加关键字时,它不遵循#:break关键字中的条件:

(require syntax/parse/define)
(define-syntax-parser myfor
  [(_ Binding #:break Break Form1 ...)
   #'(for (Binding) Form1 ...)]
)

(myfor (I (range 0 10))
  #:break (= I 5)
  (displayln I)
)

define-syntax-parser如何正确添加关键字?

【问题讨论】:

    标签: parsing syntax scheme racket keyword


    【解决方案1】:

    这有两个部分:

    1. 在输入中接受 #:break 作为可选关键字
    2. 必要时在输出中生成#:break

    共有三种方法:多分支、可选和语法类。

    多分支

    解决这个问题的最简单方法是在语法解析器中设置两个分支。一个分支有关键字,另一个分支没有。

    两个分支意味着两个模式匹配输入,两个模板产生输出。

    (define-syntax-parser myfor
      [(_ Binding #:break Break Form1 ...)
       #'(for (Binding #:break Break) Form1 ...)]
      [(_ Binding Form1 ...)
       #'(for (Binding) Form1 ...)])
    

    这相当简单明了,但缺点是您会重复自己,为每个分支指定多次 forBindingForm1 ...,如果添加更多关键字,它会变得更加冗长。

    可选和~?

    另一种方法是在模式中使用~optional~seq 来匹配输入,而在模板中使用~?~@ 来产生输出。

    (define-syntax-parser myfor
      [(_ Binding {~optional {~seq #:break Break}} Form1 ...)
       #'(for (Binding {~? {~@ #:break Break}}) Form1 ...)])
    

    注意模板中的~? 是模式中~optional 的位置,而模板中的~@ 是模式中~seq 的位置。

    当输入模式和输出模板之间有明显的对称性时,这种策略是最好的。

    语法类

    (begin-for-syntax
      (define-splicing-syntax-class maybe-break-clause
        [pattern {~seq} #:with (out ...) '()]
        [pattern {~seq #:break Break} #:with (out ...) #'(#:break Break)]))
    
    (define-syntax-parser myfor
      [(_ Binding mbc:maybe-break-clause Form1 ...)
       #'(for (Binding mbc.out ...) Form1 ...)])
    

    在这种情况下,这种策略对于 #:break 来说是多余的,但如果关键字行为比简单地传递关键字(如果存在)更复杂,则可能变得有必要。

    【讨论】:

      【解决方案2】:

      我知道了,我需要在语法中添加 ~optional 和 ~seq:

      (require syntax/parse/define)
      (define-syntax-parser myfor
        [(_ Binding {~optional {~seq #:break Break} #:defaults [(Break #'#f)] } Form1 ...)
         #'(for (Binding #:break Break) Form1 ...)]
      )
      
      (myfor (I (range 0 10))
        #:break (= I 5)
        (displayln I)
      )
      

      【讨论】:

      • 我建议不要将#:defaultsnull 一起使用,而不是使用null 作为类似布尔的条件表达式,我建议不要默认为null,而是使用~?~@ 在模板中,这样#:break Break 就不会出现在输出中,除非它也出现在输入中
      • 对不起,默认值应该是[(Break #'#f)] 而不是#'null
      猜你喜欢
      • 1970-01-01
      • 2019-10-27
      • 2021-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      相关资源
      最近更新 更多