Reputation: 12465
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.
Upvotes: 3
Views: 319
Reputation: 30227
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))))
Upvotes: 1
Reputation: 223023
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.
Upvotes: 4
Reputation: 29556
(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.
Upvotes: 5