球拍宏省略语法

时间:2013-04-17 20:16:00

标签: macros racket ellipsis

我有一个宏,当一个参数传递时,它正在工作,我想扩展它以使用...接受n个参数,但我无法弄清楚语法。

宏接受自定义语法,即key:val key:val,或者它接受一个过程。

例如:(3种不同的用法)

(schema-properties [(name:first-name type:string)])
(schema-properties [(name:age type:number required:#t)])
(schema-properties [(my-custom-fn arg1 arg2 arg3)])

定义:

(define-syntax (schema-properties stx)
  (syntax-parse stx
    [(_ [(prop:expr ...)])
     (with-syntax ([prop0 (make-prop-hash #'(prop ...))])
       #'(list prop0))]))

(define-for-syntax (make-prop-hash stx)
  (with-syntax ([(props ...) stx])
    (if (regexp-match #px":"
                      (symbol->string (car (syntax->datum #'(props ...)))))
        #'(pairs->hash 'props ...)
        #'(props ...))))

这是有效的,因为它检查prop:expr语法为“:”的存在,如果存在,则将其传递给函数(pairs-> hash'道具......),否则,它只是调用它(道具......)。

现在,我希望能够传入:

(schema-properties [(name:first-name type:string)
                    (name:last-name type:string)
                    (my-fn arg1 arg2 arg3)])

让它的工作方式相同。但我目前处于省略状态,我的大脑不再正常工作。

任何见解都表示赞赏。

2 个答案:

答案 0 :(得分:4)

另一个建议:使用语法类来帮助处理嵌套:

首先,定义一个语法类,用于识别键:值标识符(并使其组件字符串可用作keyvalue属性):

(begin-for-syntax
  (define-syntax-class key-value-id
    #:attributes (key value)
    (pattern x:id
             #:do [(define m (regexp-match "^([^:]*):([^:]*)$" 
                                           (symbol->string (syntax-e #'x))))]
             #:fail-unless m #f
             #:with (_ key value) m)))

现在将子句定义为这些子句的序列(单向处理)或其他任何内容(将被视为表达式,必须生成过程)。 code属性包含每种子句的解释

(begin-for-syntax
  (define-syntax-class clause
    #:attributes (code)
    (pattern (x:key-value-id ...)
             #:with code #'(make-immutable-hash '((x.key . x.value) ...)))
    (pattern proc
             #:declare proc (expr/c #'(-> any))
             #:with code #'(proc.c))))

现在宏只是把各个部分组合在一起:

(define-syntax (schema-properties stx)
  (syntax-parse stx
    [(_ [c:clause ...])
     #'(list c.code ...)]))

答案 1 :(得分:3)

建议:使用辅助函数来帮助处理嵌套。您的schema-properties宏知道如何处理一个级别的嵌套,并且您希望将其应用于多个子句。它与我们处理事物清单时的原则相同:有一个帮助来处理事物,然后将其应用到列表中。它有助于降低复杂性。

对于您的代码,我们可以这样做:

#lang racket
(require (for-syntax syntax/parse))

(define-syntax (schema-properties stx)
  (syntax-parse stx
    [(_ [clause ...])
     (with-syntax ([(transformed-clauses ...)
                    (map handle-clause (syntax->list #'(clause ...)))])
       #'(list transformed-clauses ...))]))


;; handle-clause: clause-stx -> stx
(define-for-syntax (handle-clause a-clause)
  (syntax-parse a-clause
    [(prop:expr ...)
     (make-prop-hash #'(prop ...))]))


(define-for-syntax (make-prop-hash stx)
  (with-syntax ([(props ...) stx])
    (if (regexp-match #px":"
                      (symbol->string (car (syntax->datum #'(props ...)))))
        #'(pairs->hash 'props ...)
        #'(props ...))))


;;; Let's try it out.  I don't know what your definition of pairs->hash is,
;;; but it probably looks something like this:
(define (pairs->hash . pairs)
  (define ht (make-hash))
  (for ([p pairs])
    (match (symbol->string p)
      [(regexp #px"([-\\w]+):([-\\w]+)" 
               (list _ key value))
       (hash-set! ht key value)]))
  ht)

(schema-properties [(name:first-name type:string)
                    (name:last-name type:string)
                    (list 1 2 3)])