[racket] racket's syntax-case and read-syntax usage

From: Jens Axel Søgaard (jensaxel at soegaard.net)
Date: Thu Aug 26 08:09:41 EDT 2010

2010/8/26 Никита Зуев <nikitazu at gmail.com>:

> ;; simplified version transform
> (define (transform stx)
>   (syntax-case stx
>     (require module lambda) ;; and more special forms
>
>     ((require id) #’(open id))
>
>     ((module id expr) #`(module id = #,(transform #’expr)))
>
>     ((lambda () expr) #`(fun () -> #,(transform #’expr)))
>
>     ;; …and so on
>     ))

>> (syntax->datum (transform #'(define a (+ 1 2 3))))
> (let a = (1 + 2 + 3))
>
>
> But when I use same procedure on a syntax-object that was read by
> `read-syntax', I'm getting exception:
> source.ss::23: require: bad syntax in: (require AModule)

The key thing to remember is that two identifiers are
compared not only according to spelling but also according
to syntactic bindings. In

   (syntax-case stx
      (require module lambda) ;; and more special forms

the require is bound to the identifier require exported
from the racket language.

Reading "require" in the REPL will return the same identifier,
since the binding from the racket language is visible in the REPL.
Reading "require" from a string will however return an identifier
with a context in which require is unbound.

Whether or not the following solution is what you need, depends
a little on your situation. It works by forcing syntax-case to
compare identifiers according to spelling.

#lang racket

(define (symbolic-identifier=? stx1 stx2)
  (and (identifier? stx1)
       (identifier? stx2)
       (symbol=? (syntax->datum stx1) (syntax->datum stx2))))

(define (transform stx)
  (syntax-case* stx (require module lambda) symbolic-identifier=?
    [(require id)          #'(open id)]
    [(module id expr)      #`(module id = #,(transform #'expr))]
    [(lambda () expr)      #`(fun () -> #,(transform #'expr))]
    [_ (raise-syntax-error 'transform "unknown syntax" stx)]))

(define (read-syntax-from-string str)
  (with-input-from-string str
    (λ ()
      (let ([stx (read-syntax 'read-from-a-string)])
        stx))))


(transform
 (read-syntax-from-string
  "(require foo)"))


--
Jens Axel Søgaard


Posted on the users mailing list.