Skip to content

Commit 3f1dec5

Browse files
committed
Added non-greedy everything/repetition.
1 parent 8d70b85 commit 3f1dec5

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

Diff for: xml-match-tests.lisp

+22
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
(collect (cons name
7171
(labels ((convert-value (v)
7272
(etypecase v
73+
(null "")
7374
(string v)
7475
(stp:element
7576
(with-output-to-string (out)
@@ -250,6 +251,27 @@
250251
(verify-match '(:div (:+ (:img :src ?src)))
251252
"<div><img src='a.jpg'/><img src='b.jpg'/><img src='c.jpg'/></div>"
252253
'((?src "a.jpg" "b.jpg" "c.jpg")))
254+
;; non-greedy 'everything' / repetition
255+
(verify-match '(:div (:+bind :**? ?x) (:+bind (:+ (:p *)) ?y))
256+
"<div>abc<p>q</p><p>r</p><p>s</p></div>"
257+
'((?y . "<p>q</p><p>r</p><p>s</p>")
258+
(?x . "abc")))
259+
(verify-match '(:div (:+bind (:*? (:p *)) ?x) (:+bind (:+ (:p :class "z" *)) ?y))
260+
"<div><p>a</p><p>q</p><p class='z'>r</p><p class='z'>s</p></div>"
261+
'((?y . "<p class=\"z\">r</p><p class=\"z\">s</p>")
262+
(?x . "<p>a</p><p>q</p>")))
263+
(verify-match '(:div (:+bind (:*? (:p *)) ?x) (:+bind (:+ (:p :class "z" *)) ?y))
264+
"<div><p class='z'>q</p><p class='z'>r</p><p class='z'>s</p></div>"
265+
'((?y . "<p class=\"z\">q</p><p class=\"z\">r</p><p class=\"z\">s</p>")
266+
(?x . "")))
267+
(verify-match '(:div (:+bind (:+? (:p *)) ?x) (:+bind (:+ (:p :class "z" *)) ?y))
268+
"<div><p>a</p><p>q</p><p class='z'>r</p><p class='z'>s</p></div>"
269+
'((?y . "<p class=\"z\">r</p><p class=\"z\">s</p>")
270+
(?x . "<p>a</p><p>q</p>")))
271+
(verify-match '(:div (:+bind (:+? (:p *)) ?x) (:+bind (:+ (:p :class "z" *)) ?y))
272+
"<div><p class='z'>q</p><p class='z'>r</p><p class='z'>s</p></div>"
273+
'((?y . "<p class=\"z\">r</p><p class=\"z\">s</p>")
274+
(?x . "<p class=\"z\">q</p>")))
253275
;; if the same variable is used both inside and outside repetition,
254276
;; the value used outside must occur among values produced by the binding
255277
;; inside repetition

Diff for: xml-match.lisp

+52
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,12 @@
132132
((and (symbolp pattern) (aliased-pattern pattern))
133133
(parse-sub (aliased-pattern pattern)))
134134
((eq pattern '*) (make-instance 'everything))
135+
((eq pattern :**?) (make-instance 'non-greedy-everything))
135136
((stringp pattern) (make-instance 'text :text pattern))
136137
((and (proper-list-p pattern)
137138
(symbolp (first pattern))
138139
(not (eq (first pattern) '*))
140+
(not (eq (first pattern) :**?))
139141
(not (variable-p (first pattern)))
140142
(not (aliased-pattern (first pattern))))
141143
(case (first pattern)
@@ -166,9 +168,15 @@
166168
(:*
167169
(make-instance 'greedy-repetition
168170
:content-pattern (parse-sub (rest pattern))))
171+
(:*?
172+
(make-instance 'non-greedy-repetition
173+
:content-pattern (parse-sub (rest pattern))))
169174
(:+
170175
(make-instance 'greedy-repetition-no-zero
171176
:content-pattern (parse-sub (rest pattern))))
177+
(:+?
178+
(make-instance 'non-greedy-repetition-no-zero
179+
:content-pattern (parse-sub (rest pattern))))
172180
((:+rx :+erx)
173181
(unless (and (stringp (second pattern))
174182
(every #'variable-p (cddr pattern)))
@@ -417,6 +425,14 @@
417425
(or (funcall next-fn nil bindings)
418426
(funcall next-fn node bindings)))))
419427

428+
(defclass non-greedy-everything (pattern) ())
429+
430+
(defmethod create-matcher ((pattern non-greedy-everything) next-fn)
431+
(matcher-closure pattern everything (node bindings)
432+
(iter (for from-node initially node then (next-sibling from-node))
433+
(while from-node)
434+
(thereis (funcall next-fn from-node bindings)))))
435+
420436
(defclass bind (compound-pattern)
421437
((variable :accessor variable-of :initarg :variable)))
422438

@@ -565,6 +581,24 @@
565581
(*rep-saved-bindings* bindings))
566582
(funcall inner-closure node bindings)))))
567583

584+
(defclass non-greedy-repetition (compound-pattern) ())
585+
586+
(defmethod create-matcher ((pattern non-greedy-repetition) next-fn)
587+
(let* ((content-matcher nil)
588+
(inner-closure
589+
(matcher-closure pattern non-greedy-repetition-inner (node bindings)
590+
(setf *rep-accumulated-bindings*
591+
(collect-added-bindings bindings *rep-saved-bindings*
592+
*rep-accumulated-bindings*))
593+
(or (funcall next-fn node (append *rep-accumulated-bindings*
594+
*rep-saved-bindings*))
595+
(funcall content-matcher node *rep-saved-bindings*)))))
596+
(setf content-matcher (create-matcher (content-pattern-of pattern) inner-closure))
597+
(matcher-closure pattern greedy-repetition (node bindings)
598+
(let ((*rep-accumulated-bindings* '())
599+
(*rep-saved-bindings* bindings))
600+
(funcall inner-closure node bindings)))))
601+
568602
(defclass greedy-repetition-no-zero (compound-pattern) ())
569603

570604
(defmethod create-matcher ((pattern greedy-repetition-no-zero) next-fn)
@@ -583,6 +617,24 @@
583617
(*rep-saved-bindings* bindings))
584618
(funcall content-matcher node bindings)))))
585619

620+
(defclass non-greedy-repetition-no-zero (compound-pattern) ())
621+
622+
(defmethod create-matcher ((pattern non-greedy-repetition-no-zero) next-fn)
623+
(let (content-matcher)
624+
(setf content-matcher
625+
(create-matcher (content-pattern-of pattern)
626+
(matcher-closure pattern repetition (node bindings)
627+
(setf *rep-accumulated-bindings*
628+
(collect-added-bindings bindings *rep-saved-bindings*
629+
*rep-accumulated-bindings*))
630+
(or (funcall next-fn node (append *rep-accumulated-bindings*
631+
*rep-saved-bindings*))
632+
(funcall content-matcher node *rep-saved-bindings*)))))
633+
(matcher-closure pattern greedy-repetition (node bindings)
634+
(let ((*rep-accumulated-bindings* '())
635+
(*rep-saved-bindings* bindings))
636+
(funcall content-matcher node bindings)))))
637+
586638
(defclass deep (compound-pattern) ())
587639

588640
(defmethod create-matcher ((pattern deep) next-fn)

0 commit comments

Comments
 (0)