Ah, I see. Thank you for that! As for rephrasing my original question: If program were provided as #%module-begin, I expect that syntax-protect would prevent a programmer from using out the phase-0 bindings. But in my version of the program Racket would keep complaining about unbound identifiers unless I provided everything.
The thing is, I don't want the user to be able to write things like set-field* in their program just because I'm forced to provide it. That's why I'm trying to figure out a way to say "Ok, this module language happens to include set-field*, but set-field* must only appear in module context, or not at all." ~slg ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Thursday, December 31, 2020 1:40 PM, Jay McCarthy <[email protected]> wrote: > I meant like this: > > ``` > #lang racket/base > (require (for-syntax racket/base > syntax/parse) > syntax/parse/define) > > ;; Run-time > (struct state (a b c d) #:transparent) > (define mt (state #f #f #f #f)) > (define-simple-macro (set-field st:expr f:id v:expr) > (struct-copy state st [f v])) > (define-simple-macro (set-field* f:id v:expr) > (λ (st) (set-field st f v))) > > ;; Compile-time > (begin-for-syntax > (define-struct modifier (f))) > > (define-simple-macro (define-modifier (m . args) body) > (begin > (define-syntax m-f > (syntax-parser [(_ args) #'body])) > (define-syntax m (modifier #'m-f)))) > > (define-modifier (the-a-says x:string) > (set-field* a x)) > (define-modifier (the-b-says 'x:id) > (set-field* b 'x)) > (define-modifier (the-c-is x:string) > (set-field* c x)) > (define-modifier (the-d-looks-like x:nat) > (set-field* d x)) > > (define-syntax-parser term > [(_ ((~var m (static modifier? "state modifier")) . args)) > #:with f (modifier-f (attribute m.value)) > #'(f args)]) > > (define-syntax-parser program > [(_) #'mt] > [(_ x . more) #'((term x) (program . more))]) > > ;; Example > (program > (the-a-says "Aaah") > (the-b-says 'Baah) > (the-c-is "Caaah") > (the-d-looks-like 42)) > ``` > > -- > Jay McCarthy > Associate Professor @ CS @ UMass Lowell > http://jeapostrophe.github.io > Vincit qui se vincit. > > On Thu, Dec 31, 2020 at 12:07 PM Sage Gerard <[email protected]> wrote: > >> I didn't see an answer to my original question, but I still want to make >> sure I understood you before I rephrase. Thank you for the insights! >> >> By your three step suggestion, is something like this what you meant? I'm >> not sure I understood Step 3. It seems like I still have to add new >> structure types and macros to define new terms, which is about as laborious >> as what I was doing with #:datum-literals and struct-copy. >> >> (module id racket/base >> (require racket/format >> (for-syntax racket/base syntax/parse)) >> >> (begin-for-syntax (struct item (name num))) >> >> (define (record-item name num) >> (~a num ". " name)) >> >> (define-syntax (term stx) >> (syntax-parse stx >> #:literals ([stmt item #:phase 1]) >> [(_ (stmt name:id num:exact-positive-integer)) >> #'(define name (record-item 'name num))])) >> >> (define-syntax-rule (program x ...) >> (begin (term x) ...))) >> >> ~slg >> >> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ >> On Thursday, December 31, 2020 9:45 AM, Jay McCarthy >> <[email protected]> wrote: >> >>> Are you trying to take a macro argument and ensure that it is an >>> expression? If so, then you can expand into `#%expression` >>> >>> https://docs.racket-lang.org/reference/__expression.html >>> >>> On the other hand, if you are trying to take a macro argument and ensure >>> that it is NOT an expression... then that means that you know what it is >>> allowed to be, so I don't think there's any better way than to just >>> enumerate what it CAN be. I think I would use a syntax class that specifies >>> the allowable patterns and then use that. For example, in my little >>> teachlog language, I have the `term` syntax class for this purpose >>> >>> https://github.com/jeapostrophe/teachlog/blob/master/main.rkt#L119 >>> >>> Looking at your code, I think what I would do is: >>> 1) Define a phase-1 structure that represents one of these fields and an >>> associated phase-0 function that records its values >>> 2) Define `define-syntax` bindings for each particular field as an instance >>> of these fields >>> 3) Write `update` as a short `syntax-parse` that expects a list where the >>> head is a static instance of the phase-1 structure and expands into an >>> application of the associate phase-0 function on the arguments >>> >>> This would allow you to better abstract things so you don't have tie the >>> `update` function to the particular fields. >>> >>> Jay >>> >>> -- >>> Jay McCarthy >>> Associate Professor @ CS @ UMass Lowell >>> http://jeapostrophe.github.io >>> Vincit qui se vincit. >>> >>> On Wed, Dec 30, 2020 at 10:20 PM Sage Gerard <[email protected]> wrote: >>> >>>> I'm trying to learn how to restrict where expressions appear. Those >>>> expressions might be procedure applications, or macros before expansion. >>>> >>>> [1] shows a library I use to help me implement a collection pass for a >>>> module language. To save you some reading, it uses syntax-parse with a >>>> lengthy #:datum-literals. That's the only way I know how to restrict what >>>> expressions appear in module context. >>>> >>>> One element of the #:datum-literals happens to share an identifier with a >>>> bound procedure, so I expand the calls as-is in a module-level expression >>>> [2][3]. I want that procedure to be applied ONLY in the module context, >>>> but nothing in the language enforces that. >>>> >>>> I don't know what I don't know. Could I please get a link to a part of the >>>> documentation that teaches me what I need to understand? I'm tied between >>>> querying syntax properties for a fully expanded module, and writing a >>>> second set of macros that somehow know where they should be. Not sure >>>> which is best. >>>> >>>> [1]: https://github.com/zyrolasting/xiden/blob/master/pkgdef/expand.rkt >>>> [2]: >>>> https://github.com/zyrolasting/xiden/blob/master/pkgdef/expand.rkt#L111 >>>> [3]: >>>> https://github.com/zyrolasting/xiden/blob/master/pkgdef/expand.rkt#L156 >>>> >>>> ~slg >>>> >>>> -- >>>> You received this message because you are subscribed to the Google Groups >>>> "Racket Users" group. >>>> To unsubscribe from this group and stop receiving emails from it, send an >>>> email to [email protected]. >>>> To view this discussion on the web visit >>>> [https://groups.google.com/d/msgid/racket-users/bVbaZ_0mwFcWTIaeuwqMUr7TVY6Rhr5dusG9LkbT0gqW7gWIYAb8IOEUYnKQPIVR2ZrDGm9QMGnW-2YvYqw81oUJVCSCuwhuX_Wx2OGVG-w%3D%40sagegerard.com](https://groups.google.com/d/msgid/racket-users/bVbaZ_0mwFcWTIaeuwqMUr7TVY6Rhr5dusG9LkbT0gqW7gWIYAb8IOEUYnKQPIVR2ZrDGm9QMGnW-2YvYqw81oUJVCSCuwhuX_Wx2OGVG-w%3D%40sagegerard.com?utm_medium=email&utm_source=footer). >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Racket Users" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion on the web visit >> [https://groups.google.com/d/msgid/racket-users/AqmR-7s3hD8k0bf_kMXDsZzwswfEDWXltrNMX4L8EmghOzqZdXHsptRiSANiepcq-m_JBjriooGpx8PbJmu9Bppci68ViREo6un9Fv0uXM0%3D%40sagegerard.com](https://groups.google.com/d/msgid/racket-users/AqmR-7s3hD8k0bf_kMXDsZzwswfEDWXltrNMX4L8EmghOzqZdXHsptRiSANiepcq-m_JBjriooGpx8PbJmu9Bppci68ViREo6un9Fv0uXM0%3D%40sagegerard.com?utm_medium=email&utm_source=footer). -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/nV9CQYlVP_TDoZiSKSx9xKAATgrOhrG2z9Q5erXF04cza6pfHq3kUIPiTEUZX3HNOJeFVDrcbG-f6qHOYTT79yS6JPYKDM5KbtAdCbWoMdM%3D%40sagegerard.com.

