[racket] variables within macros

From: Jens Axel Søgaard (jensaxel at soegaard.net)
Date: Wed Jan 16 12:22:25 EST 2013

Hi Tim,

Danny explains the problem well.
To accumulate a result, one must stay in the same
same dynamic extent. One way to solve the problem
is to use local-expand. In some cases one can
write expanders  for the forms, that needs analysing
and call them directlye (rather than write a macro
for the subform and then let the expander
call a macro transformer).

Below is a short example, which incidently shows
an alternative solution the local-expand problem,
I had the other day
(see  http://www.mail-archive.com/users@racket-lang.org/msg15937.html ).

If you need local-expand, then see Matthews answer on how
to use definition contexts with local-expand.

/Jens Axel



#lang racket

;;; Consider the following grammar where upper case
;;; symbols stand for literal identifiers (in lowercase).

; program       ::= (PROGRAM var-decl-part body)
; var-decl-part ::= (VAR-DECL-PART var-decl ...)
; var-decl      ::= (VAR-DECL DEF id type expr)
; body          ::= (BODY expr ...)
; type          ::= identifier

;;; The program

; (program
;   (var-decl-part
;      (var-decl (def x int 42))
;      (var-decl (def y str "foo"))
;   (body (list x y)))

;;; expands to

(let ()
  (define x 42)
  (define y "foo")
  (list x y))

;;; During the expansion, type information is
;;; collected.

;;; The goal is to define one macro for each grammar rule.
;;; In the expansion of the body, type information
;;; from the expansion of the var-decl-part must be present
;;; to signal error for, say, (+ x y).

;;; The program macro thus uses local-expand to expand
;;; the var-decl-part.

(require racket/stxparam
         racket/splicing
         (for-syntax syntax/parse
                     syntax/context
                     racket/syntax))

(begin-for-syntax
  (define *types* '())
  (define (add-type id type)
    (set! *types* (cons (cons (syntax->datum id)
                              (syntax->datum type))
                        *types*))))

(define-syntax (program stx)
  ; program ::= (PROGRAM var-decl-part body)
  (syntax-parse stx
    [(_ var-decl-part body)
     (define expanded-var-decls
       (expand-var-decl-part #'var-decl-part))
     (displayln (list "Type information: " *types*))
     (with-syntax
         ([(def ...) expanded-var-decls])
       (syntax/loc stx (begin def ... body)))]))

(begin-for-syntax
  (define (expand-var-decl-part stx)
    ; var-decl-part ::= (VAR-DECL-PART var-decl ...)
    (syntax-parse stx
      [(_ var-decl ...)
       (define expanded-var-decls
         (map expand-var-decl (syntax->list #'(var-decl ...))))
       expanded-var-decls]))

  (define (expand-var-decl stx)
    ; var-decl ::= (VAR-DECL DEF id type expr)
    (displayln stx)
    (syntax-parse stx
      [(_ (def id type expr))
       (add-type #'id #'type)
       (syntax/loc stx (define id expr))])))

(define-syntax (body stx)
  ; body ::= (BODY expr ...)
  (syntax-parse stx
    [(_ expr ...)
     (syntax/loc stx (begin expr ...))]))


(program
 (var-decl-part
  (var-decl (def x int 42))
  (var-decl (def y str "foo")))
 (body (list x y)))



>
> Tim
>
> ===================================================================================
> "counted-defines.rkt" (similar to what's on the 17.1.2 Using #lang s-exp
> Guide Page)
> ===================================================================================
> #lang racket
> (provide (except-out (all-from-out racket) #%module-begin define)
>          (rename-out (module-begin #%module-begin) (my-define define)))
>
> (require (for-syntax syntax/parse))
> (define-for-syntax counter 0)
>
> (define-syntax (my-define stx)
>   (syntax-parse
>    stx
>    [(_ i v) (set! counter (add1 counter)) #`(define i v)]
>    [(_ (i args ...) v ...+) (set! counter (add1 counter))
>                             #`(define (i args ...) v ...)]))
>
> (define-syntax (module-begin stx)
>   (syntax-parse
>    stx
>    [(_ expr ...)
>     #`(#%module-begin expr ...
>        (define defines-count #,counter)
>        (provide defines-count))]))
>
>
> ==========================
> "test-counted-defines.rkt"
> ==========================
> #lang s-exp "counted-defines.rkt"
> (define woo 2) ; should have pushed counter up by one?
>
>
> ==========================
> I then run: racket -e '(require "test-counted-defines.rkt") defines-count'
> Which returns me "0"
>
>
>
> --
> Tim Brown <tim.brown at cityc.co.uk>  | City Computing Limited            |
> T: +44 20 8770 2110                | City House, Sutton Park Road      |
> F: +44 20 8770 2130                | Sutton, Surrey, SM1 2AE, GB       |
> -----------------------------------------------------------------------|
> BEAUTY:  What's in your eye when you have a bee in your hand           |
> -----------------------------------------------------------------------'
> City Computing Limited registered in London No. 1767817.
> Registered Office: City House, Sutton Park Road, Sutton, Surrey, SM1 2AE
> VAT number 372 8290 34.
> ____________________
>  Racket Users list:
>  http://lists.racket-lang.org/users



-- 
--
Jens Axel Søgaard


Posted on the users mailing list.