Skip to content

Commit 194dbab

Browse files
committed
Enforce autopticity, i.e. a requirement that the structural syntax objects parsed by a macro call are within the lexical scope of a hypothetical binding located at the call site location. Some of the things we defined using define-match-expander-attenuated, define-match-expander-from-match-and-make, and define-imitation-simple-struct were already enforcing autopticity since those utilities have recently been updated to enforce it.
1 parent 5e4f268 commit 194dbab

18 files changed

Lines changed: 351 additions & 209 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
racket-catalogs-id: "racksnaps"
2929
racket-catalogs: |
3030
https://download.racket-lang.org/releases/8.18/catalog/,
31-
https://racksnaps.defn.io/built-snapshots/2025/09/03/catalog/
31+
https://racksnaps.defn.io/built-snapshots/2025/09/08/catalog/
3232
steps:
3333
- name: Checkout
3434
uses: actions/checkout@v4

notes/code-sketches/hypertee-macros/hypertee-macro.rkt

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
;
55
; A framework for macros which take hypersnippet-shaped syntax.
66

7-
; Copyright 2018-2019, 2021 The Lathe Authors
7+
; Copyright 2018-2019, 2021, 2025 The Lathe Authors
88
;
99
; Licensed under the Apache License, Version 2.0 (the "License");
1010
; you may not use this file except in compliance with the License.
@@ -21,13 +21,14 @@
2121

