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.

Reply via email to