Hello, all you wonderful people. I'm seeing some strange behavior from my
macros.
So I invoke my define-sequencer-buttons-info-class macro with a bunch of
identifiers.
Its job is to rename the identifiers to add a "-btn" suffix (yes, I use macros
to save typing tedium) and then
invoke my make-button-contracts macro inside of my class/c* macro. The job of
the former is to expand
into a literal form that starts with init-field-accessor with some extra
arguments which is interpreted specially by the latter.
When I can get make-button-contracts to be invoked, it runs correctly,
expanding into an argument list starting with init-field-accessor.
class/c* (star at the end) is my macro that expands argument pairs starting
with literal symbols init-field-accessor and field-accessor
into sets of init, field, and method definitions for mutations of a given
name---or passes regular class/c statements through unmodified.
The problem is that when Racket is in the define-sequencer-buttons-info-class
transformer procedure, it constructs a definition including
an invocation of make-button-contracts nested inside my invocation of class/c*.
When class/c* then gets expanded, it passes the
invocation of make-button-contracts through to the standard class/c (without
the star)---unmodified----and then starts expanding class/c
without having first expanded make-button-contracts.
It further seems, that class/c does not macro-expand any of its arguments, but
only matches literal names, so how am I going to get my make-button-contracts
to expand
BEFORE class/c* is expanded? How can I do this with Racket?
Finally, lest anyone be confused. I use a convention where my s-expression
lists that represent immediate procedure applications at that point in the
source are surrounded with braces, the ones that are expanded to macros or
core, non-procedure functionality are in parentheses, and the rest are in
brackets. It's unconventional, but it works for me.
Many thanks!
---Christopher
; module total-ca-state.rkt (main file) [ file abbreviated to relevant portions
]
(define-syntax [make-button-contracts stx]
(syntax-case stx []
[[_ btn-name ...]
(let [[ret #'(init-field-accessor [btn-name {is-a?/c button-info%}] ...)]]
{print ret}
ret )]))
(define-syntax [define-sequencer-buttons-info-class stx]
(syntax-case stx []
[[_ name button ...]
(let [[buttons
(for/list [[btn-id {in-list {syntax->list #'[button ...]}}]]
{datum->syntax
btn-id
{string->symbol
{string-append
{symbol->string {syntax->datum btn-id}}
"-btn" }}
btn-id
btn-id })]]
(define ret
#`(define name
(class/c*
(make-button-contracts #,@buttons) )))
{print ret}
ret )]))
(provide sequencer-buttons-info%)
(define-sequencer-buttons-info-class sequencer-buttons-info%
next-user
previous-user
; ... long list of names (abbreviated) ...
lock-user
)
; module class-util.rkt (required by the other file) [ truncated to relevant
portions ]
(define-syntax [class/c* stx]
(define [flatten lst [times 1]]
(if {zero? times}
lst
{flatten {apply append lst} {sub1 times}} ))
(syntax-case stx []
[[_ clause ...]
(let []
(define clause-list {syntax->list #'[clause ...]})
(when {zero? {length clause-list}}
{raise-syntax-error #f
"must supply at least one clause"
stx })
(define syntax-ret
{datum->syntax
stx
{append
{list #'class/c}
{flatten
(for/list [[clause {in-list clause-list}]]
(syntax-case clause [init-field-accessor field-accessor]
[[init-field-accessor sub-clause ...]
{init-field-accessor* clause} ]
[[field-accessor sub-clause ...]
{field-accessor* clause} ]
; THIS IS WHERE THE CALL TO make-button-contracts IS PASSED
THRU TO REGULAR class/c
[whole-thing {list {list clause}}] ))
2 }}
stx})
{print syntax-ret}
syntax-ret )]))
____________________
Racket Users list:
http://lists.racket-lang.org/users