Make descendant combinators work with an implicit parent
Fri Dec 4 04:47:58 UTC 2009 pix@kepibu.org
* Make descendant combinators work with an implicit parent
diff -rN -u old-Oh, Ducks!/selectors.lisp new-Oh, Ducks!/selectors.lisp
--- old-Oh, Ducks!/selectors.lisp 2013-07-24 10:32:44.000000000 +0000
+++ new-Oh, Ducks!/selectors.lisp 2013-07-24 10:32:44.000000000 +0000
@@ -1,5 +1,8 @@
(in-package #:oh-ducks)
+(defvar *effective-root* nil
+ "The element to be considered as the root element during unification. Is the implicit element to be matched by combinators without a leading qualifier. E.g., \"> a\" will match <a> tags directly under *effective-root*.")
+
#.(set-dispatch-macro-character #\# #\T 'unify::|sharp-T-reader|)
(defclass selector (unify::string-template)
@@ -49,17 +52,23 @@
<http://common-lisp.net/pipermail/cl-unification-devel/attachments/20091201/d5021e15/attachment.obj> ~
to ensure proper functioning of the \"Oh, Ducks!\" library.")
+(defclass %root-selector (simple-selector) ())
+(defparameter %root-selector (make-instance '%root-selector))
+
+(defmethod print-object ((selector %root-selector) stream)
+ (print-unreadable-object (selector stream :type t)))
+
(defun parse-selector (selector)
(match-case (selector)
;; combinators
(#T(regexp$ "[ ]*[~][ ]*" ())
- (list (make-instance 'sibling-combinator :matcher (parse-selector &rest))))
+ (list (make-instance 'sibling-combinator :matcher (or (parse-selector &rest) %root-selector))))
(#T(regexp$ "[ ]*[+][ ]*" ())
- (list (make-instance 'adjacent-combinator :matcher (parse-selector &rest))))
+ (list (make-instance 'adjacent-combinator :matcher (or (parse-selector &rest) %root-selector))))
(#T(regexp$ "[ ]*[>][ ]*" ())
- (list (make-instance 'child-combinator :matcher (parse-selector &rest))))
+ (list (make-instance 'child-combinator :matcher (or (parse-selector &rest) %root-selector))))
(#T(regexp$ "[ ]+" ())
- (list (make-instance 'descendant-combinator :matcher (parse-selector &rest))))
+ (list (make-instance 'descendant-combinator :matcher (or (parse-selector &rest) %root-selector))))
;; simple selectors
;; cyclic (An+B, n+B)
(#T(regexp$ ":nth-child\\([ ]*([+-]?)([0-9]+)?n[ ]*([+-])[ ]*([0-9]+)?[ ]*\\)" (?asign ?a ?bsign ?b))
@@ -138,6 +147,9 @@
(declare (ignore element selector))
t)
+(defmethod element-matches-p (element (selector %root-selector))
+ (eq element *effective-root*))
+
(defmethod element-matches-p (element (selector list))
(every (curry #'element-matches-p element) selector))
diff -rN -u old-Oh, Ducks!/tests.lisp new-Oh, Ducks!/tests.lisp
--- old-Oh, Ducks!/tests.lisp 2013-07-24 10:32:44.000000000 +0000
+++ new-Oh, Ducks!/tests.lisp 2013-07-24 10:32:44.000000000 +0000
@@ -69,6 +69,26 @@
"<div>I <i>really</i> <b>like</b> cheese. Do you not <i>dislike</i> cheese?</div>")
(values i))
+;; Sometimes, you want to match a thing inside a thing, in which case
+;; combinators should implicitly assume an unspecified right side means
+;; "whatever element I gave you".
+(match (#T(html (:model dom)
+ ("q" . ?q))
+ "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i></div>")
+ (match (#t(html ("> i" . ?i))
+ (first q))
+ i))
+
+;; Note, however, that searches are strictly recursive. So a sibling
+;; combinator won't match.
+;; FIXME: should it?
+(match (#T(html (:model dom)
+ ("q" . ?q))
+ "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i></div>")
+ (match (#t(html ("+ i" . ?i))
+ (first q))
+ i))
+
#+LATER?
(match (#t(html ("div::content" . #t(regexp+ "^f(o+)" (?o))))
diff -rN -u old-Oh, Ducks!/unify.lisp new-Oh, Ducks!/unify.lisp
--- old-Oh, Ducks!/unify.lisp 2013-07-24 10:32:44.000000000 +0000
+++ new-Oh, Ducks!/unify.lisp 2013-07-24 10:32:44.000000000 +0000
@@ -18,7 +18,8 @@
(css-selector-template
(unify template document env))
(t
- (let ((val (find-matching-elements css-specifier document)))
+ (let* ((*effective-root* document)
+ (val (find-matching-elements css-specifier document)))
(cond
((null val)
(error 'unification-failure