I am trying to create macro, that from input list generates some code. But I can't match it in my syntax-case.
Example code:
#lang racket/base
(require (for-syntax racket/base))
(define test-data '(root
()
(branch ((name "left")) (leaf ((name "green0"))) (leaf ((name "yellow"))) (leaf ((name "pink"))))
(branch ((name "right")) (leaf ((name "name2"))))
(branch ((name "broken")))))
(define-syntax (parse-xml stx)
(syntax-case stx (root branch)
[(_ (root () branches ...))
#'branches...
]
[(_ rest) #'rest]))
(parse-xml (datum->syntax #'() test-data))
I feel that I am missing something very basic, but I can't find out how to "expand" input parameter into stx to be matchable. Could you please help me with it?
Thank you in advance.
If you want to parse it at run time
You don't need parse-xml to be a macro, it can just be a normal function. This means two changes.
Change the define-syntax back into define
Change the function to account for it receiving just the xml stuff, not the entire "macro" expression.
Changing both of these results in
(define (parse-xml xml)
(syntax-case xml (root branch)
[(root () branches ...) ; notice how the pattern changed for (2)
#'branches...
]
[rest #'rest]))
The body is unfinished, I assume you can take it from here.
If you want to parse it at compile time
According to your comments, a user would use it like this:
(parse-xml/file "path/to/data.xml")
Then the macro shouldn't take a variable like test-data, it should take a file path.
(require (for-syntax racket/base))
(define-syntax parse-xml/file
(lambda (stx)
(syntax-case stx ()
[(_ file-path)
....
])))
The next thing to do is find the file to read. The best way I've found for resolving a path for a macro is resolve-path-spec from syntax/path-spec.
(require (for-syntax racket/base
syntax/path-spec))
(define-syntax parse-xml/file
(lambda (stx)
(syntax-case stx ()
[(_ file-path)
(.... (resolve-path-spec #'file-path #'file-path stx) ....)])))
The next step is reading from it, probably using open-input-file and syntax:read-xml from xml.
(require (for-syntax racket/base
syntax/path-spec
xml))
(define-syntax parse-xml/file
(lambda (stx)
(syntax-case stx ()
[(_ file-path)
(....
(syntax:read-xml
(open-input-file (resolve-path-spec #'file-path #'file-path stx)))
....)])))
At this point, you have a syntax object, produced by syntax:read-xml, containing the data.
Now we can use the function back from the If you want to parse it at run time section. To use it at compile time, just put the definition in a begin-for-syntax.
(require (for-syntax racket/base
syntax/path-spec
xml))
(begin-for-syntax
(define (parse-xml xml)
(syntax-case (xml->xexpr
((eliminate-whitespace '(root branch name leaf))
(document-element xml))) (root branch)
[(root () branches ...)
#''(branches ...)
]
[rest #'rest])))
(define-syntax parse-xml/file
(lambda (stx)
(syntax-case stx ()
[(_ file-path)
(parse-xml
(read-xml
(open-input-file (resolve-path-spec #'file-path #'file-path stx))))
])))
All that's left is finishing the function version of parse-xml to parse it and generate the code.
Related
In racket, we have defsubform for subform, but defsubform does not accept a form like bar but only (bar ...)
Here's a (cleaned-up) implementation of defsubform
#lang racket
(require scribble/core
scribble/decode)
(define (into-blockquote s)
(make-nested-flow (make-style "leftindent" null)
(if (splice? s)
(decode-flow (splice-run s))
(list s))))
(define-syntax (defsubform stx)
(syntax-case stx ()
[(_ . rest) #'(into-blockquote (defform . rest))]))
This suggests that you can implement defsubidform as follows:
(define-syntax (defsubidform stx)
(syntax-case stx ()
[(_ . rest) #'(into-blockquote (defidform . rest))]))
The syntax-parameterize is commonly used in conjunction with make-rename-transformer so that the syntax-parameter p behaves as an alias for another identifier:
#lang racket
(require racket/stxparam)
(define-syntax-parameter p #f)
(define-syntax (test-1-p stx)
(syntax-case stx ()
[(_ body)
#'(let ([tmp 123])
(syntax-parameterize ([p (make-rename-transformer #'tmp)])
body))]))
(test-1-p p) ;; prints 123
The code above works well, as tmp is an identifier bound by let. If however I try to make p an alias for the pattern variable tmp bound by with-syntax, then it does not work as expected:
#lang racket
(require racket/stxparam)
(define-syntax-parameter p #f)
(define-syntax (test-2-p stx)
(syntax-case stx ()
[(_ body)
#'(with-syntax ([tmp #'(foo 123)])
(syntax-parameterize ([p (make-rename-transformer #'tmp)])
body))]))
(test-2-p #'p) ;; gives #'p, instead of #'(foo 123)
If instead I declare a p-unhygienic syntax, and bind it to (make-rename-transformer #'tmp), then it works fine:
#lang racket
(define-syntax (test-3-p stx)
(syntax-case stx ()
[(_ body)
#`(with-syntax ([tmp #'(foo 123)])
(define-syntax #,(syntax-local-introduce #'p-unhygienic)
(make-rename-transformer #'tmp))
body)]))
(test-3-p #'p-unhygienic) ;; gives #'(foo 123), as expected
How can I create a hygienic alias for a pattern variable using syntax-parameterize ?
This is an interesting case, I expected the same thing you did. This might be either a bug somewhere or a limitation; I'm not sure. Either way, the define-rename-transformer-parameter form is provided as a workaround.
The initial value of a rename-transformer-parameter must be a rename-transformer, so you can use a p-init identifier with #f as the transformer binding.
(define-syntax p-init #f)
(define-rename-transformer-parameter p (make-rename-transformer #'p-init))
In context:
#lang racket
(require racket/stxparam)
(define-syntax p-init #f)
(define-rename-transformer-parameter p (make-rename-transformer #'p-init))
(define-syntax (test-2-p stx)
(syntax-case stx ()
[(_ body)
#'(with-syntax ([tmp #'(foo 123)])
(syntax-parameterize ([p (make-rename-transformer #'tmp)])
body))]))
(test-2-p #'p) ; #<syntax:10:28 (foo 123)>
Consider a scenario of two macros: the outer-macro defines a general structure of some entity, and the inner-macro expands in the scope of the outer macro. My intent is captured in the following code, where the expected output is a print statement. This example throws the following error for the pattern of the inner macro: (_ value ...).
syntax: no pattern variables before ellipsis in template in: ...
I intend to use value ... in the same way as the body ... pattern of the outer macro. In fact, a list of the 'values' is exactly what I need (not necessarily a very flexible 'ellipsis pattern'). Sadly it does not work this way. How can I capture a variable amount of arguments in the inner macro?
#lang racket
(require
racket/stxparam
(for-syntax syntax/parse))
(define-syntax-parameter inner-macro
(lambda (stx)
(raise-syntax-error 'inner-macro "generic error message" stx)))
(define-syntax (outter-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(syntax-case stx ()
[(_ value ...)
(printf "values are: ~a~n" (list value ...))]))])
body ...)]))
(outter-macro
(inner-macro 'a 'b 'c))
; expected result
; > "values are: (a b c)"
To “escape” ellipses in syntax templates, you can use the syntax (... <form>), where <form> is a syntax template where ... sequences are treated literally. Therefore, you can wrap a piece of syntax to include literal ellipses:
> #'(... (syntax-rules ()
[(x ...) (list x ...)]))
#<syntax:4:9 (syntax-rules () ((x ...) (li...>
You can use this to surround your inner macro definition to escape the inner ellipses:
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(... (syntax-case stx ()
[(_ value ...)
(printf "values are: ~a~n" (list value ...))])))])
body ...)]))
However, this is actually not quite right, because your syntax-case body is wrong—it does not return a syntax object. You are just missing a #' before the (printf ...) (or you could use syntax-rules), so the correct implementation should be the following:
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(... (syntax-case stx ()
[(_ value ...)
#'(printf "values are: ~a~n" (list value ...))])))])
body ...)]))
This should work as intended.
Alexis King's answer is good. However another way to do it, which I find simpler to think about, is to use a #:with pattern (or a with-syntax), to define something like ooo as a literal ellipsis.
You can create a literal ellipsis with quote-syntax, so the #:with clause looks like #:with ooo (quote-syntax ...). Then you use ooo whenever you want to generate an ellipsis in the output of the macro.
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#:with ooo (quote-syntax ...)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(syntax-case stx ()
[(_ value ooo)
#'(printf "values are: ~a~n" (list value ooo))]))])
body ...)]))
Given:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([body0 (process-body #'(body ...))])
#'body0)]))
How should I receive the pattern and the ellipses in the helper? I'm not even sure if wrapping the body ... inside () is correct, but I've seen it around and it's the only thing that doesn't crash.
The process-body procedure ends up with syntax that has extra () wrapping it. I can try and break this apart, but I'm just wondering what the correct way to do this is.
process-body wraps the body pattern with some code before AND after. And, similar to define, I want to be able to provide the macro with multiple forms rather than all forms in one list. So, if given (form1) (form2), where form2 is the ellipses, process-body should (do-something) (form1) (form2) (do-something-else).
ie,
(define-for-syntax (process-body body-syntax)
(with-syntax ([stx body-syntax])
(syntax/loc body-syntax
(λ (request)
stx))))
Of course I have this working when I define the template in-line, and I suppose I could do that here, but sometimes the template becomes unwieldy and it's nice to call a helper.
Thanks a lot.
As an edit to try dyoo's first example, I'm providing the following:
#lang racket
(define-syntax (test2 stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([(body0 ...) (process-body2 #'(body ...))])
#'(begin body0 ...))]))
(define-for-syntax (process-body2 bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
body ...))))
(test2 (print "hi"))
λ: bad syntax
The left hand side of a with-syntax pattern can also have ellipses, so that the following is possible:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([(body0 ...) (process-body #'(body ...))])
#'(begin body0 ...))]))
The basic idea is that if process-body returns the transformed body elements, we can then introduce them all together with a begin.
Your process-body definition can also use with-syntax with ellipses too. So you can do something like this:
(define-for-syntax (process-body bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
body ...))))
If that's the definition of process-body, we should amend test since the shape of the result from process-body is now a complete lambda expression, so we can just return its result directly:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(process-body (syntax/loc stx (body ...)))]))
As a self-contained example:
#lang racket
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(process-body
(syntax/loc stx (body ...)))]))
(define-for-syntax (process-body bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
(printf "before the body\n")
body ...
(printf "after the body\n")))))
;; Let's try it:
(define p
(test (displayln "hello") (displayln "world")))
(p 'should-be-a-request)
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.
(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.
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))))