2016-12-12 2 views
3

Рассмотрим сценарий, в котором я хотел бы указать очень упрощенный язык актеров с использованием макросов Racket. Актер определяется поведением, которое определяет некоторое локальное состояние и обработчики сообщений, которые реализуют некоторую логику. Тело обработчика сообщений может использовать как формальные параметры сообщения, так и переменные состояния. Пример приведен в приведенном ниже коде.Преобразование списка символов в список идентификаторов, которые будут использоваться в макросе

В коде есть довольно много контекста, который, вероятно, даже не нужен. Тем не менее, я включил его независимо от того, чтобы предоставить пример, и тот факт, что мне нужно использовать syntax-parametrize, может усложнить решение. Особый интерес представляет статья with-syntax в макросе MESSAGE, где мне нужен шаблон (local-state-variable ...), который соответствует списку идентификаторов, в настоящее время #'local-state-variables, который представляет собой список символов (связанный в макросе 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)))) 

ответ

2

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

Самый простой способ превратить любую точку привязки (включая список символа) в качестве идентификатора с datum->syntax. (Вы также можете использовать format-id, но это работает только с одним идентификатором.) С помощью этих функций вы передаете объект синтаксиса для областей, которые хотите получить у вашего нового идентификатора, или #f, если вы хотите, чтобы он наследовал области, которые ваш текущий макрос генерирует. Получение списка идентификаторов (в виде одного объекта синтаксиса, будет просто:.

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

Если я ошибаюсь, если это так, исправьте это.

+0

Это было бы действительно был простым решением, но '(карта (Карри формата-идентификатор STX«~ а») местной государственной-переменные)' выдает ошибку незанятого идентификатора (для 'local-state-variables') и использует' # 'local-state-variables' интерпретирует его как синтаксический объект идентификатора 'local-state-variables'. Сложность, похоже, связана с ссылкой на параметр синтаксиса. В более широком смысле, делая переменные состояния доступными для макроса 'MESSAGE', который всегда содержится в макросе' ACTOR' (где определяются переменные состояния). – Sam