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