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-11-16 12:29:11.000000000 +0000 +++ new-Oh, Ducks!/selectors.lisp 2013-11-16 12:29:12.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 tags directly under *effective-root*.") + #.(set-dispatch-macro-character #\# #\T 'unify::|sharp-T-reader|) (defclass selector (unify::string-template) @@ -49,17 +52,23 @@ ~ 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-11-16 12:29:11.000000000 +0000 +++ new-Oh, Ducks!/tests.lisp 2013-11-16 12:29:12.000000000 +0000 @@ -69,6 +69,26 @@ "
I really like cheese. Do you not dislike cheese?
") (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)) + "
ham foo bar baz quuz spam
") + (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)) + "
ham foo bar baz quuz spam
") + (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-11-16 12:29:11.000000000 +0000 +++ new-Oh, Ducks!/unify.lisp 2013-11-16 12:29:12.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