2012-05-28 8 views
7

Supongamos que deseo desencadenar una macro de esquema en algo que no sea el primer elemento de una expresión s. Por ejemplo, supongamos que yo quería para reemplazar define con un estilo infija :=, de modo que:Macro de esquema activada por palabra clave que no es el encabezado de una lista

(a := 5) -> (define a 5) 
((square x) := (* x x)) -> (define (square x) (* x x)) 

La transformación real parece ser bastante sencillo. El truco será lograr que Scheme encuentre las expresiones := y expandirlas macro. He pensado en rodear grandes secciones de código que usan la sintaxis de infijo con una macro estándar, tal vez: (with-infix-define expr1 expr2 ...), y hacer que la macro estándar recorra las expresiones en su cuerpo y realice las transformaciones necesarias. Sé que si tomo este enfoque, tendré que tener cuidado de evitar la transformación de listas que, en realidad, se supone que son datos, como las listas citadas y ciertas secciones de listas cuasicadas. Un ejemplo de lo que preveo:

(with-infix-define 
    ((make-adder n) := (lambda (m) (+ n m))) 

    ((foo) := 
     (add-3 := (make-adder 3)) 
     (add-6 := (make-adder 6)) 
     (let ((a 5) (b 6)) 
      (+ (add-3 a) (add-6 b)))) 

    (display (foo)) 
    (display '(This := should not be transformed)) 

Por lo tanto, mi pregunta es doble:

  1. Si tomo la ruta with-infix-define, tengo que mirar hacia fuera para cualquier escollos distintos de cotización y cuasiquote?
  2. Me siento como si estuviera reinventando la rueda. Este tipo de acceso de código parece exactamente qué tendrían que hacer los sistemas de macro expansión estándar, la única diferencia es que solo miran el primer elemento de una lista cuando deciden si hacer o no una transformación de código. ¿Hay alguna manera de que pueda aprovechar los sistemas existentes?
+0

¿Hay un paquete de código de walker para Scheme? Parece que todo lo que necesita hacer es ajustar el código en un macro, recorrer ese código y cambiar: = con el s-exp anterior, luego definir una macro de símbolo que haga: = equivalente para definir. No estoy seguro en las s-exps citadas sin embargo. –

Respuesta

12
  1. Antes de continuar con esto, lo mejor es pensar en cosas más a fondo - IME a menudo se iba a encontrar que lo que realmente quiere un manejo de nivel lector de := como una sintaxis infija. Eso, por supuesto, significa que también es infijo en las citas, etc., por lo que parece malo por ahora, pero una vez más, mi experiencia es que terminas dándote cuenta de que es mejor hacer las cosas de manera constante.

  2. Para completar, voy a mencionar que, en la raqueta hay una lectura de sintaxis para hackear expresiones infijas como: (x . define . 1)es leído como(define x 1). (Y como arriba, funciona en todas partes)

  3. De lo contrario, su idea de una macro de ajuste es prácticamente lo único que puede hacer. Sin embargo, esto no lo hace completamente imposible, es posible que tenga un gancho en el expansor de su implementación que le permita hacer tales cosas; por ejemplo, Racket tiene una macro especial llamada #%module-begin que envuelve un cuerpo de módulo completo y #%top-interaction que se envuelve a nivel expresiones en el REPL. (Ambos se añaden de manera implícita en los dos contextos.) He aquí un ejemplo (estoy usando de define-syntax-rule por simplicidad de la raqueta):

    #lang racket/base 
    
    (provide (except-out (all-from-out racket/base) 
            #%module-begin #%top-interaction) 
         (rename-out [my-module-begin #%module-begin] 
            [my-top-interaction #%top-interaction])) 
    
    (define-syntax infix-def 
        (syntax-rules (:= begin) 
        [(_ (begin E ...)) (begin (infix-def E) ...)] 
        [(_ (x := E ...)) (define x (infix-def E) ...)] 
        [(_ E)    E])) 
    
    (define-syntax-rule (my-module-begin E ...) 
        (#%module-begin (infix-def E) ...)) 
    (define-syntax-rule (my-top-interaction . E) 
        (#%top-interaction . (infix-def E))) 
    

    Si pongo esto en un archivo llamado my-lang.rkt, que ahora puede utilizarlo como sigue:

    #lang s-exp "my-lang.rkt" 
    (x := 10) 
    ((fib n) := 
    (done? := (<= n 1)) 
    (if done? n (+ (fib (- n 1)) (fib (- n 2))))) 
    (fib x) 
    
  4. Sí, tiene que lidiar con un montón de cosas. Dos ejemplos en lo anterior son el manejo de expresiones begin y manejo de cuerpos de funciones. Obviamente, esta es una lista muy parcial: también querrás cuerpos de lambda, let, etc.Pero esto es aún mejor que un masaje a ciegas, ya que no es práctico, ya que no se puede decir de antemano cómo terminará alguna pieza de código al azar. Como un ejemplo sencillo, considere este sencillo macro:

    (define-syntax-rule (track E) 
        (begin (eprintf "Evaluating: ~s\n" 'E) 
         E)) 
    (x := 1) 
    
  5. El resultado de esto es que una solución adecuada, necesita alguna manera de pre-expandir el código, por lo que a continuación, puede escanearlo y tratar con las pocas formas centrales conocidas en su implementación.

  6. Sí, todo esto es un trabajo repetitivo que hacen los macroexpansores, pero como está cambiando cómo funciona la expansión, no hay forma de evitar esto. (Para ver por qué es un cambio fundamental, considere algo como (if := 1) - ¿Es esta una expresión condicional o una definición? ¿Cómo decide cuál tiene prioridad?) Por esta razón, para los idiomas con dicha "sintaxis linda", una más popular El enfoque es leer y analizar el código en simples expresiones S, y luego dejar que la implementación real del lenguaje use funciones simples y macros.

+0

Hmmm ... Puedo ver cómo mi solución propuesta podría ponerse muy fea muy rápidamente ... Si decido ir por la ruta del lector-macro, ¿conoce algún buen recurso donde pueda aprender sobre Scheme (particularmente Racket) leer -macras? He tenido muy poca experiencia con ellos, pero me parece recordar que Scheme reader-macros me pareció mucho menos poderosa que, por ejemplo, Common Lisp reader-macros. En particular: ¿puedo escribir una macro normal que se expanda a una definición de lector-macro? ¿O será demasiado tarde en ese momento para modificar la lectura? – Ord

+0

Racket tiene la capacidad de realizar un análisis completo (que es demasiado) y macros de lector basadas en lecturas CL (que tampoco se ajustan bien aquí). En su lugar, es mejor simplemente leer el archivo como de costumbre, luego encontrar S-exprs que se parecen a lo que desea, y convertirlos moviendo el ': =' a la izquierda. Pero es posible hacer eso con el elemento '#% module-begin' simplemente haciendo un bucle simple que hace lo mismo y luego envía el resultado al habitual, pero este ciclo debe hacerse con código real, no con un 'sintaxis-reglas'. –

+0

Está bien, entonces, si entiendo tu solución correctamente, utilizo una macro '' define-sintaxis '' muy parecida a tu ejemplo 'my-module-begin', y luego simplemente paso por los s-exprs que pasan a mi macro y transformarlos según sea necesario en expresiones de prefijo? – Ord

3

Redefiniendo define es un poco complicado. Vea la excelente explicación de @Eli.

Si, por otro lado, está satisfecho con := para usar set! las cosas son un poco más simples.

Aquí hay un pequeño ejemplo:

#lang racket 

(module assignment racket 
    (provide (rename-out [app #%app])) 

    (define-syntax (app stx) 
    (syntax-case stx (:=) 
     [(_ id := expr) 
     (identifier? #'id) 
     (syntax/loc stx (set! id expr))]  
     [(_ . more) 
     (syntax/loc stx (#%app . more))]))) 

(require 'assignment) 

(define x 41) 
(x := (+ x 1)) 
(displayln x) 

Para que el ejemplo sea en un solo archivo, solía submódulos (disponibles en la versión preliminar de la raqueta).

+0

Gracias soegaard; pero ¿por qué no podría simplemente aplicar tu solución a 'define' también? Si cada expresión está envuelta implícitamente en la aplicación #%, ¿no funcionaría eso? – Ord

+0

En resumen: las definiciones no son expresiones. Las definiciones y expresiones pueden ocurrir en diferentes lugares. Considere el lado derecho de un enlace de let, por ejemplo. – soegaard

+0

Entonces, ¿no todos los formularios de "definir" están necesariamente envueltos con #% de la aplicación? ¿O es que algunas formas en las que una definición no sería apropiada se envuelven con la aplicación #% (como en, supongo, su ejemplo de let)? – Ord

Cuestiones relacionadas