3

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.

chustar
  • 12,225
  • 24
  • 81
  • 119
  • 1
    I bet if you provided more details about what kind of language translator you were working on, you'd get much more useful help. What are you translating from and into, and why? – John Clements Mar 02 '12 at 02:03

3 Answers3

5
(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.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
  • Thanks for the response. I meant that it should return an actual string (so that I can use (string-append) on it. – chustar Mar 02 '12 at 01:52
  • Would it be acceptable for you to wrap a use of a `symbol->string` around the `'v`? – dyoo Mar 02 '12 at 02:00
  • This works with the symbol->string wrapping. Now I need to find a work around for bare numbers. Thank you. – chustar Mar 02 '12 at 02:11
  • @chustar: I updated it with `symbol->string`. If you want to do something with number it would be similar in principle. – Eli Barzilay Mar 02 '12 at 02:20
  • Thanks @EliBarzilay. I added an (if (?number 'v) (number->string 'v) (symbol->string 'v))) to handle the number situation. Thanks. – chustar Mar 02 '12 at 02:26
  • @chustar In that case, you'd be better off using `syntax-case`'s guards. Lemme post an example. – C. K. Young Mar 02 '12 at 02:33
  • @chustar: But what about other kinds of syntax? Sounds like you want some kind of `format` to just turn anything into a string. – Eli Barzilay Mar 02 '12 at 02:35
  • @EliBarzilay I think I only need to worry about things that are numbers, and things that I can pretend are symbols (using `). Everything else (like lists and no one) would be parsed recursively. I'll look into format just in case though. – chustar Mar 02 '12 at 15:58
4

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.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 1
    I wanted to add: with a small modification, Chris's `syntax-case` macro can do a little more work at compile-time, so that even the `symbol->string` and `number->string` stuff doesn't occur at run-time. – dyoo Mar 02 '12 at 02:45
  • 1
    Bah. It makes little sense in doing *that*... If you're going with `syntax-case` you can just as well to the conversions there, instead of expanding to code that does them. – Eli Barzilay Mar 02 '12 at 02:49
  • 1
    @Eli: I agree. I just wanted to match your code (which I know works, because your posts are always high-quality :-)), so I don't have to bother testing mine. But I'll edit the code now. – C. K. Young Mar 02 '12 at 03:06
  • Wow, this is more info than I've seen on the topic online all day. Thanks. – chustar Mar 02 '12 at 05:46
  • @chustar: Most of that info is from Eli. Thanks to him, indeed. :-D – C. K. Young Mar 02 '12 at 06:25
1

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))))
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102