Skip to content

Commit 9be5c6f

Browse files
AlexKnauthlexi-lambda
authored andcommitted
Allow pattern expansion in "head" positions
1 parent 6b70a37 commit 9be5c6f

File tree

2 files changed

+88
-21
lines changed

2 files changed

+88
-21
lines changed

hackett-lib/hackett/private/adt.rkt

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,8 @@
200200
(struct pat-base (stx) #:transparent)
201201
(struct pat-var pat-base (id) #:transparent)
202202
(struct pat-hole pat-base () #:transparent)
203-
(struct pat-con pat-base (constructor pats) #:transparent)
203+
(struct pat-con pat-base (constructor) #:transparent)
204+
(struct pat-app pat-base (head pats) #:transparent)
204205
(struct pat-str pat-base (str) #:transparent)
205206
(struct pat-int pat-base (int) #:transparent)
206207
(define pat? pat-base?)
@@ -218,23 +219,13 @@
218219
#:with :pat (local-apply-transformer trans #'pat-exp 'expression)]
219220

220221
[pattern {~and constructor:data-constructor-val ~!}
221-
#:do [(define val (attribute constructor.local-value))
222-
(define arity (data-constructor-arity val))]
223-
#:fail-unless (zero? arity)
224-
(~a "cannot match ‘" (syntax-e #'constructor) "’ as a value; it is a "
225-
"constructor with arity " arity)
226-
#:attr pat (pat-con this-syntax val '())
222+
#:do [(define val (attribute constructor.local-value))]
223+
#:attr pat (pat-con #'constructor val)
227224
#:attr disappeared-uses (list (syntax-local-introduce #'constructor))]
228-
[pattern (~parens constructor:data-constructor-val ~! arg:pat ...)
229-
#:do [(define val (attribute constructor.local-value))
230-
(define arity (data-constructor-arity val))]
231-
#:fail-when {(length (attribute arg)) . < . arity}
232-
(~a "not enough arguments provided for constructor ‘"
233-
(syntax-e #'constructor) "’, which has arity " arity)
234-
#:fail-when {(length (attribute arg)) . > . arity}
235-
(~a "too many arguments provided for constructor ‘"
236-
(syntax-e #'constructor) "’, which has arity " arity)
237-
#:attr pat (pat-con this-syntax (attribute constructor.local-value) (attribute arg.pat))
225+
[pattern (~parens head:pat ~! arg:pat ...)
226+
#:attr pat (pat-app this-syntax
227+
(attribute head.pat)
228+
(attribute arg.pat))
238229
#:attr disappeared-uses (cons (syntax-local-introduce #'constructor)
239230
(append* (attribute arg.disappeared-uses)))]
240231
[pattern {~braces a:pat constructor:data-constructor-val b:pat}
@@ -246,7 +237,8 @@
246237
#:fail-when (not (= arity 2))
247238
(~a "cannot match ‘" (syntax-e #'constructor) "’ infix; it has arity "
248239
arity ", but constructors matched infix must have arity 2")
249-
#:attr pat (pat-con this-syntax (attribute constructor.local-value)
240+
#:attr pat (pat-app this-syntax
241+
(pat-con #'constructor val)
250242
(list (attribute a.pat) (attribute b.pat)))
251243
#:attr disappeared-uses (cons (syntax-local-introduce #'constructor)
252244
(append (attribute a.disappeared-uses)
@@ -301,12 +293,34 @@
301293
(values (expand-type #'String) '() #{values #`(app force- #,str) %})]
302294
[(pat-int _ int)
303295
(values (expand-type #'Integer) '() #{values #`(app force- #,int) %})]
304-
[(pat-con _ con pats)
296+
[(pat-con stx con)
297+
(define arity (data-constructor-arity con))
298+
(unless (zero? arity)
299+
(raise-syntax-error #f
300+
(~a "cannot match ‘" (syntax-e stx) "’ as a value; it is a "
301+
"constructor with arity " arity)
302+
stx))
303+
(pat⇒! (pat-app stx pat '()))]
304+
[(pat-app stx (pat-con cstx con) pats)
305+
(define arity (data-constructor-arity con))
306+
(when {(length pats) . < . arity}
307+
(raise-syntax-error #f
308+
(~a "not enough arguments provided for constructor ‘"
309+
(syntax-e cstx) "’, which has arity " arity)
310+
stx))
311+
(when {(length pats) . > . arity}
312+
(raise-syntax-error #f
313+
(~a "too many arguments provided for constructor ‘"
314+
(syntax-e cstx) "’, which has arity " arity)
315+
stx))
316+
305317
(let*-values ([(τs_args τ_result) (data-constructor-args/result! con)]
306318
[(assumps mk-pats) (pats⇐! pats τs_args)])
307319
(values τ_result assumps
308320
(λ (ids) (let-values ([(match-pats rest) (mk-pats ids)])
309-
(values ((data-constructor-make-match-pat con) match-pats) rest)))))]))
321+
(values ((data-constructor-make-match-pat con) match-pats) rest)))))]
322+
[(pat-app outer-stx (pat-base inner-stx) _)
323+
(raise-syntax-error #f "expected a constructor" outer-stx inner-stx)]))
310324

311325
(define/contract (pat⇐! pat t)
312326
(-> pat? type?
@@ -435,7 +449,8 @@
435449
; When we hit a constructor pattern, we check the ideal. If it is a constructor, compare the
436450
; tags and then recur for the sub-patterns. If it is a variable, then split the ideal into new
437451
; ideals for each kind of constructor.
438-
[(pat-con _ ctor sub-pats)
452+
[(or (pat-app _ (pat-con _ ctor) sub-pats)
453+
(and (pat-con _ ctor) (app (λ (x) '()) sub-pats)))
439454
(match q
440455
[(ideal-con ctor-tag sub-ideals)
441456
(and (eq? (syntax-local-value ctor-tag) ctor)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#lang hackett
2+
3+
(require hackett/private/test
4+
(only-in racket/base
5+
define-syntax for-syntax begin-for-syntax)
6+
(for-syntax racket/base
7+
syntax/parse
8+
(only-in hackett/private/prop-case-pattern-expander
9+
case-pattern-expander)))
10+
11+
(begin-for-syntax
12+
(struct group [hash])
13+
(define (group-ref g x)
14+
(hash-ref (group-hash g) x)))
15+
16+
(define-syntax group-ref
17+
(case-pattern-expander
18+
(syntax-parser
19+
[(_ {~var G (static group? "group")} x)
20+
(group-ref (attribute G.value) (syntax-e #'x))])))
21+
22+
(data Result
23+
(Success Integer)
24+
(Failure String))
25+
26+
(define-syntax G (group (hash 'good #'Success 'bad #'Failure)))
27+
28+
(test {(case (Success 5)
29+
[((group-ref G good) x) (Just x)]
30+
[((group-ref G bad) y) Nothing])
31+
==!
32+
(Just 5)})
33+
34+
(data T (C Integer Integer))
35+
36+
(define-syntax n
37+
(case-pattern-expander
38+
(syntax-parser
39+
[(n) #'m])))
40+
41+
(define-syntax m
42+
(case-pattern-expander
43+
(syntax-parser
44+
[:id #'C]
45+
[(_ . _)
46+
(raise-syntax-error #f "must use `m` as an identifier" this-syntax)])))
47+
48+
(test {(case (C 1 2)
49+
[((n) x y) x])
50+
==!
51+
1})
52+

0 commit comments

Comments
 (0)