-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpackage.lisp
74 lines (65 loc) · 2.3 KB
/
package.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;;; Copyright (c) 2024 Bruno Dias
(defpackage #:cl-html5-query-selector
(:use #:cl)
(:export
#:query-selector))
(in-package :cl-html5-query-selector)
(defun match-selector (selector target)
(when target
(if (equal :and (car selector))
(reduce (lambda (acc selector)
(and acc (match-selector selector target)))
(cdr selector)
:initial-value t)
(destructuring-bind (kind . value)
selector
(case kind
(:tag-name
(string-equal value
(html5-parser:node-name target)))
(:id
(string-equal value
(html5-parser:element-attribute target "id")))
(:class
(let ((class-names (str:words (html5-parser:element-attribute target "class"))))
(member value class-names :test #'string-equal))))))))
(defun reduce-tree (fn tree &key initial-value)
(etypecase tree
(html5-parser::document
(reduce (lambda (acc tree) (reduce-tree fn tree :initial-value acc))
(html5-parser::%node-child-nodes tree)
:initial-value nil))
(html5-parser::element
(reduce (lambda (acc tree) (reduce-tree fn tree :initial-value acc))
(html5-parser::%node-child-nodes tree)
:initial-value (funcall fn initial-value tree)))
(html5-parser::text-node (funcall fn initial-value tree))
(t initial-value)))
(defun make-selector (selector)
(let ((cat (case (char selector 0) (#\. :class) (#\# :id) (t :tag-name))))
(cons cat
(if (equal cat :tag-name)
selector
(subseq selector 1)))))
(defun specialized-selector (name)
(let ((selectors (cl-ppcre:all-matches-as-strings "[\\#\\.]?([\\w\\-\\_0-9]+)" name)))
(reduce (lambda (acc selector)
(append acc (list (make-selector selector))))
(cdr selectors)
:initial-value (list (make-selector (car selectors))))))
(defun parse-selector (name)
(let ((groups (str:words name)))
(cons :and (reduce (lambda (acc selector)
(list* acc (specialized-selector selector)))
(cdr groups)
:initial-value (specialized-selector (car groups))))))
(defun query-selector (selector tree)
(let ((matcher (parse-selector selector)))
(reduce-tree (lambda (acc element)
(etypecase element
(html5-parser::text-node acc)
(t (progn
(when (match-selector matcher element)
(setf acc (append acc (list element))))
acc))))
tree)))