Christophe Weblog Wiki Code Publications Music
initial import of pattern-specializer system
[specializable.git] / src / pattern-specializer / optima-extensions.lisp
diff --git a/src/pattern-specializer/optima-extensions.lisp b/src/pattern-specializer/optima-extensions.lisp
new file mode 100644 (file)
index 0000000..644194a
--- /dev/null
@@ -0,0 +1,254 @@
+;;;; optima-extensions.lisp --- Necessary extensions of the optima library.
+;;;;
+;;;; Copyright (C) 2014 Jan Moringen
+;;;;
+;;;; Author: Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
+
+(cl:in-package #:pattern-specializer)
+
+;;; Protocol
+
+(defgeneric pattern-more-specific-p (pattern1 pattern2)
+  (:documentation
+   "Return true if PATTERN1 is strictly more specific than
+    PATTERN2.
+
+    General principles:
+
+    * Constant pattern are more specific than all other patterns
+
+    * Variable patterns are less specific than all other patterns
+
+    * For most complex patterns, subpatterns are compared
+      lexicographically. Exceptions:
+
+      * For `class-pattern' s, subclass relations have higher
+        precedence. The above rule applies only when the classes are
+        identical.
+
+      * `and-pattern's are comparable to all patterns by checking
+        whether some of their subpatterns are more specific than the
+        pattern in question.
+
+      * `or-pattern's are similar."))
+
+(defun subpatterns-unrestricted-p (pattern)
+  (every (of-type 'optima.core:variable-pattern)
+         (optima.core:complex-pattern-subpatterns pattern)))
+
+;;; Implementation
+
+(defmethod pattern-more-specific-p :around ((pattern1 optima::pattern)
+                                            (pattern2 optima::pattern))
+  (if (eq pattern1 pattern2)
+      '=
+      (call-next-method)))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima::pattern))
+  '/=)
+
+;; `constant-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:constant-pattern)
+                                    (pattern2 optima::pattern))
+  (if (typep pattern2 'optima.core:complex-pattern)
+      (call-next-method)
+      '<))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima.core:constant-pattern))
+  (if (typep pattern1 'optima.core:complex-pattern)
+      (call-next-method)
+      '>))
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:constant-pattern)
+                                    (pattern2 optima.core:constant-pattern))
+  (if (equal (optima.core:constant-pattern-value pattern1)
+             (optima.core:constant-pattern-value pattern2))
+      '=
+      '/=))
+
+;; `variable-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:variable-pattern)
+                                    (pattern2 optima::pattern))
+  (if (typep pattern2 '(or optima.core:or-pattern optima.core:and-pattern))
+      (call-next-method)
+      '>))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima.core:variable-pattern))
+  (if (typep pattern1 '(or optima.core:or-pattern optima.core:and-pattern))
+      (call-next-method)
+      '<))
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:variable-pattern)
+                                    (pattern2 optima.core:variable-pattern))
+  '=)
+
+;;; `guard-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:guard-pattern)
+                                    (pattern2 optima::pattern))
+  (if (typep pattern2 '(or optima.core:or-pattern optima.core:and-pattern)) ; TODO not-pattern
+      (call-next-method)
+      '<))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima.core:guard-pattern))
+  (if (typep pattern1 '(or optima.core:or-pattern optima.core:and-pattern))
+      (call-next-method)
+      '>))
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:guard-pattern)
+                                    (pattern2 optima.core:guard-pattern))
+  (if (equal (optima.core:guard-pattern-test-form pattern1) ; TODO not enough because of variable names; encode variables with TODO numbers
+             (optima.core:guard-pattern-test-form pattern2))
+      (pattern-more-specific-p
+       (optima.core:guard-pattern-subpattern pattern1)
+       (optima.core:guard-pattern-subpattern pattern2))
+      '/=))
+
+;; `and-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:and-pattern)
+                                    (pattern2 optima::pattern))
+  (if (typep pattern2 'optima.core:and-pattern)
+      (call-next-method)
+      (let ((result (pattern-more-specific-p pattern2 pattern1)))
+        (case result
+          (< '>)
+          (> '<)
+          (t result)))))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima.core:and-pattern))
+  (reduce (lambda (result subpattern)
+            (case (pattern-more-specific-p pattern1 subpattern)
+              (<  (case result
+                    ((nil <) '<)
+                    (=       '=)
+                    (t       '/=)))
+              (>  (case result
+                    ((nil > =) '>)
+                    (t         '/=)))
+              (=  (case result
+                    ((nil < =) '=)
+                    (>         '>)
+                    (t         '/=)))
+              (t '/=)))
+          (optima.core:complex-pattern-subpatterns pattern2)
+          :initial-value nil))
+
+;; `or-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:or-pattern)
+                                    (pattern2 optima::pattern))
+  (if (typep pattern2 'optima.core:or-pattern)
+      (call-next-method)
+      (let ((result (pattern-more-specific-p pattern2 pattern1)))
+        (case result
+          (< '>)
+          (> '<)
+          (t result)))))
+
+(defmethod pattern-more-specific-p ((pattern1 optima::pattern)
+                                    (pattern2 optima.core:or-pattern))
+  (reduce (lambda (result subpattern)
+            (case (pattern-more-specific-p pattern1 subpattern)
+              (<  '<)
+              (>  (case result
+                    ((nil >) '>)
+                    (t       result)))
+              (=  (case result
+                    ((nil = >) '=)
+                    (t         result)))
+              (/= (case result
+                    ((nil) '/=)
+                    (=     '<)
+                    (t     result)))))
+          (optima.core:complex-pattern-subpatterns pattern2)
+          :initial-value nil))
+
+;; `cons-pattern'
+
+; TODO do this in a generic way via optima.core:complex-pattern-subpatterns
+(defmethod pattern-more-specific-p ((pattern1 optima.core:cons-pattern)
+                                    (pattern2 optima.core:cons-pattern))
+  (let* ((car1 (optima.core:cons-pattern-car-pattern pattern1))
+         (cdr1 (optima.core:cons-pattern-cdr-pattern pattern1))
+         (car2 (optima.core:cons-pattern-car-pattern pattern2))
+         (cdr2 (optima.core:cons-pattern-cdr-pattern pattern2))
+         (result/car (pattern-more-specific-p car1 car2))
+         (result/cdr (pattern-more-specific-p cdr1 cdr2)))
+    (cond
+      ((and (eq result/cdr '=) (eq result/car '=))
+       '=)
+      ((and (eq result/car '<) (member result/cdr '(< =)))
+       '<)
+      ((and (eq result/cdr '<) (member result/car '(< =)))
+       '<)
+      ((and (eq result/car '>) (member result/cdr '(> =)))
+       '>)
+      ((and (eq result/cdr '>) (member result/car '(> =)))
+       '>)
+      (t
+       '/=))))
+
+;; `class-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:class-pattern)
+                                    (pattern2 optima.core:class-pattern))
+  (let* ((class1         (optima.core:class-pattern-class-name pattern1))
+         (slots1         (optima.core:class-pattern-slot-names pattern1))
+         (subpatterns1   (optima.core:class-pattern-subpatterns pattern1))
+         (class2         (optima.core:class-pattern-class-name pattern2))
+         (slots2         (optima.core:class-pattern-slot-names pattern2))
+         (subpatterns2   (optima.core:class-pattern-subpatterns pattern2))
+         (fewer-slots1-p (set-difference slots2 slots1))
+         (fewer-slots2-p (set-difference slots1 slots2)))
+    (labels ((lookup (slot)
+               (when-let ((position (position slot slots2)))
+                 (nth position subpatterns2)))
+             (compare-slots (initial)
+               ;; TODO alternate idea: iterate over (union slots1 slots2); use lookup1 and lookup2 leading to :missing1 and :missing2
+               (reduce (lambda (result slot1-and-subpattern1)
+                         (destructuring-bind (slot1 . subpattern1) slot1-and-subpattern1
+                           (case (if-let ((subpattern2 (lookup slot1)))
+                                   (pattern-more-specific-p subpattern1 subpattern2)
+                                   :missing)
+                             ((< :missing) (case result
+                                             ((nil < =) '<)
+                                             (t         '/=)))
+                             (>            (case result
+                                             ((nil > =) '>)
+                                             (t         '/=)))
+                             (=            result)
+                             (t            '/=))))
+                       (mapcar #'cons slots1 subpatterns1)
+                       :initial-value initial)))
+      (multiple-value-bind (result1 certain1-p) (subtypep class1 class2)
+        (multiple-value-bind (result2 certain2-p) (subtypep class2 class1)
+          (assert (and certain1-p certain2-p))
+          (cond
+            ((and result1 result2)
+             (compare-slots (if fewer-slots1-p '> '=)))
+            (result1
+             (cond
+               (fewer-slots1-p '/=)
+               (fewer-slots2-p (compare-slots '<))
+               (t              (compare-slots '<))))
+            (result2
+             (cond
+               (fewer-slots2-p '/=)
+               (fewer-slots1-p (compare-slots '>))
+               (t              (compare-slots '>))))
+            (t
+             '/=)))))))
+
+;; `structure-pattern'
+
+(defmethod pattern-more-specific-p ((pattern1 optima.core:structure-pattern)
+                                    (pattern2 optima.core:structure-pattern))
+  (error "not implemented"))