2

假设我有以下宏:

(define-syntax-rule (qq x) '(1 x))

我可以做一些看起来像(qq (qq 2))扩展'(1 (1 2))而不是的东西(1 (qq 2)吗?

我说“看起来像”的原因是因为到目前为止我发现的唯一提示表明由内而外的宏扩展很棘手,所以我想知道是否有更明智的方法来获得我想要的最终结果。

我最初的动机与 Racket 的解析器生成器库有关:为了创建语法,该库提供了一个parser如下所示的宏:

(define my-parser (parser
  (start start) (end EOF)
  (tokens value-tokens op-tokens)
  (error (lambda (a b c) (void)))

  (grammar
   (start [(numbers) $1])

   (numbers [(numberx) (reverse $1)])
   (numberx [() empty]
            [(numberx NUM) (cons $2 $1)])
  )
))

我的语法有很多我想抽象出来的样板。例如,我希望能够定义某种list-rules抽象,让我写出类似于

(define my-parser (parser
  (start start) (end EOF)
  (tokens value-tokens op-tokens)
  (error (lambda (a b c) (void)))

  (grammar
   (start [(numbers) $1])

   (list-rules NUM numbers numberx)
  )
))

但是,如果parserget 首先扩展,它将把list-rules自己视为非终结符,而不是将其扩展为真正的非终结符(numbersand numberx)。

4

3 回答 3

2

你可以试试local-expand,虽然我承认我没有花时间理解你问题的第二部分,不足以知道它是否适合使用。

#lang racket
(define-syntax (qq stx)
  (syntax-case stx ()
    [(_ x) 
     (with-syntax ([y (local-expand #'x 'expression '())])
       #'`(1 ,y))]))
(qq 2)
(qq (qq 2))
(qq (qq (qq 2)))

=>

'(1 2)
'(1 (1 2))
'(1 (1 (1 2)))
于 2013-10-06T04:10:41.600 回答
1

好吧,您可以对形状进行宏检查:

(define-syntax qq
  (syntax-rules ()
    [(qq (qq x)) (list 1 (qq x))]
    [(qq x)      (list 1 x)]))

好消息是它“递归”地工作:

(qq 2)           ; => '(1 2)
(qq (qq 2))      ; => '(1 (1 2))
(qq (qq (qq 2))) ; => '(1 (1 (1 2)))

但即使qq提供了其他东西,它也可以工作:

(qq (+ 1))       ; => '(1 2)

如果这对您的预期用途来说是一个问题——如果你希望将其标记为错误——你可以这样做:

(define-syntax (rr stx)
  (syntax-case stx ()
    [(_ (rr x))
     (cond [(equal? 'rr (syntax->datum #'rr))
            #'(list 1 (rr x))]
           [else (raise-syntax-error #f "Expected rr" stx #'rr)])]
    [(_ x)
     #'(list 1 x)]))

示例的结果相同,但最后一个现在给出了语法错误:

(rr 2)           ; => '(1 2)
(rr (rr 2))      ; => '(1 (1 2))
(rr (rr (rr 2))) ; => '(1 (1 (1 2)))
(rr (+ 1))       ; =>
; src/scheme/misc/so-syntax.rkt:27:5: rr: Expected rr
;  at: +
;  in: (rr (+ 1))

更新:第二个版本可以写得更干净一点syntax-parse

(require (for-syntax syntax/parse))
(define-syntax (rr stx)
  (syntax-parse stx
    [(_ ((~literal rr) x)) #'(list 1 (rr x))]
    [(_ (_ x)) (raise-syntax-error #f "Expected rr" stx #'rr)]
    [(_ x) #'(list 1 x)]))
于 2013-10-06T02:05:09.523 回答
1

您需要有 2 条语法规则,首先是更通用的规则;也不使用 quasiquote 和 unquote 运算符,以便可以评估内部形式:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))

-> (qq x)
'(1 x)
-> (qq (qq x))
'(1 (1 x))
-> (qq (qq (qq x)))
'(1 (1 (1 x)))
-> (qq (qq (qq (qq y))))
'(1 (1 (1 (1 y))))

这也是可组合的:

(define-syntax qq
   (syntax-rules ()
     ((_ (x ...) ...)  `(1 ,(x ...) ...))
     ((_ x)            `(1 x))))

(define-syntax hh
   (syntax-rules ()
     ((_ (x ...) ...)  `(2 ,(x ...) ...))
     ((_ x)            `(2 x))))

-> (qq (hh x))
'(1 (2 x))
-> (hh (qq x))
'(2 (1 x))
于 2013-10-06T01:31:28.807 回答