2222
(require #/only-in racket/contract/base -> any/c list/c)
2323
(require #/only-in racket/contract/region define/contract)
24-
(require #/only-in syntax/parse id syntax-parse)
24+
(require #/only-in syntax/parse ~not id syntax-parse)
2525

2626
(require #/only-in lathe-comforts dissect expect fn mat w- w-loop)
2727
(require #/only-in lathe-comforts/list list-foldr list-map)
2828
(require #/only-in lathe-comforts/maybe
2929
just maybe? maybe-bind maybe-map nothing)
3030
(require #/only-in lathe-comforts/struct struct-easy)
31+
(require #/only-in lathe-comforts/syntax ~autoptic-to)
3132
(require #/only-in lathe-comforts/trivial trivial)
3233

3334
(require #/only-in punctaffy/hypersnippet/dim
@@ -159,19 +160,31 @@
159160
(struct-easy (ht-tag-2-prefab key stx-example) #:equal)
160161
(struct-easy (ht-tag-2-other val) #:equal)
161162

162-
; This recursively converts the given Racket syntax object into an
163+
; This recursively converts the given Racket syntax object into a
163164
; degree-omega hypertee. It performs a kind of macroexpansion on lists
164165
; that begin with an identifier with an appropriate
165166
; `syntax-local-value` binding. For everything else, it uses
166167
; particular data structures in the holes of the result hypertee to
167168
; represent the other atoms, proper lists, improper lists, vectors,
168169
; and prefab structs it encounters.
169170
;
170-
(define/contract (s-expr-stx->ht-expr stx)
171-
(-> syntax? hypertee?)
172-
(mat
171+
(define/contract (s-expr-stx->ht-expr err-dsl-stx stx)
172+
(-> syntax? syntax? hypertee?)
173+
(w- default
174+
(fn
175+
; We return a degree-omega hypertee with trivial contents in its
176+
; degree-0 hole, and with a single degree-1 hole that contains
177+
; `stx` itself (perhaps put in some kind of container so that it
178+
; can be distinguished from degree-1 holes that a user-defined
179+
; syntax introduces for a different reason).
180+
(ht-bracs ds (omega) (htb-labeled 1 #/ht-tag-1-s-expr-stx stx) 0
181+
#/htb-labeled 0 #/trivial))
182+
#/syntax-parse stx #:context err-dsl-stx
183+
[{~not #/~autoptic-to err-dsl-stx _} (default)]
184+
#/ _
185+
#/mat
173186
(syntax-parse stx
174-
[ (op:id arg ...)
187+
[ (op:id . args)
175188
(maybe-bind (syntax-local-maybe #'op) #/fn op
176189
#/maybe-map (ht-builder-syntax-maybe op) #/fn proc
177190
#/list op proc)]
@@ -208,10 +221,11 @@
208221
; `s-expr-stx->ht-expr` reports that it has broken its own
209222
; contract.
210223
;
211-
(proc op stx)
224+
(proc op err-dsl-stx stx)
212225
#/w- process-list
213226
(fn elems
214-
(list-map elems #/fn elem #/s-expr-stx->ht-expr elem))
227+
(list-map elems #/fn elem
228+
(s-expr-stx->ht-expr err-dsl-stx elem)))
215229
; NOTE: We go to some trouble to detect improper lists here. This is
216230
; so we can preserve the metadata of syntax objects occurring in
217231
; tail positions partway through the list, which we would lose track
@@ -250,7 +264,7 @@
250264
; Usually it'll be wrapped up as an atom. However, it could still
251265
; be expanded as a identifier syntax or processed as a vector or
252266
; as a prefab struct.
253-
#/w- tail (s-expr-stx->ht-expr tail)
267+
#/w- tail (s-expr-stx->ht-expr err-dsl-stx tail)
254268
; This is like the proper list case, but this time the metadata
255269
; represents an improper list operation (`list*`) rather than a
256270
; proper list operation (`list`).
@@ -271,17 +285,9 @@
271285
; metadata represents a vector operation (`vector`) rather
272286
; than a proper list operation (`list`).
273287
(make-list-layer (ht-tag-2-vector stx-example) elems))]
274-
275-
[_
276-
; We return a degree-omega hypertee with trivial contents in its
277-
; degree-0 hole, and with a single degree-1 hole that contains
278-
; `stx` itself (perhaps put in some kind of container so that it
279-
; can be distinguished from degree-1 holes that a user-defined
280-
; syntax introduces for a different reason).
281-
(ht-bracs ds (omega) (htb-labeled 1 #/ht-tag-1-s-expr-stx stx) 0
282-
#/htb-labeled 0 #/trivial)]))
288+
[_ (default)]))
283289

284-
; This recursively converts the given Racket syntax object into an
290+
; This recursively converts the given Racket syntax object into a
285291
; degree-omega hypertee just like `s-expr-stx->ht-expr`, but it
286292
; expects the outermost layer of the syntax object to be a proper
287293
; list, and it does not represent that list in the result, so the
@@ -292,20 +298,20 @@
292298
; reminder that ht-expressions aren't quite "expressions" so much as
293299
; snippets of expression-like data.
294300
;
295-
(define/contract (splicing-s-expr-stx->ht-expr stx)
296-
(-> syntax? hypertee?)
301+
(define/contract (splicing-s-expr-stx->ht-expr err-dsl-stx stx)
302+
(-> syntax? syntax? hypertee?)
297303
(hypertee-append-zero ds (omega)
298304
#/list-map (syntax->list stx) #/fn elem
299-
(s-expr-stx->ht-expr elem)))
305+
(s-expr-stx->ht-expr err-dsl-stx elem)))
300306

301307

302308
(struct-easy (simple-ht-builder-syntax impl)
303309
#:other
304310
#:property prop:ht-builder-syntax
305-
(fn this stx
311+
(fn this err-dsl-stx stx
306312
(expect this (simple-ht-builder-syntax impl)
307313
(error "Expected this to be a simple-ht-builder-syntax")
308-
#/impl stx)))
314+
#/impl err-dsl-stx stx)))
309315

310316
(struct-easy
311317
(syntax-and-ht-builder-syntax syntax-impl ht-builder-syntax-impl)
@@ -320,10 +326,10 @@
320326
#/syntax-impl stx))
321327

322328
#:property prop:ht-builder-syntax
323-
(fn this stx
329+
(fn this err-dsl-stx stx
324330
(expect this
325331
(syntax-and-ht-builder-syntax
326332
syntax-impl ht-builder-syntax-impl)
327333
(error "Expected this to be a syntax-and-ht-builder-syntax")
328-
#/ht-builder-syntax-impl stx))
334+
#/ht-builder-syntax-impl err-dsl-stx stx))
329335
)

notes/code-sketches/hypertee-macros/qq.rkt

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
; sequences which can generalize unquotation and nested
77
; quasiquotation.
88

9-
; Copyright 2018-2019, 2021 The Lathe Authors
9+
; Copyright 2018-2019, 2021, 2025 The Lathe Authors
1010
;
1111
; Licensed under the Apache License, Version 2.0 (the "License");
1212
; you may not use this file except in compliance with the License.
@@ -22,7 +22,10 @@
2222

2323

2424
(require #/for-syntax racket/base)
25-
(require #/for-syntax #/only-in syntax/parse id syntax-parse)
25+
(require #/for-syntax #/only-in syntax/parse
26+
id syntax-parse this-syntax)
27+
28+
(require #/only-in lathe-comforts define-syntax-parse-rule/autoptic)
2629

2730
(provide my-quasiquote)
2831

@@ -37,6 +40,7 @@
3740
(require #/only-in lathe-comforts dissect expect fn mat w-)
3841
(require #/only-in lathe-comforts/maybe just)
3942
(require #/only-in lathe-comforts/struct struct-easy)
43+
(require #/only-in lathe-comforts/syntax ~autoptic-list)
4044
(require #/only-in lathe-comforts/trivial trivial)
4145

4246
(require #/only-in "hypertee-macro.rkt"
@@ -77,7 +81,7 @@
7781
#:equal)
7882

7983
(define (make-op-bracket call-stx)
80-
(syntax-parse call-stx #/ (op body)
84+
(syntax-parse call-stx #/ {~autoptic-list (op body)}
8185
#/ht-bracs ds (omega)
8286
(htb-labeled 2 #/ht-tag-2-list #/datum->syntax call-stx #/list)
8387
1
@@ -90,7 +94,7 @@
9094
#/htb-labeled 0 #/trivial))
9195

9296
(define my-quasiquote-uq
93-
(simple-ht-builder-syntax #/fn stx
97+
(simple-ht-builder-syntax #/fn err-dsl-stx stx
9498
(syntax-parse stx
9599
[op:id
96100
; If this syntax transformer is used in an identifier
@@ -103,7 +107,7 @@
103107
(htb-labeled 1 #/ht-tag-1-s-expr-stx stx)
104108
0
105109
#/htb-labeled 0 #/trivial)]
106-
#/ (op interpolation)
110+
#/ {~autoptic-list (op interpolation)}
107111
#/ht-bracs ds (omega)
108112
(htb-labeled 1
109113
#/ht-tag-1-other #/my-quasiquote-tag-1-unmatched-unquote
@@ -113,7 +117,7 @@
113117
#/htb-labeled 0 #/trivial)))
114118

115119
(define my-quasiquote-qq
116-
(simple-ht-builder-syntax #/fn stx
120+
(simple-ht-builder-syntax #/fn err-dsl-stx stx
117121
(syntax-parse stx
118122
[op:id
119123
; If this syntax transformer is used in an identifier
@@ -123,8 +127,8 @@
123127
(htb-labeled 1 #/ht-tag-1-s-expr-stx stx)
124128
0
125129
#/htb-labeled 0 #/trivial)]
126-
#/ (op body)
127-
#/w- body (s-expr-stx->ht-expr #'body)
130+
#/ {~autoptic-list (op body)}
131+
#/w- body (s-expr-stx->ht-expr err-dsl-stx #'body)
128132
; We make a hypertee with a single degree-2 hole (annotated with
129133
; the body of the quasiquotation) and some degree-1 holes
130134
; (annotated with the `s-expr-stx->ht-expr` expansions of the
@@ -149,7 +153,7 @@
149153
closing-bracket interpolation)
150154
(hypertee-increase-degree-to (omega) hole)
151155
#/hypertee-done (omega)
152-
(s-expr-stx->ht-expr interpolation)
156+
(s-expr-stx->ht-expr err-dsl-stx interpolation)
153157
hole)))))
154158

155159
(define (my-quasiquote-ht-expr->stx ht-expr)
@@ -291,16 +295,18 @@
291295
stx))
292296

293297
(define (my-quasiquote-begin-fn stx)
294-
(syntax-parse stx #/ (_ quotation:expr)
295-
#/my-quasiquote-ht-expr->stx #/s-expr-stx->ht-expr #'quotation))
298+
(syntax-parse stx #/
299+
{~autoptic-list (_ err-dsl-stx quotation:expr)}
300+
#/my-quasiquote-ht-expr->stx
301+
(s-expr-stx->ht-expr #'err-dsl-stx #'quotation)))
296302

297303
)
298304
(require #/for-syntax 'part1-private)
299305

300306

301307
(define-syntax my-quasiquote-begin my-quasiquote-begin-fn)
302308

303-
(define-syntax (my-quasiquote stx)
304-
(syntax-parse stx #/ (_ uq:id (qq:id body))
305-
#'(let-syntax ([uq my-quasiquote-uq] [qq my-quasiquote-qq])
306-
#/my-quasiquote-begin body)))
309+
(define-syntax-parse-rule/autoptic (my-quasiquote uq:id (qq:id body))
310+
#:with err-dsl-stx this-syntax
311+
(let-syntax ([uq my-quasiquote-uq] [qq my-quasiquote-qq])
312+
#/my-quasiquote-begin err-dsl-stx body))

punctaffy-doc/scribblings/private/shim.rkt

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,24 @@
2525
(require #/for-syntax #/only-in syntax/parse syntax-parse)
2626

2727
(require #/for-syntax #/only-in lathe-comforts fn w-)
28+
(require #/for-syntax #/only-in lathe-comforts/syntax ~autoptic-list)
2829

29-
(require scribble/base)
30+
(require scribble/manual)
31+
32+
(require #/only-in lathe-comforts define-syntax-parse-rule/autoptic)
3033

3134

3235
(provide
3336
shim-require-various-for-label
34-
code-block)
37+
lathe-comforts-doc
38+
code-block
39+
enforces-autopticity
40+
constructor-enforces-autopticity)
3541

3642

3743
(define-syntax (shim-require-various-for-label stx)
3844
(syntax-protect
39-
#/syntax-parse stx #/ (_)
45+
#/syntax-parse stx #/ {~autoptic-list (_)}
4046
#/w- break (fn id #/datum->syntax stx id)
4147
#`(require #/for-label
4248

@@ -60,12 +66,11 @@
6066
(only-in #,(break 'syntax/datum) datum with-datum)
6167
(only-in #,(break 'syntax/parse)
6268
~optional prop:pattern-expander ~seq syntax-parse)
63-
(only-in #,(break 'syntax/parse/define)
64-
define-syntax-parse-rule)
6569
(only-in #,(break 'syntax/parse/experimental/template)
6670
define-template-metafunction)
6771

68-
(only-in #,(break 'lathe-comforts) fn)
72+
(only-in #,(break 'lathe-comforts)
73+
define-syntax-parse-rule/autoptic fn)
6974
(only-in #,(break 'lathe-comforts/contract)
7075
flat-obstinacy obstinacy? obstinacy-contract/c)
7176
(only-in #,(break 'lathe-comforts/list) list-bind)
@@ -104,5 +109,19 @@
104109
)))
105110

106111

107-
(define-syntax-rule @code-block[args ...]
112+
(define lathe-comforts-doc
113+
'(lib "lathe-comforts/scribblings/lathe-comforts.scrbl"))
114+
115+
116+
(define-syntax-parse-rule/autoptic @code-block[args ...]
108117
@nested[#:style 'code-inset]{@verbatim[args ...]})
118+
119+
(define @enforces-autopticity[]
120+
@list{
121+
This syntax must be called with @tech[#:doc lathe-comforts-doc]{autopticity}.
122+
})
123+
124+
(define @constructor-enforces-autopticity[]
125+
@list{
126+
The constructor syntax must be called with @tech[#:doc lathe-comforts-doc]{autopticity}.
127+
})

punctaffy-doc/scribblings/punctaffy/baseline-hyperbracket.scrbl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
@;
55
@; Baseline hyperbracket notations.
66

7-
@; Copyright 2021, 2022 The Lathe Authors
7+
@; Copyright 2021, 2022, 2025 The Lathe Authors
88
@;
99
@; Licensed under the Apache License, Version 2.0 (the "License");
1010
@; you may not use this file except in compliance with the License.
@@ -47,6 +47,8 @@ Whether a certain notation uses Punctaffy's hyperbrackets or not, if it has a st
4747
This notation is an instance of @racket[taffy-notation-akin-to-^<>d?] at compile time, so a hyperbracket parser which recognizes such syntaxes will be sufficient for parsing it.
4848

4949
For the common case where @racket[degree] is 2, see the shorthand @racket[^<], which has examples in @secref["intro"]. For a rare example of hypothetical code where @racket[degree] would be 3, see @secref["potential-use-case-ellipsis-unsyntax"].
50+
51+
@enforces-autopticity[]
5052
}
5153

5254
@defform[(^>d degree term ...)]{
@@ -57,6 +59,8 @@ Whether a certain notation uses Punctaffy's hyperbrackets or not, if it has a st
5759
This notation is an instance of @racket[taffy-notation-akin-to-^<>d?] at compile time, so a hyperbracket parser which recognizes such syntaxes will be sufficient for parsing it.
5860

5961
For the common case where @racket[degree] is 1, see the shorthand @racket[^>], which has examples in @secref["intro"]. For a rare example of hypothetical code where @racket[degree] would be 2, see @secref["potential-use-case-ellipsis-unsyntax"].
62+
63+
@enforces-autopticity[]
6064
}
6165

6266
@defform[(^< term ...)]{
@@ -71,6 +75,8 @@ Whether a certain notation uses Punctaffy's hyperbrackets or not, if it has a st
7175
This notation is an instance of @racket[taffy-notation-akin-to-^<>d?] at compile time, so a hyperbracket parser which recognizes such syntaxes will be sufficient for parsing it.
7276

7377
For examples of how to use @racket[^<] and @racket[^>], see @secref["intro"].
78+
79+
@enforces-autopticity[]
7480
}
7581

7682
@defform[(^> term ...)]{
@@ -85,4 +91,6 @@ Whether a certain notation uses Punctaffy's hyperbrackets or not, if it has a st
8591
This notation is an instance of @racket[taffy-notation-akin-to-^<>d?] at compile time, so a hyperbracket parser which recognizes such syntaxes will be sufficient for parsing it.
8692

8793
For examples of how to use @racket[^<] and @racket[^>], see @secref["intro"].
94+
95+
@enforces-autopticity[]
8896
}

0 commit comments

Comments
 (0)