In Guile or using SRFI-46 it is possible like shown in Specifying a Custom Ellipsis Identifier. But is it possible in SISC or "pure scheme" R5RS?
I know it is possible without using ellipsis, but what if I need to use inner ellipsis like the example bellow?
(define-syntax define-quotation-macros
(syntax-rules ()
((_ (macro-name head-symbol) ...)
(begin (define-syntax macro-name
(syntax-rules ::: ()
((_ x :::)
(quote (head-symbol x :::)))))
...))))
(define-quotation-macros (quote-a a) (quote-b b) (quote-c c))
(quote-a 1 2 3) ⇒ (a 1 2 3)
The macro expander used in SISC, psyntax, supports a different way to do inner ellipses, by using the ... macro. You can write this by applying the ... macro to each inner ellipses you want to use:
(define-syntax define-quotation-macros
(syntax-rules ()
((_ (macro-name head-symbol) ...)
(begin (define-syntax macro-name
(syntax-rules ()
((_ x (... ...))
'(head-symbol x (... ...)))))
...))))
or you can apply it to an outer form where all the ellipses within are supposed to be inner:
(define-syntax define-quotation-macros
(syntax-rules ()
((_ (macro-name head-symbol) ...)
(begin (define-syntax macro-name
(... (syntax-rules ()
((_ x ...)
'(head-symbol x ...)))))
...))))
Related
I am testing a wrapper macro around the racket syntax-case macro. At step 1, it does nothing interesting and simply passes through all the parts to syntax-case directly as follows:
#lang racket
;; definition
(define-syntax guarded-syntax-case
(lambda (x)
(syntax-case x ()
((guarded-syntax-case y (literal ...) clause ...)
#'(syntax-case y (literal ...) clause ...)
))))
;; test case
(define-syntax (binop stx)
(guarded-syntax-case stx () ; *problem site*
[(_ op n1 n2) #'(op n1 n2)]))
But this simple case fails with the following error at the empty brackets () labeled with problem site labeled in above code:
; ....rkt:11:27: #%app: missing procedure expression;
; probably originally (), which is an illegal empty application
; after encountering unbound identifier (which is possibly the real problem):
; guarded-syntax-case
; in: (#%app)
I couldn't figure out what could be wrong in this simple pass-through macro. The error message seems to suggest that there is a unbound variable somewhere, which I couldn't identify. I would think that the literal ... should be matched to empty.
Could someone help explain what went wrong and how to fix the macro?
The problem is that guarded-syntax-case is not recognized as a macro in the correct phase. In particular, when you (define-syntax guarded-syntax-case ...) in your program, you define the macro guarded-syntax-case that is available in phase 0. But forms in (define-syntax (binop stx) ...) must be in phase 1.
There are two ways to correct the mistake.
You can wrap (define-syntax guarded-syntax-case ...) inside begin-for-syntax. However, doing so would require syntax-case and other stuff to be available in phase 2. So you need an additional (require (for-meta 2 racket/base)). Here's the full code:
#lang racket
(require (for-meta 2 racket/base))
;; definition
(begin-for-syntax
(define-syntax guarded-syntax-case
(lambda (x)
(syntax-case x ()
((guarded-syntax-case y (literal ...) clause ...)
#'(syntax-case y (literal ...) clause ...))))))
;; test case
(define-syntax (binop stx)
(guarded-syntax-case stx () ; *problem site*
[(_ op n1 n2) #'(op n1 n2)]))
(binop + 1 2) ;=> 3
Alternatively, you can define a (sub)module that provides guarded-syntax-case, and then require the (sub)module with for-syntax. Here's the full code:
#lang racket
(module lib racket
(provide guarded-syntax-case)
;; definition
(define-syntax guarded-syntax-case
(lambda (x)
(syntax-case x ()
((guarded-syntax-case y (literal ...) clause ...)
#'(syntax-case y (literal ...) clause ...))))))
(require (for-syntax 'lib))
;; test case
(define-syntax (binop stx)
(guarded-syntax-case stx () ; *problem site*
[(_ op n1 n2) #'(op n1 n2)]))
(binop + 1 2)
I'm trying to define a macro that generates an anonymous function taking one argument named it, for succinctness, so that instead of
(λ (it) body)
I can write
(λλ body)
(In other words, (λλ body) transforms to (λ (it) body))
(define-syntax-parameter it #f)
(define-syntax λλ
(syntax-rules ()
((_ body)
(λ (x) (syntax-parameterize ((it x)) body)))))
(λλ (< it 0)) ; For testing
I get operators.rkt:13:28: ?: literal data is not allowed; no #%datum syntax transformer is bound in the transformer environment in: #f at (define-syntax-parameter if #f), but as far as I can tell, this is exactly like the example given in racket's doc for how to use define-syntax-parameter. I can suppress the error by replacing #f with a function (I used member, but not for any real reason), but after doing that, I get operators.rkt:17:38: x: identifier used out of context in: x. What am I doing wrong?
You left out the syntax-id-rules part in the example. It's the part that specifies that it should expand to x. Alternatively, you can use make-rename-transformer:
#lang racket
(require racket/stxparam)
(define-syntax-parameter it #f)
(define-syntax λλ
(syntax-rules ()
((_ body)
(λ (x) (syntax-parameterize ([it (make-rename-transformer #'x)]) body)))))
((λλ (< it 0)) 5)
((λλ (< it 0)) -5)
=>
#f
#t
Syntax parameters are not the only way to implement the macro you have in mind. A simpler (IMO) way is to just use datum->syntax to inject the identifier it:
(define-syntax (λλ stx)
(syntax-case stx ()
((_ body ...)
(with-syntax ((it (datum->syntax stx 'it)))
#'(λ (it) body ...)))))
To use your example:
(define my-negative? (λλ (< it 0)))
(my-negative? -1) ;; => #t
I have a macro that's working when one argument is passed, and I'd like to expand it to accept n number of arguments using ..., but I'm having trouble figuring out the syntax.
The macro accepts either custom syntax, ie, key:val key:val, or it accepts a procedure.
For example: (3 different usages)
(schema-properties [(name:first-name type:string)])
(schema-properties [(name:age type:number required:#t)])
(schema-properties [(my-custom-fn arg1 arg2 arg3)])
Definition:
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [(prop:expr ...)])
(with-syntax ([prop0 (make-prop-hash #'(prop ...))])
#'(list prop0))]))
(define-for-syntax (make-prop-hash stx)
(with-syntax ([(props ...) stx])
(if (regexp-match #px":"
(symbol->string (car (syntax->datum #'(props ...)))))
#'(pairs->hash 'props ...)
#'(props ...))))
This works, in that it checks the prop:expr syntax for the presense of ":", and if it exists, passes it to the function (pairs->hash 'props ...), otherwise, it just invokes it (props ...).
Now, I'd like to be able to pass in:
(schema-properties [(name:first-name type:string)
(name:last-name type:string)
(my-fn arg1 arg2 arg3)])
and have it work the same way. But I'm currently in ellipsis hell and my brain is no longer working correctly.
Any insights are appreciated.
Recommendation: use helper functions to help deal with nesting. Your schema-properties macro knows how to deal with one level of nesting, and you want to apply that to multiple clauses. It's the same principle as when we deal with lists of things: have a helper to deal with the thing, and then apply that across your list. It helps cut down complexity.
For your code, we can do it like this:
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [clause ...])
(with-syntax ([(transformed-clauses ...)
(map handle-clause (syntax->list #'(clause ...)))])
#'(list transformed-clauses ...))]))
;; handle-clause: clause-stx -> stx
(define-for-syntax (handle-clause a-clause)
(syntax-parse a-clause
[(prop:expr ...)
(make-prop-hash #'(prop ...))]))
(define-for-syntax (make-prop-hash stx)
(with-syntax ([(props ...) stx])
(if (regexp-match #px":"
(symbol->string (car (syntax->datum #'(props ...)))))
#'(pairs->hash 'props ...)
#'(props ...))))
;;; Let's try it out. I don't know what your definition of pairs->hash is,
;;; but it probably looks something like this:
(define (pairs->hash . pairs)
(define ht (make-hash))
(for ([p pairs])
(match (symbol->string p)
[(regexp #px"([-\\w]+):([-\\w]+)"
(list _ key value))
(hash-set! ht key value)]))
ht)
(schema-properties [(name:first-name type:string)
(name:last-name type:string)
(list 1 2 3)])
Another recommendation: use syntax classes to help deal with nesting:
First, define a syntax class that recognizes key:value identifiers (and makes their component strings available as key and value attributes):
(begin-for-syntax
(define-syntax-class key-value-id
#:attributes (key value)
(pattern x:id
#:do [(define m (regexp-match "^([^:]*):([^:]*)$"
(symbol->string (syntax-e #'x))))]
#:fail-unless m #f
#:with (_ key value) m)))
Now define a clause as either a sequence of those (to be handled one way) or anything else (to be treated as an expression, which must produce a procedure). The code attribute contains the interpretation of each kind of clause.
(begin-for-syntax
(define-syntax-class clause
#:attributes (code)
(pattern (x:key-value-id ...)
#:with code #'(make-immutable-hash '((x.key . x.value) ...)))
(pattern proc
#:declare proc (expr/c #'(-> any))
#:with code #'(proc.c))))
Now the macro just puts the pieces together:
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [c:clause ...])
#'(list c.code ...)]))
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))))