implicit-element is a better name than root
Annotate for file selectors.lisp
2009-11-18 pix 1 (in-package #:oh-ducks)
2009-11-15 pix 2
2009-12-05 pix 3 (defvar *implicit-element* nil
07:18:05 ' 4 "The element to be considered as an implicit element to be matched by combinators without a leading qualifier. E.g., \"> a\" will match <a> tags directly under *implicit-element*, and \"+ a\" will match <a> tags directly following *implicit-element*.")
2009-12-04 pix 5
2009-11-15 pix 6 #.(set-dispatch-macro-character #\# #\T 'unify::|sharp-T-reader|)
14:25:29 ' 7
' 8 (defclass selector (unify::string-template)
' 9 ((matcher :reader matcher :initarg :matcher)))
' 10
' 11 (defgeneric selector-p (object)
' 12 (:method ((ob selector)) t)
' 13 (:method ((ob t)) nil))
' 14
2009-11-16 pix 15 (defclass simple-selector (selector)
2009-11-15 pix 16 ((arg :reader selector-arg :initarg :arg)))
14:25:29 ' 17
' 18 (defmethod print-object ((selector simple-selector) stream)
' 19 (format stream "#<selector ~s>" (selector-arg selector)))
' 20
' 21 (defclass combinator (selector) ())
2009-11-16 pix 22
08:14:42 ' 23 (defgeneric combinator-p (object)
' 24 (:method ((ob combinator)) t)
' 25 (:method ((ob t)) nil))
' 26
' 27 (defmethod print-object ((selector combinator) stream)
2009-11-21 pix 28 (format stream "#<~s ~s>" (class-name (class-of selector)) (matcher selector)))
2009-11-16 pix 29
2009-11-15 pix 30 (defclass child-combinator (combinator) ())
14:25:29 ' 31 (defclass descendant-combinator (combinator) ())
' 32 (defclass adjacent-combinator (combinator) ())
' 33 (defclass sibling-combinator (combinator) ())
' 34
2009-11-16 pix 35 (defclass universal-selector (simple-selector) ())
2009-11-15 pix 36 (defclass type-selector (simple-selector) ())
14:25:29 ' 37 (defclass id-selector (simple-selector) ())
' 38 (defclass class-selector (simple-selector) ())
2009-11-23 pix 39 (defclass nth-child-selector (simple-selector) ())
2009-11-30 pix 40 (defclass nth-last-child-selector (nth-child-selector) ())
2009-11-23 pix 41
11:33:15 ' 42 (defmethod print-object ((selector universal-selector) stream)
' 43 (format stream "#<universal-selector>"))
2009-11-15 pix 44
2009-11-16 pix 45 (defmethod initialize-instance :after ((template combinator) &key)
2009-11-15 pix 46 (unless (slot-boundp template 'matcher)
14:25:29 ' 47 (let ((selector (template-spec template)))
' 48 (setf (slot-value template 'matcher) (parse-selector (string-trim " " selector))))))
' 49
2010-01-01 pix 50 (warn "parse-selector currently relies on a patch which has not yet made ~
05:06:19 ' 51 it in to cl-unification. Be sure to apply the patch from ~
' 52 <http://common-lisp.net/pipermail/cl-unification-devel/attachments/20091201/d5021e15/attachment.obj> ~
' 53 to ensure proper functioning of the \"Oh, Ducks!\" library.")
' 54
2009-12-05 pix 55 (defclass %implicit-element-selector (selector) ())
07:18:05 ' 56 (defparameter %implicit-element-selector (make-instance '%implicit-element-selector))
2009-12-04 pix 57
2009-12-05 pix 58 (defmethod print-object ((selector %implicit-element-selector) stream)
2009-12-04 pix 59 (print-unreadable-object (selector stream :type t)))
04:47:58 ' 60
2009-11-15 pix 61 (defun parse-selector (selector)
14:25:29 ' 62 (match-case (selector)
' 63 ;; combinators
2010-01-04 pix 64 (#T(regexp$ "[ ]*[~][ ]*" ())
2009-12-05 pix 65 (list (make-instance 'sibling-combinator :matcher (or (parse-selector &rest) %implicit-element-selector))))
2010-01-04 pix 66 (#T(regexp$ "[ ]*[+][ ]*" ())
2009-12-05 pix 67 (list (make-instance 'adjacent-combinator :matcher (or (parse-selector &rest) %implicit-element-selector))))
2010-01-04 pix 68 (#T(regexp$ "[ ]*[>][ ]*" ())
2009-12-05 pix 69 (list (make-instance 'child-combinator :matcher (or (parse-selector &rest) %implicit-element-selector))))
2010-01-04 pix 70 (#T(regexp$ "[ ]+" ())
2009-12-05 pix 71 (list (make-instance 'descendant-combinator :matcher (or (parse-selector &rest) %implicit-element-selector))))
2009-11-23 pix 72 ;; simple selectors
2009-11-30 pix 73 ;; cyclic (An+B, n+B)
2010-01-04 pix 74 (#T(regexp$ ":nth-child\\([ ]*([+-]?)([0-9]+)?n[ ]*([+-])[ ]*([0-9]+)?[ ]*\\)" (?asign ?a ?bsign ?b))
2009-11-30 pix 75 (cons (make-instance 'nth-child-selector
2010-01-04 pix 76 :arg (cons (funcall (if (string= "-" asign) #'- #'+)
05:59:48 ' 77 (if (stringp a) (parse-integer a) 1))
' 78 (funcall (if (string= "-" bsign) #'- #'+)
' 79 (if (stringp b) (parse-integer b) 0))))
2009-11-30 pix 80 (parse-selector &rest)))
04:48:22 ' 81 ;; absolute (B)
2010-01-04 pix 82 (#T(regexp$ ":nth-child\\([ ]*([+-]?[0-9]+)[ ]*\\)" (?b))
05:59:48 ' 83 (cons (make-instance 'nth-child-selector :arg (cons 0 (parse-integer b))) (parse-selector &rest)))
2009-11-30 pix 84 ;; named (odd, even)
2010-01-04 pix 85 (#T(regexp$ ":nth-child\\([ ]*(odd|even)[ ]*\\)" (?which))
05:59:48 ' 86 (cons (make-instance 'nth-child-selector :arg (cons 2 (if (string-equal "odd" which) 1 0)))
2009-11-30 pix 87 (parse-selector &rest)))
2010-01-04 pix 88 (#T(regexp$ ":first-child" ())
05:59:48 ' 89 (cons (make-instance 'nth-child-selector :arg (cons 0 1)) (parse-selector &rest)))
' 90 (#T(regexp$ "[#](\\w+)" (?id))
2009-11-23 pix 91 (cons (make-instance 'id-selector :arg id) (parse-selector &rest)))
2010-01-04 pix 92 (#T(regexp$ "[\\.](\\w+)" (?class))
2009-11-23 pix 93 (cons (make-instance 'class-selector :arg class) (parse-selector &rest)))
2010-01-04 pix 94 (#T(regexp$ "(\\w+)" (?type))
2009-11-23 pix 95 (cons (make-instance 'type-selector :arg type) (parse-selector &rest)))
2010-01-04 pix 96 (#T(regexp$ "\\*" ())
2009-11-23 pix 97 (cons (make-instance 'universal-selector) (parse-selector &rest)))
13:14:00 ' 98 (t (unless (string= selector "")
' 99 (error "Unable to to parse selector: ~s" selector)))))
2009-11-15 pix 100
2010-01-04 pix 101 ;; Hrm... would something like this make things more or less clear?
05:59:48 ' 102 ;#t(lex$ (":nth-child(" :s? (?a :int) "n" :s? (or #\+ #\-) :s? (?b :int) :s? ")"))
' 103 ;#t(lex$ ("#" (?id :identifier)))
' 104 ;#t(lex$ (?type :identifier))
' 105
2009-12-05 pix 106 (defun find-matching-elements-in-list (selector element-list)
07:18:05 ' 107 (reduce #'nconc
' 108 (mapcar (curry #'find-matching-elements selector)
' 109 element-list)))
' 110
2010-01-04 pix 111 (defgeneric find-matching-elements (selector element)
2010-01-04 pix 112 (:method (selector (element t))
01:03:10 ' 113 (flet ((find-in-list (elements)
2010-01-04 pix 114 (mapcar (curry #'find-matching-elements selector)
2010-01-04 pix 115 elements)))
01:03:10 ' 116 (nconc
' 117 (when (element-matches-p element selector) (list element))
' 118 (reduce #'nconc
' 119 (find-in-list (element-children element)))))))
2009-11-15 pix 120
2010-01-04 pix 121 (defgeneric element-matches-p (element selector))
2009-11-15 pix 122
2010-01-04 pix 123 (defmethod element-matches-p (element (selector type-selector))
2009-11-15 pix 124 (element-type-equal element (selector-arg selector)))
14:25:29 ' 125
2010-01-04 pix 126 (defmethod element-matches-p (element (selector id-selector))
2009-11-15 pix 127 (string= (element-id element) (selector-arg selector)))
14:25:29 ' 128
2010-01-04 pix 129 (defmethod element-matches-p (element (selector nth-child-selector))
2010-01-04 pix 130 (when-let* ((parent (element-parent element))
05:59:48 ' 131 (pos (position element (funcall (typecase selector
' 132 (nth-last-child-selector #'reverse)
' 133 (nth-child-selector #'identity))
' 134 (element-children parent)) :test #'eq)))
' 135 (let ((pos (1+ pos))
' 136 (a (car (selector-arg selector)))
' 137 (b (cdr (selector-arg selector))))
' 138 ;; pos = An + B
' 139 (cond
' 140 ;; pos = 0n + B
' 141 ((= 0 a) (= b pos))
' 142 ;; (pos - B)/A = n
' 143 (t (and (zerop (mod (- pos b) a))
' 144 (not (minusp (/ (- pos b) a)))))))))
2009-11-23 pix 145
2010-01-04 pix 146 (defmethod element-matches-p (element (selector class-selector))
2009-11-15 pix 147 (member (selector-arg selector)
2010-01-04 pix 148 (element-classes element)
05:59:48 ' 149 :test #'string=))
2009-11-15 pix 150
2010-01-04 pix 151 (defmethod element-matches-p (element (selector universal-selector))
2009-12-03 pix 152 (declare (ignore element selector))
2009-11-19 pix 153 t)
06:25:36 ' 154
2009-12-05 pix 155 (defmethod element-matches-p (element (selector %implicit-element-selector))
07:18:05 ' 156 (eq element *implicit-element*))
2009-12-04 pix 157
2010-01-04 pix 158 (defmethod element-matches-p (element (selector list))
01:04:12 ' 159 (every (curry #'element-matches-p element) selector))
2009-11-16 pix 160
2010-01-04 pix 161 (defmethod element-matches-p (element (selector child-combinator))
01:04:12 ' 162 (element-matches-p (element-parent element) (matcher selector)))
2009-11-16 pix 163
2010-01-04 pix 164 (defmethod element-matches-p (element (selector descendant-combinator))
01:04:12 ' 165 (some (rcurry #'element-matches-p (matcher selector)) (element-ancestors element)))
2009-11-19 pix 166
2010-01-04 pix 167 (defmethod element-matches-p (element (selector adjacent-combinator))
2009-12-03 pix 168 (let* ((parent (element-parent element))
00:12:02 ' 169 (siblings (element-children parent))
' 170 (ourpos (position element siblings :test #'eq)))
' 171 (and ourpos
' 172 (> ourpos 0)
2010-01-04 pix 173 (element-matches-p (elt siblings (1- ourpos)) (matcher selector)))))
2009-11-19 pix 174
2010-01-04 pix 175 (defmethod element-matches-p (element (selector sibling-combinator))
2009-12-03 pix 176 (let* ((parent (element-parent element))
00:12:02 ' 177 (siblings (element-children parent))
' 178 (ourpos (position element siblings :test #'eq)))
' 179 (and ourpos
' 180 (> ourpos 0)
2010-01-04 pix 181 (find-if (rcurry #'element-matches-p (matcher selector)) siblings :end ourpos))))
2009-12-05 pix 182
07:18:05 ' 183 ;; Hello excessively long name
' 184 (defun terminating-implicit-sibling-combinator-p (selector)
' 185 (typecase selector
' 186 ((or sibling-combinator adjacent-combinator)
' 187 (typecase (matcher selector)
' 188 (%implicit-element-selector t)
' 189 (list (terminating-implicit-sibling-combinator-p (car (last (matcher selector)))))))
' 190 (combinator (terminating-implicit-sibling-combinator-p (matcher selector)))
' 191 (selector nil)
' 192 (null nil)
' 193 (list (terminating-implicit-sibling-combinator-p (car (last selector))))
' 194 (t nil)))