Question

I am working on a language translator in guile scheme, and need to handle the basic case, where you're trying to convert a single word.

(define var 5)
(translate var)

This should return the string var and not the number 5.
How do I do this using R5RS Scheme macros (the define-syntax style)?

Edit:
I'm translating from Scheme to Coffeescript.

Was it helpful?

Solution

(define-syntax translate
  (syntax-rules ()
    [(_ v) 'v]))

And if you want a string:

(define-syntax translate
  (syntax-rules ()
    [(_ v) (symbol->string 'v)]))

Hopefully Guile's compiler is smart enough to fold the resulting expression so it essentially becomes a constant string.

OTHER TIPS

With syntax-case and its guard support:

(define-syntax translate
  (lambda (stx)
    (syntax-case stx ()
      [(_ v) (identifier? #'v)
       #'(symbol->string 'v)]
      [(_ v) (number? (syntax-e #'v))
       #'(number->string v)])))

(I've used square brackets for easy comparison with Eli's answer, however, it's not my usual style. ;-))

But if you're using syntax-case, then you can just as well do the conversion at the syntax level instead of producing code that does it at runtime:

(define-syntax translate
  (lambda (stx)
    (syntax-case stx ()
      [(_ v) (identifier? #'v)
       (datum->syntax stx (symbol->string (syntax->datum #'v)))]
      [(_ v) (number? (syntax-e #'v))
       (datum->syntax stx (number->string (syntax->datum #'v)))])))

The main thing here is that the macro code is now plain scheme, for example, you could abstract the common parts into a helper:

(define-syntax translate
  (lambda (stx)
    (define (rewrap convert x)
      (datum->syntax stx (convert (syntax->datum x))))
    (syntax-case stx ()
      [(_ v) (identifier? #'v) (rewrap symbol->string #'v)]
      [(_ v) (number? (syntax-e #'v)) (rewrap number->string #'v)])))

Along the same lines, if this macro is so simple, then there's no real need for syntax-case, other than pulling out the subexpression:

(define-syntax translate
  (lambda (stx)
    (syntax-case stx ()
      [(_ v) (let ([d (syntax->datum #'v)])
               (datum->syntax
                stx
                ((cond [(number? d) number->string]
                       [(symbol? d) symbol->string])
                 d)))])))

Note, BTW, that there is no magic in syntax-case -- and in the case of this simple pattern, you could just pull out the value yourself:

(define-syntax translate
  (lambda (stx)
    (let ([d (cadr (syntax->datum #'v))])
      (datum->syntax
       stx
       ((cond [(number? d) number->string]
              [(symbol? d) symbol->string])
        d)))))

There is some boilerplate stuff that syntax-case does that this last version loses:

  • If you use the macro in an unexpected way like (translate) then this version will throw an error about cadr instead of a more comprehensible syntax error

  • Similarly, if you use (translate 1 2) then this version will just silently ignore the 2 instead of an error.

  • And if it's used with something that is neither an identifier nor a number (eg, (translate (+ 1 2))) then this will depend on the unspecified value that cond returns rather than throwing a syntax error.

The other answers are useful enough already, but I thought I'd just point out that it's possible to generalize this technique in a very useful ways: macros to print out expressions and their results for debugging:

(define-syntax log-expr
  (syntax-rules ()
    ((_ expr)
     (let ((result expr))
       (write (quote expr))
       (display " evaluates to ")
       (write result)
       (newline)
       result))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top