RackUnit source location inside of macros - macros

I am building a set of rackunit tests, where the actual test-case and check-equal? function is defined in a macro. The code looks something like this:
#lang racket
(require rackunit
rackunit/text-ui)
(define-syntax (my-test=? stx)
(syntax-case stx ()
[(_ case1 case2)
(syntax/loc stx
(test-case "tests"
(check-equal? case1 case2)))]))
(define tests
(test-suite "tests"
(my-test=? 'a 'b)))
(run-tests tests)
However, when I run this code I get the following output:
--------------------
tests > tests
tests
FAILURE
name: check-equal?
location: unsaved-editor:11:9
actual: 'a
expected: 'b
. Check failure
--------------------
0 success(es) 1 failure(s) 0 error(s) 1 test(s) run
Where line 11 is the line of the check-equal? function inside of the macro: (check-equal? case1 case2)))]))
Is there any way I can rackunit to show the error on the line where my-test=? is used: (my-test=? 'a 'b)))?

You can put the syntax location directly on the check-equal? expression to get the behavior that you want. Here's an example:
(define-syntax (my-test=? stx)
(syntax-case stx ()
[(_ case1 case2)
(quasisyntax
(test-case "tests"
#,(syntax/loc stx (check-equal? case1 case2))))]))
Putting the syntax location on the outer expression doesn't automatically propagate it in general.
With this change, the location is reported as "15:4" for me (as opposed to "11:9") which is where the (my-test=? 'a 'b) expression occurs.

Related

unbound variable in racket macro

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)

Macro syntax-case match list

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.

Transforming a list of symbols to a list of identifiers to be used in a macro

Consider the scenario where I would like to specify a very simplistic actor language using Racket macros. An actor is defined by a behaviour that defines some local state and message handlers that implement some logic. The body of a message handler can use both the formal parameters of the message, as well as the state variables. An example is implemented in the code below.
There is quite a lot of context in the code which is probably not even necessary. However, I have included it regardless in order to provide a running example, and the fact that I need to use syntax-parametrize may complicate the solution. The special point of interest is the with-syntax clause in the MESSAGE macro, where I require the (local-state-variable ...) pattern to match a list of identifiers, currently #'local-state-variables which is a list of symbols (bound by syntax-parameterize in the ACTOR macro), and thus does not match. So far I have not been able to find the solution, although it does not seem like it should be shockingly difficult. Am I missing something obvious?
#lang racket
(require (for-syntax syntax/parse))
(require racket/stxparam)
(define LOCAL_STATE
(lambda (stx)
(raise-syntax-error 'LOCAL_STATE "should only be used inside an actor" stx)))
; Define some syntax classes because abstractions are nice
(begin-for-syntax
(define-syntax-class actor-local-state
#:description "actor local state"
#:literals (LOCAL_STATE)
(pattern (LOCAL_STATE state-variable:id ...)))
(define-syntax-class message-pattern
#:description "actor message pattern"
(pattern (identifier:id argument:id ...))))
(define-syntax-parameter local-state-variables
(lambda (stx)
(raise-syntax-error 'local-state-variables "reserved keyword for actors" stx)))
(define-syntax (MESSAGE stx)
(syntax-parse stx
[(_ pattern:message-pattern body:expr ...+)
; Currently there is a "binding match failed" error on the following line, but replacing #'local-state-variables with #'(a b) (a list of identifiers) needless to say works.
(with-syntax ([(local-state-variable ...) #'local-state-variables])
; For simplicity just display the state variables - this is normally where some magic happens
#'(display '(local-state-variable ...)))]))
(define-syntax (ACTOR stx)
(syntax-parse stx
[(_ state:actor-local-state handler:expr ...+)
#'(syntax-parameterize
([local-state-variables '(state.state-variable ...)])
; For the sake of simplicity, an actor is currently a list of message handlers
(list handler ...))]))
; in this proof-of-concept code this should print (a b)
(define behaviour
(ACTOR (LOCAL_STATE a b)
(MESSAGE (add x y) (+ a b x y))))
Use syntax-parameter-value. Here's an example of using syntax parameters to manage lists of variables:
;; vars : syntax parameter of (Listof Identifier)
(define-syntax-parameter vars null)
;; with-vars: like let, but set vars
(define-syntax (with-vars stx)
(syntax-parse stx
[(_ ([var:id rhs:expr] ...) . body)
#'(let ([var rhs] ...)
(syntax-parameterize ([vars (list (quote-syntax var) ...)])
. body))]))
;; get-vars: get vars (symbolic name) and their values
(define-syntax (get-vars stx)
(syntax-parse stx
[(_)
(with-syntax ([(var ...) (syntax-parameter-value #'vars)])
#'(list (list (quote var) var) ...))]))
;; Examples:
(get-vars)
;; => '()
(with-vars ([x 1])
(get-vars))
;; => '((x 1))
(with-vars ([x 1])
(with-vars ([y 2] [z 3])
(set! z 17)
(get-vars)))
;; => '((y 2) (z 17))
The easiest way to turn any datum (including a list of symbol) into an identifier with datum->syntax. (You can also use format-id, but that works on only a single identifier.) With these functions, you pass in a syntax object for the scopes you want your new identifier to have, or #f if you want it to inherit the scopes that your current macro is generating.1 Getting your list of identifiers (as one single syntax object, would just be:
(syntax->datum stx '(a b c))
Where '(a b c) is your list of identifiers. Finally, you can then add this in your with-syntax:
(with-syntax ([(local-state-variables ...) (datum->syntax stx ...)])
...)
As a side note, the way to answer the title of your question, just iterate over your list with map producing a new list using format-id:
(map (curry format-id stx "~a") '(a b c)
1Unless I'm wrong, if so, please correct this.

syntax-parameter pointing to a syntax-pattern-variable?

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)>

Capturing a variable number of arguments via an ellipsis in a nested macro; Missing pattern variable error

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 ...)]))