【问题标题】:Racket Macro Ellipsis Syntax球拍宏省略号语法
【发布时间】:2013-04-10 18:28:41
【问题描述】:

我有一个宏在传递一个参数时工作,我想扩展它以使用 ... 接受 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 'props ...),否则,它只是调用它(道具...)。

现在,我希望能够通过:

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

并让它以相同的方式工作。但我目前处于省略号地狱,我的大脑不再正常工作。

感谢任何见解。

【问题讨论】:

    标签: macros racket ellipsis


    【解决方案1】:

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

    首先,定义一个语法类来识别 key:value 标识符(并使其组件字符串可用作 key 和 value 属性):

    (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 ...)]))
    

    【讨论】:

    • 那就更好了,谢谢。我的技能水平在概念上有点密集,但我想是时候深入研究语法课程了。 :)
    【解决方案2】:

    建议:使用辅助函数来帮助处理嵌套。您的 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)])
    

    【讨论】:

    • 像往常一样,丹尼立即做出了彻底的回应!非常感谢。
    猜你喜欢
    • 2012-12-29
    • 2012-10-25
    • 2013-07-12
    • 2011-08-02
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    相关资源
    最近更新 更多