考虑我想使用Racket宏指定一种非常简单的actor语言的场景。 actor由定义一些实现某些逻辑的本地状态和消息处理程序的行为定义。消息处理程序的主体可以使用消息的形式参数以及状态变量。下面的代码中实现了一个示例。
代码中有很多上下文,甚至可能都没有必要。但是,无论是为了提供运行示例,我都包含它,而且我需要使用syntax-parametrize
这一事实可能会使解决方案复杂化。感兴趣的特殊点是with-syntax
宏中的MESSAGE
子句,我需要(local-state-variable ...)
模式匹配标识符列表,当前#'local-state-variables
是一个列表符号(由syntax-parameterize
宏中的ACTOR
绑定),因此不匹配。到目前为止,我还没有找到解决方案,尽管看起来似乎并不令人惊讶。我错过了一些明显的东西吗
#lang racket
(require (for-syntax syntax/parse))
(require racket/stxparam)
(define LOCAL_STATE
(lambda (stx)
(raise-syntax-error 'LOCAL_STATE "should only be used inside an actor" stx)))
; Define some syntax classes because abstractions are nice
(begin-for-syntax
(define-syntax-class actor-local-state
#:description "actor local state"
#:literals (LOCAL_STATE)
(pattern (LOCAL_STATE state-variable:id ...)))
(define-syntax-class message-pattern
#:description "actor message pattern"
(pattern (identifier:id argument:id ...))))
(define-syntax-parameter local-state-variables
(lambda (stx)
(raise-syntax-error 'local-state-variables "reserved keyword for actors" stx)))
(define-syntax (MESSAGE stx)
(syntax-parse stx
[(_ pattern:message-pattern body:expr ...+)
; Currently there is a "binding match failed" error on the following line, but replacing #'local-state-variables with #'(a b) (a list of identifiers) needless to say works.
(with-syntax ([(local-state-variable ...) #'local-state-variables])
; For simplicity just display the state variables - this is normally where some magic happens
#'(display '(local-state-variable ...)))]))
(define-syntax (ACTOR stx)
(syntax-parse stx
[(_ state:actor-local-state handler:expr ...+)
#'(syntax-parameterize
([local-state-variables '(state.state-variable ...)])
; For the sake of simplicity, an actor is currently a list of message handlers
(list handler ...))]))
; in this proof-of-concept code this should print (a b)
(define behaviour
(ACTOR (LOCAL_STATE a b)
(MESSAGE (add x y) (+ a b x y))))
答案 0 :(得分:3)
使用syntax-parameter-value
。以下是使用语法参数管理变量列表的示例:
;; vars : syntax parameter of (Listof Identifier)
(define-syntax-parameter vars null)
;; with-vars: like let, but set vars
(define-syntax (with-vars stx)
(syntax-parse stx
[(_ ([var:id rhs:expr] ...) . body)
#'(let ([var rhs] ...)
(syntax-parameterize ([vars (list (quote-syntax var) ...)])
. body))]))
;; get-vars: get vars (symbolic name) and their values
(define-syntax (get-vars stx)
(syntax-parse stx
[(_)
(with-syntax ([(var ...) (syntax-parameter-value #'vars)])
#'(list (list (quote var) var) ...))]))
;; Examples:
(get-vars)
;; => '()
(with-vars ([x 1])
(get-vars))
;; => '((x 1))
(with-vars ([x 1])
(with-vars ([y 2] [z 3])
(set! z 17)
(get-vars)))
;; => '((y 2) (z 17))
答案 1 :(得分:1)
将任何数据(包括符号列表)转换为datum->syntax
标识符的最简单方法。 (您也可以使用format-id
,但仅适用于单个标识符。)使用这些函数,您可以传入您希望新标识符具有的范围的语法对象,或者如果您希望它,则传递#f继承当前宏生成的范围。 1 获取标识符列表(作为单个语法对象,只是:
(syntax->datum stx '(a b c))
'(a b c)
是您的标识符列表。最后,您可以在with-syntax
:
(with-syntax ([(local-state-variables ...) (datum->syntax stx ...)])
...)
作为旁注,回答问题标题的方法,只需使用map
遍历列表,使用format-id
生成新列表:
(map (curry format-id stx "~a") '(a b c)
1 除非我错了,如果是的话,请更正。