summaryrefslogtreecommitdiff
path: root/cfn/pattern.scm
blob: 64badbf3d9a42b36b61232c736f55af59a9300f8 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
;;; pattern.scm -- cfn processing of pattern-related AST structures
;;;
;;; author :  Sandra Loosemore
;;; date   :  27 Feb 1992
;;;
;;; This file contains specialized CFN walkers for lambda, case, and valdef
;;; structures.



;;;=====================================================================
;;; Top-level walkers
;;;=====================================================================


;;; The calls to remember-context are so an appropriate error message
;;; can be produced for pattern-matching failures.

(define-walker-method cfn lambda (object)
  (remember-context object
    (do-cfn-lambda (lambda-pats object) (lambda-body object))))


(define-walker-method cfn case (object)
  (remember-context object
    (do-cfn-case
      (case-exp object)
      (case-alts object))))




;;; Valdefs are always processed as a list.

(define (cfn-valdef-list list-of-valdefs)
  (if (null? list-of-valdefs)
      '()
      (nconc (cfn-valdef (car list-of-valdefs))
	     (cfn-valdef-list (cdr list-of-valdefs)))))

(define (cfn-valdef object)
  (remember-context object
    (if (null? (single-fun-def-args (car (valdef-definitions object))))
	;; This is a pattern binding.
	(do-cfn-pattern-def-top object)
	;; This is a function binding.
	;; Branch on single-headed/multi-headed definition.
	(list (add-dict-params
	        object
		(if (null? (cdr (valdef-definitions object)))
		    (do-cfn-function-def-simple object)
		    (do-cfn-function-def-general object))))
      )))


;;; This adds the dictionary parameters needed by the type system.  A valdef
;;; structure has a dictionary-args field which contains the variables to be
;;; bound to dicationary arguments.

