【发布时间】:2016-08-09 15:25:19
【问题描述】:
我正在使用此页面上提到的宏 (Macro for keyword and default values of function arguments in Racket),它允许我将 {arg_name arg_value} 用于默认和命名参数(不需要 #:key_name)。否则它可以正常工作,但它会干扰具有可变参数(fnname . vars)的函数声明。该错误只是“错误的语法”。如何纠正?感谢您的 cmets / 答案。
编辑:我当前的代码是:
(require syntax/parse/define ; for define-simple-macro
(only-in racket [define old-define] [#%app old-#%app])
(for-syntax syntax/stx)) ; for stx-map
(begin-for-syntax
;; identifier->keyword : Identifer -> (Syntaxof Keyword)
(define (identifier->keyword id)
(datum->syntax id (string->keyword (symbol->string (syntax-e id))) id id))
;; for use in define
(define-syntax-class arg-spec
[pattern name:id
;; a sequence of one thing
#:with (norm ...) #'(name)]
[pattern {name:id default-val:expr} ; rn: ch if {} needed here; since works well with [] here;
#:when (equal? #\{ (syntax-property this-syntax 'paren-shape))
#:with name-kw (identifier->keyword #'name)
;; a sequence of two things
#:with (norm ...) #'(name-kw {name default-val})]))
(define-syntax-parser define ; instead of define-simple-macro;
[(define x:id val:expr)
#'(old-define x val)]
[(define (fn arg:arg-spec ...) body ...+)
#'(old-define (fn arg.norm ... ...) body ...)])
(begin-for-syntax
;; for use in #%app
(define-syntax-class arg
[pattern arg:expr
#:when (not (equal? #\{ (syntax-property this-syntax 'paren-shape)))
;; a sequence of one thing
#:with (norm ...) #'(arg)]
[pattern {name:id arg:expr}
#:when (equal? #\{ (syntax-property this-syntax 'paren-shape))
#:with name-kw (identifier->keyword #'name)
;; a sequence of two things
#:with (norm ...) #'(name-kw arg)]))
(require (for-syntax (only-in racket [#%app app])))
(define-simple-macro (#%app fn arg:arg ...)
#:fail-when (app equal? #\{ (app syntax-property this-syntax 'paren-shape))
"function applications can't use `{`"
(old-#%app fn arg.norm ... ...))
我不确定要更改哪个部分。如果我删除最后一部分(定义简单宏),{} 中的命名/默认参数不起作用。
进一步编辑:我已将代码修改如下:
(define-syntax-parser define ; instead of define-simple-macro;
[(define x:id val:expr)
#'(old-define x val)]
[(define (fn arg:arg-spec ...) body ...+)
#'(old-define (fn arg.norm ... ...) body ...)]
[(define (fn . vars) body ...+)
#'(old-define (fn . vars) body ...)] )
它的工作原理:
(define (testvars . vars)
(println (list? vars))
(for ((item vars))(println item)) )
(testvars 1 2 3)
#t
1
2
3
但为什么我还需要“(define-simple-macro ..”部分?另外,为什么我需要2个“(begin-for-syntax..”定义)?
再次编辑:进一步修改:
(define-syntax-parser define
[(define x:id val:expr)
#'(old-define x val)]
[(define (fn arg:arg-spec ...) body ...+)
#'(old-define (fn arg.norm ... ...) body ...)]
[(define (fn arg:arg-spec ... . vars) body ...+)
#'(old-define (fn arg.norm ... ... . vars) body ...)]
)
Above 最终适用于命名参数和变量参数,例如(fnname {x 0} {y 1} 10 20 30),感谢下面 cmets 中 @AlexKnauth 的所有帮助。
【问题讨论】:
-
该问题/答案中的
define宏不支持声明“rest”参数。当我写它的时候,我不想用不相关的特性来混淆实现。 -
虽然实际上,尝试自己扩展它应该不是很难。而不是
define-simple-macro,您必须使用define-syntax-parser,有两种情况,一种用于没有其余参数的情况,另一种用于有剩余参数的情况。 -
define是您需要更改的那个,它已经用define-syntax-parser编写了。我忘记了,但这使这更容易。您只需在现有的define宏中添加另一个案例。 -
最后一个
define-simple-macro定义了#%app宏,它重新定义了函数应用程序的样子。如果你忽略它,你将可以拨打(greet #:hello "hi"),但不能拨打(greet [hello "hi"])。 -
另外,您可能应该添加修改后的
define宏作为答案,尽管应该通过编写vars:id来确保vars是一个标识符,并且您应该确保您可以有一些正常的参数后跟 ` 。 rst-id`也是