git.fiddlerwoaroof.com
Browse code

Add tests for pattern-matching

fiddlerwoaroof authored on 24/08/2017 07:20:48
Showing 1 changed files
... ...
@@ -1,23 +1,29 @@
1 1
 (in-package :patmatch)
2 2
 
3
+(define-condition no-pattern (error)
4
+  ((%pattern-obj :initarg :pattern-obj :reader pattern-obj)
5
+   (%pattern-form :initarg :pattern-form :reader pattern-form)
6
+   (%pattern-args :initarg :pattern-args :reader pattern-args)))
7
+
3 8
 (eval-when (:compile-toplevel :load-toplevel :execute)
4 9
   (defgeneric handle-pattern (pattern form &rest args)
5 10
     (:method-combination append)
6
-    (:method append ((pattern cons) form &rest args)
11
+
12
+    (:method append ((pattern (eql 'cons)) form &rest args)
7 13
              (let ((val-sym (gensym "VAL")))
8 14
                (destructuring-bind (car cdr) args
9 15
                  `((,val-sym ,form)
10 16
                    (,car (car ,val-sym))
11 17
                    (,cdr (cdr ,val-sym))))))
12 18
 
13
-    (:method append ((pattern vector) form &rest args)
19
+    (:method append ((pattern (eql 'vector)) form &rest args)
14 20
              (let ((val-sym (gensym "VAL")))
15 21
                `((,val-sym ,form)
16 22
                  ,@ (loop for arg in args
17 23
                        for idx from 0
18 24
                        collect `(,arg (aref ,val-sym ,idx))))))
19 25
 
20
-    (:method append ((pattern hash-table) form &rest args)
26
+    (:method append ((pattern (eql 'hash-table)) form &rest args)
21 27
              (let* ((val-sym (gensym "VAL"))
22 28
                     (binding-forms (loop for (key sym) in args
23 29
                                       append `((,sym (gethash ',key ,val-sym))))))
... ...
@@ -25,11 +31,12 @@
25 31
                  ,@binding-forms)))
26 32
 
27 33
     (:method append ((pattern symbol) form &rest args)
28
-             (apply #'handle-pattern
29
-                    (closer-mop:class-prototype
30
-                     (find-class pattern))
31
-                    form
32
-                    args)))) 
34
+             (when (closer-mop:subclassp pattern 'standard-object)
35
+               (apply #'handle-pattern
36
+                      (closer-mop:class-prototype
37
+                       (find-class pattern))
38
+                      form
39
+                      args))))) 
33 40
 
34 41
 (defmacro let-pat* ((&rest clauses) &body body)
35 42
   `(let* (,@ (loop for ((discriminator . args) val-form) in clauses
... ...
@@ -37,3 +44,100 @@
37 44
      ,@body))
38 45
 
39 46
 
47
+(defpackage :patmatch/test
48
+  (:use :cl :should-test)
49
+  (:import-from :patmatch :let-pat*)
50
+  (:export ))
51
+(in-package :patmatch/test)
52
+
53
+
54
+(deftest let-pat*-handles-cons ()
55
+    ""
56
+  (should be eql
57
+          2
58
+          (let-pat* (((cons a b) '(2 . 3)))
59
+            (declare (ignore b))
60
+            a))
61
+  (should be eql
62
+          3
63
+          (let-pat* (((cons a b) '(2 . 3)))
64
+            (declare (ignore a))
65
+            b)))
66
+
67
+(deftest let-pat*-handles-vector ()
68
+    ""
69
+  (should be eql
70
+          2
71
+          (let-pat* (((vector a b) #(2 3)))
72
+            (declare (ignore b))
73
+            a))
74
+  (should be eql
75
+          3
76
+          (let-pat* (((vector a b) #(2 3)))
77
+            (declare (ignore a))
78
+            b)))
79
+
80
+(deftest let-pat*-handles-hash-table ()
81
+    ""
82
+  (should be eql
83
+          2
84
+          (let-pat* (((hash-table (:a a) (:b b)) (alexandria:plist-hash-table '(:a 2 :b 3))))
85
+            (declare (ignore b))
86
+            a))
87
+  (should be eql
88
+          3
89
+          (let-pat* (((hash-table (:a a) (:b b)) (alexandria:plist-hash-table '(:a 2 :b 3))))
90
+            (declare (ignore a))
91
+            b)))
92
+
93
+(defclass test-base ()
94
+  ((a :initform 1)))
95
+(defclass test-sub1 (test-base)
96
+  ())
97
+(defclass test-sub2 (test-base)
98
+  ((b :initform 2)))
99
+
100
+(defmethod patmatch:handle-pattern append ((pattern test-base) form &rest args)
101
+  (alexandria:when-let ((arg (getf args :a)))
102
+    (let ((val-sym (gensym "test-base-")))
103
+      `((,val-sym ,form)
104
+         ,@(serapeum:unsplice
105
+             `(,arg (slot-value ,val-sym 'a)))))))
106
+
107
+(defmethod patmatch:handle-pattern append ((pattern test-sub2) form &rest args)
108
+  (alexandria:when-let ((arg (getf args :b)))
109
+    (let ((val-sym (gensym "test-base-")))
110
+      `((,val-sym ,form)
111
+        ,@(serapeum:unsplice
112
+           `(,arg (slot-value ,val-sym 'b)))))))
113
+
114
+
115
+(deftest let-pat*-handles-object-destructuring ()
116
+    ""
117
+  (should be eql
118
+          1
119
+          (let-pat* (((test-base :a a) (make-instance 'test-base)))
120
+            a)))
121
+
122
+(deftest let-pat*-handles-inheritance ()
123
+    ""
124
+ (should be eql
125
+          1
126
+          (let-pat* (((test-base :a a) (make-instance 'test-sub1)))
127
+            a))
128
+
129
+ (should be eql
130
+          1
131
+          (let-pat* (((test-sub1 :a a) (make-instance 'test-sub1)))
132
+            a))
133
+
134
+ (should be eql
135
+         1
136
+         (let-pat* (((test-sub2 :a a) (make-instance 'test-sub2)))
137
+           a))
138
+
139
+ (should be equal
140
+         '(1 2)
141
+         (let-pat* (((test-sub2 :a a :b b) (make-instance 'test-sub2)))
142
+           (list a b))))
143
+