(define (add-dict-params original-valdef generated-valdef)
  (let ((vars (valdef-dictionary-args original-valdef)))
    (when (not (null? vars))
      (let* ((sfd  (car (valdef-definitions generated-valdef)))
	     (rhs  (car (single-fun-def-rhs-list sfd)))
	     (exp  (guarded-rhs-rhs rhs))
	     (pats (map (function **var-pat/def) vars)))
	(if (is-type? 'lambda exp)
	    (setf (lambda-pats exp)
		  (nconc pats (lambda-pats exp)))
	    (setf (guarded-rhs-rhs rhs)
		  (**lambda/pat pats exp))))))
  generated-valdef)


;;;=====================================================================
;;; Lambda rewriting
;;;=====================================================================


;;; For lambda, make all the argument patterns into var pats.
;;; Rewrite the body as a CASE to do any more complicated pattern
;;; matching.
;;; The CFN output for lambda is a modified lambda expression with
;;; all var-pats as arguments.

(define (do-cfn-lambda pats body)
  (let ((new-args  '())
	(new-vars  '())
	(new-pats  '()))
    (dolist (p pats)
      (typecase p
	(wildcard-pat
	  (push (**var-pat/def (create-temp-var 'arg)) new-args))
        (var-pat
	  (push p new-args))
	(as-pat
	  (let ((var  (var-ref-var (as-pat-var p))))
	    (push (**var-pat/def var) new-args)
	    (push (**var/def var) new-vars)
	    (push (as-pat-pattern p) new-pats)))
	(else
	  (let ((var  (create-temp-var 'arg)))
	    (push (**var-pat/def var) new-args)
	    (push (**var/def var) new-vars)
	    (push p new-pats)))))
    (setf new-args (nreverse new-args))
    (setf new-vars (nreverse new-vars))
    (setf new-pats (nreverse new-pats))
    (**lambda/pat
      new-args
      (cond ((null? new-vars)
	     ;; No fancy pattern matching necessary.
	     (cfn-ast-1 body))
	    ((null? (cdr new-vars))
	     ;; Exactly one argument to match on.
	     (do-cfn-case (car new-vars)
			  (list (**alt/simple (car new-pats) body))))
	    (else
	     ;; Multiple arguments to match on.
	     (do-cfn-case-tuple
	       new-vars
	       (list (**alt/simple (**tuple-pat new-pats) body))))
	    ))))


;;;=====================================================================
;;; Function definitions
;;;=====================================================================


;;; The output of the CFN for function definitions is a simple 
;;; valdef which binds a variable to a lambda expression.


;;; The simple case:  there is only one set of arguments.

(define (do-cfn-function-def-simple object)
  (let* ((pat    (valdef-lhs object))
	 (sfd    (car (valdef-definitions object))))
    (**valdef/pat
      pat
      (do-cfn-lambda
        (single-fun-def-args sfd)
	(rewrite-guards-and-where-decls
	  (single-fun-def-where-decls sfd)
	  (single-fun-def-rhs-list sfd)
	  '#f)))))


;;; The general case:  generate new variables as the formal parameters 
;;; to the resulting lambda, then use case to do the pattern matching.

(define (do-cfn-function-def-general object)
  (let ((pat   (valdef-lhs object))
	(vars  (map (lambda (p)
		      (declare (ignore p))
		      (create-temp-var 'arg))
		    (single-fun-def-args (car (valdef-definitions object)))))
	(alts  (map (lambda (sfd)
		      (**alt (**tuple-pat (single-fun-def-args sfd))
			     (single-fun-def-rhs-list sfd)
			     (single-fun-def-where-decls sfd)))
		    (valdef-definitions object))))
    (**valdef/pat
      pat
      (**lambda/pat
        (map (function **var-pat/def) vars)
	(if (null? (cdr vars))
	    ;; one-argument case
	    (do-cfn-case (**var/def (car vars)) alts)
	    ;; multi-argument case
	    (do-cfn-case-tuple (map (function **var/def) vars) alts))))
    ))


;;;=====================================================================
;;; Case
;;;=====================================================================


;;; For case, add failure alt, then call helper function to generate
;;; pattern matching tests.
;;; The CFN output for case is a case-block construct.

(define (do-cfn-case exp alts)
  (setf alts
	(append alts
		(list (**alt/simple (**wildcard-pat) (make-failure-exp)))))
  (let ((list-of-pats  	(map (lambda (a) (list (alt-pat a))) alts)))
    (if (is-type? 'var-ref exp)
	(match-pattern-list (list exp) list-of-pats alts)
	(let ((temp  (create-temp-var 'cfn)))
	  (**let (list (**valdef/def temp (cfn-ast-1 exp)))
		 (match-pattern-list
		   (list (**var/def temp))
		   list-of-pats
		   alts)))
      )))



;;; Here's a special case, for when the exp being matched is a tuple
;;; of var-refs and all the alts also have tuple pats.

(define (do-cfn-case-tuple exps alts)
  (setf alts
	(append alts
		(list
		  (**alt/simple
		    (**tuple-pat
		      (map (lambda (e) (declare (ignore e)) (**wildcard-pat))
			   exps))
		    (make-failure-exp)))))
  (match-pattern-list
    exps
    (map (lambda (a) (pcon-pats (alt-pat a))) alts)
    alts))


(define (match-pattern-list exps list-of-pats alts)
  (let ((block-name  (gensym "PMATCH")))
    (**case-block
      block-name
      (map (lambda (a p) (match-pattern exps p a block-name))
	   alts
	   list-of-pats))))


;;; Produce an exp that matches the given alt against the exps.
;;; If the match succeeds, it will return-from the given block-name.

(define (match-pattern exps pats alt block-name)
  (if (null pats)
      ;; No more patterns to match.
      ;; Return an exp that handles the guards and where-decls.
      (cfn-ast-1
        (rewrite-guards-and-where-decls
	  (alt-where-decls alt) (alt-rhs-list alt) block-name))
      ;; Otherwise dispatch on type of first pattern.
      (let ((pat  (pop pats))
	    (exp  (pop exps)))
	(funcall
	  (typecase pat
	    (wildcard-pat (function match-wildcard-pat))
	    (var-pat      (function match-var-pat))
	    (pcon         (function match-pcon))
	    (as-pat       (function match-as-pat))
	    (irr-pat      (function match-irr-pat))
	    (const-pat    (function match-const-pat))
	    (plus-pat     (function match-plus-pat))
	    (list-pat     (function match-list-pat))
	    (else         (error "Unrecognized pattern ~s." pat)))
	  pat
	  exp
	  pats
	  exps
	  alt
	  block-name))))




;;; Wildcard patterns add no pattern matching test.
;;; Just recurse on the next pattern to be matched.

(define (match-wildcard-pat pat exp pats exps alt block-name)
  (declare (ignore pat exp))
  (match-pattern exps pats alt block-name))


;;; A variable pattern likewise does not add any test.  However,
;;; a binding of the variable to the corresponding exp must be added.

(define (match-var-pat pat exp pats exps alt block-name)
  (push (**valdef/pat pat exp)
	(alt-where-decls alt))
  (match-pattern exps pats alt block-name))


;;; Pcons are the hairy case because they may have subpatterns that need
;;; to be matched.
;;; If there are subpats and the exp is not a var-ref, make a let binding.
;;; If the con is a tuple type, there is no need to generate a test
;;; since the test would always succeed anyway.
;;; Do not generate let bindings here for subexpressions; do this lazily
;;; if and when necessary.

(define (match-pcon pat exp pats exps alt block-name)
  (let* ((var?    (is-type? 'var-ref exp))
	 (var     (if var?
		      (var-ref-var exp)
		      (create-temp-var 'conexp)))
	 (con     (pcon-con pat))
	 (arity   (con-arity con))
	 (alg     (con-alg con))
	 (tuple?  (algdata-tuple? alg))
	 (subpats (pcon-pats pat))
	 (subexps '()))
    (dotimes (i arity)
      (push (**sel con (**var/def var) i) subexps))
    (setf exps (nconc (nreverse subexps) exps))
    (setf pats (append subpats pats))
    (let ((tail  (match-pattern exps pats alt block-name)))
      (when (not tuple?)
	(setf tail
	      (**and-exp (**is-constructor (**var/def var) con) tail)))
      (when (not var?)
	(setf tail
	      (**let (list (**valdef/def var (cfn-ast-1 exp))) tail)))
      tail)))


;;; For as-pat, add a variable binding.
;;; If the expression being matched is not already a variable reference,
;;; take this opportunity to make the let binding.  Otherwise push the
;;; let-binding onto the where-decls.

(define (match-as-pat pat exp pats exps alt block-name)
  (let ((var    (var-ref-var (as-pat-var pat)))
	(subpat (as-pat-pattern pat)))
    (if (is-type? 'var-ref exp)
	(begin
	  (push (**valdef/def var (**var/def (var-ref-var exp)))
		(alt-where-decls alt))
	  (match-pattern
	    (cons exp exps)
	    (cons subpat pats)
	    alt
	    block-name))
	(**let (list (**valdef/def var (cfn-ast-1 exp)))
	       (match-pattern
		 (cons (**var/def var) exps)
		 (cons subpat pats)
		 alt
		 block-name)))))


;;; An irrefutable pattern adds no test to the pattern matching,
;;; but adds a pattern binding to the where-decls.

(define (match-irr-pat pat exp pats exps alt block-name)
  (let ((subpat  (irr-pat-pattern pat)))
    (push (**valdef/pat subpat exp) (alt-where-decls alt))
    (match-pattern exps pats alt block-name)))


;;; A const pat has a little piece of code inserted by the typechecker
;;; to do the test.
;;; For matches against string constants, generate an inline test to match 
;;; on each character of the string.

(define (match-const-pat pat exp pats exps alt block-name)
  (let ((const  (const-pat-value pat)))
    (**and-exp 
      (if (is-type? 'string-const const)
	  (let ((string  (string-const-value const)))
	    (if (string=? string "")
		(**is-constructor exp (core-symbol "Nil"))
		(**app (**var/def (core-symbol "primStringEq")) const exp)))
	  (cfn-ast-1 (**app (const-pat-match-fn pat) exp)))
      (match-pattern exps pats alt block-name))
    ))


;;; Plus pats have both a magic test and a piece of code to
;;; make a binding in the where-decls.  Make a variable binding
;;; for the exp if it's not already a variable.

(define (match-plus-pat pat exp pats exps alt block-name)
  (let* ((var?  (is-type? 'var-ref exp))
	 (var   (if var? (var-ref-var exp) (create-temp-var 'plusexp))))
    (push (**valdef/pat (plus-pat-pattern pat)
			(**app (plus-pat-bind-fn pat) (**var/def var)))
	  (alt-where-decls alt))
    (let ((tail  (match-pattern exps pats alt block-name)))
      (setf tail
	    (**and-exp
	      (cfn-ast-1 (**app (plus-pat-match-fn pat) (**var/def var)))
	      tail))
      (if var?
	  tail
	  (**let (list (**valdef/def var exp)) tail)))))


;;; Rewrite list pats as pcons, then process recursively.

(define (match-list-pat pat exp pats exps alt block-name)
  (let ((newpat  (rewrite-list-pat (list-pat-pats pat))))
    (match-pattern
      (cons exp exps)
      (cons newpat pats)
      alt
      block-name)))

(define (rewrite-list-pat subpats)
  (if (null? subpats)
      (**pcon/def (core-symbol "Nil") '())
      (**pcon/def (core-symbol ":")
		  (list (car subpats)
			(rewrite-list-pat (cdr subpats))))))




;;;=====================================================================
;;; Pattern definitions
;;;=====================================================================


(define (do-cfn-pattern-def-top object)
  (typecase (valdef-lhs object)
    (var-pat
      ;; If the pattern definition is a simple variable assignment, it
      ;; may have dictionary parameters that need to be messed with.
      ;; Complicated pattern bindings can't be overloaded in this way.
      (list (add-dict-params object (do-cfn-pattern-def-simple object))))
    (irr-pat
      ;; Irrefutable patterns are redundant here.
      (setf (valdef-lhs object) (irr-pat-pattern (valdef-lhs object)))
      (do-cfn-pattern-def-top object))
    (wildcard-pat
     ;; Wildcards are no-ops.
     '())
    (pcon
     ;; Special-case because it's frequent and general case creates
     ;; such lousy code
     (do-cfn-pattern-def-pcon object))
    (else
      (do-cfn-pattern-def-general object))))


;;; Do a "simple" pattern definition, e.g. one that already has a
;;; var-pat on the lhs.

(define (do-cfn-pattern-def-simple object)
  (let* ((pat  (valdef-lhs object))
	 (sfd  (car (valdef-definitions object)))
	 (exp  (rewrite-guards-and-where-decls
		 (single-fun-def-where-decls sfd)
		 (single-fun-def-rhs-list sfd)
		 '#f)))
  (**valdef/pat pat (cfn-ast-1 exp))))


;;; Destructure a pcon.
;;; Note that the simplified expansion is only valid if none of
;;; the subpatterns introduce tests.  Otherwise we must defer to
;;; the general case.

(define (do-cfn-pattern-def-pcon object)
  (let* ((pat     (valdef-lhs object))
	 (subpats (pcon-pats pat)))
    (if (every (function irrefutable-pat?) subpats)
	(let* ((con     (pcon-con pat))
	       (arity   (con-arity con))
	       (alg     (con-alg con))
	       (tuple?  (algdata-tuple? alg))
	       (temp    (create-temp-var 'pbind))
	       (result  '()))
	  (dotimes (i arity)
	    (setf result
		  (nconc result
			 (do-cfn-pattern-def-top 
			   (**valdef/pat (pop subpats)
					 (**sel con (**var/def temp) i))))))
	  (if (null? result)
	      '()
	      (let* ((sfd   (car (valdef-definitions object)))
		     (exp   (cfn-ast-1
			      (rewrite-guards-and-where-decls
			        (single-fun-def-where-decls sfd)
				(single-fun-def-rhs-list sfd)
				'#f))))
		(when (not tuple?)
		  (let ((temp1  (create-temp-var 'cfn)))
		    (setf exp
			  (**let (list (**valdef/def temp1 exp))
				 (**if (**is-constructor (**var/def temp1) con)
				       (**var/def temp1)
				       (make-failure-exp))))))
		(cons (**valdef/def temp exp) result))))
	(do-cfn-pattern-def-general object))))



;;; Turn a complicated pattern definition into a list of simple ones.
;;; The idea is to use case to match the pattern and build a tuple of
;;; all the values which are being destructured into the pattern
;;; variables.

(define (do-cfn-pattern-def-general object)
  (multiple-value-bind (new-pat vars new-vars)
      (copy-pattern-variables (valdef-lhs object))
    (if (not (null? vars))
	(let* ((sfd      (car (valdef-definitions object)))
	       (exp      (rewrite-guards-and-where-decls
			   (single-fun-def-where-decls sfd)
			   (single-fun-def-rhs-list sfd)
			   '#f))
	       (arity    (length vars)))
	  (if (eqv? arity 1)
	      (list (**valdef/def
		      (var-ref-var (car vars))
		      (do-cfn-case
		        exp
			(list (**alt/simple new-pat (car new-vars))))))
	      (let ((temp     (create-temp-var 'pbind))
		    (bindings '()))
		(dotimes (i arity)
		  (push (**valdef/def (var-ref-var (pop vars))
				      (**tuple-sel arity i (**var/def temp)))
			bindings))
		(cons (**valdef/def
		        temp
			(do-cfn-case
			  exp
			  (list (**alt/simple new-pat (**tuple/l new-vars)))))
		      bindings))))
	'())))



;;; Helper function for above.
;;; All the variables in the pattern must be replaced with temporary
;;; variables.  

(define (copy-pattern-variables pat)
  (typecase pat
    (wildcard-pat
      (values pat '() '()))
    (var-pat
      (let ((new  (create-temp-var (var-ref-name (var-pat-var pat)))))
	(values (**var-pat/def new)
		(list (var-pat-var pat))
		(list (**var/def new)))))
    (pcon
      (multiple-value-bind (new-pats vars new-vars)
	  (copy-pattern-variables-list (pcon-pats pat))
	(values (**pcon/def (pcon-con pat) new-pats)
		vars
		new-vars)))
    (as-pat
      (let ((new  (create-temp-var (var-ref-name (as-pat-var pat)))))
	(multiple-value-bind (new-pat vars new-vars)
	    (copy-pattern-variables (as-pat-pattern pat))
	  (values
	    (make as-pat
		  (var (**var/def new))
		  (pattern new-pat))
	    (cons (as-pat-var pat) vars)
	    (cons (**var/def new) new-vars)))))
    (irr-pat
      (multiple-value-bind (new-pat vars new-vars)
	  (copy-pattern-variables (irr-pat-pattern pat))
	(values
	  (make irr-pat (pattern new-pat))
	  vars
	  new-vars)))
    (const-pat
      (values pat '() '()))
    (plus-pat
      (multiple-value-bind (new-pat vars new-vars)
	  (copy-pattern-variables (plus-pat-pattern pat))
	(values
	  (make plus-pat
		(pattern new-pat)
		(k (plus-pat-k pat))
		(match-fn (plus-pat-match-fn pat))
		(bind-fn (plus-pat-bind-fn pat)))
	  vars
	  new-vars)))
    (list-pat
      (multiple-value-bind (new-pats vars new-vars)
	  (copy-pattern-variables-list (list-pat-pats pat))
	(values (make list-pat (pats new-pats))
		vars
		new-vars)))
    (else
      (error "Unrecognized pattern ~s." pat))))

(define (copy-pattern-variables-list pats)
  (let ((new-pats  '())
	(vars      '())
	(new-vars  '()))
    (dolist (p pats)
      (multiple-value-bind (p v n) (copy-pattern-variables p)
	(push p new-pats)
	(setf vars (nconc vars v))
	(setf new-vars (nconc new-vars n))))
    (values (nreverse new-pats)
	    vars
	    new-vars)))



;;;=====================================================================
;;; Helper functions for processing guards and where-decls
;;;=====================================================================

;;; Process guards and where-decls into a single expression.
;;; If block-name is non-nil, wrap the exp with a return-from.
;;; If block-name is nil, add a failure exp if necessary.
;;; Note that this does NOT do the CFN traversal on the result or
;;; any part of it.

(define (rewrite-guards-and-where-decls where-decls rhs-list block-name)
  (if (null? where-decls)
      (rewrite-guards rhs-list block-name)
      (**let where-decls
	     (rewrite-guards rhs-list block-name))))

(define (rewrite-guards rhs-list block-name)
  (if (null? rhs-list)
      (if block-name
	  (**con/def (core-symbol "False"))
	  (make-failure-exp))
      (let* ((rhs     (car rhs-list))
	     (guard   (guarded-rhs-guard rhs))
	     (exp     (guarded-rhs-rhs rhs)))
	(when block-name
	  (setf exp (**return-from block-name exp)))
	(cond ((is-type? 'omitted-guard (guarded-rhs-guard (car rhs-list)))
	       exp)
	      ((and block-name (null? (cdr rhs-list)))
	       (**and-exp guard exp))
	      (else
	       (**if guard
		     exp
		     (rewrite-guards (cdr rhs-list) block-name)))
	      ))))


(define (make-failure-exp)
  (let ((c  (dynamic *context*)))
    (**abort
      (if (not c)
	  "Pattern match failed."
	  (let* ((stuff  (ast-node-line-number c))
		 (line   (source-pointer-line stuff))
		 (file   (source-pointer-file stuff)))
	    (if (and (is-type? 'valdef c)
		     (is-type? 'var-pat (valdef-lhs c)))
		(format
		  '#f
		  "Pattern match failed in function ~a at line ~s in file ~a."
		  (valdef-lhs c) line file)
		(format
		  '#f
		  "Pattern match failed at line ~s in file ~a."
		  line file)))))))