[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/xr e5b51bf 01/10: Add wrapped subsumption in repeated f
From: |
Mattias Engdegård |
Subject: |
[elpa] externals/xr e5b51bf 01/10: Add wrapped subsumption in repeated forms |
Date: |
Sun, 3 May 2020 11:13:07 -0400 (EDT) |
branch: externals/xr
commit e5b51bf5608720dddac10495950258a9cd07a178
Author: Mattias Engdegård <address@hidden>
Commit: Mattias Engdegård <address@hidden>
Add wrapped subsumption in repeated forms
This check finds regexps like "\\(?:a*c[ab]*\\)+", where
the first and last item in a repeated sequence are considered
adjacent.
---
xr-test.el | 13 ++++++++++++
xr.el | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/xr-test.el b/xr-test.el
index 432b8af..2001a39 100644
--- a/xr-test.el
+++ b/xr-test.el
@@ -592,6 +592,19 @@
(46 . "Repetition subsumed by preceding repetition"))))
))
+(ert-deftest xr-lint-wrapped-subsumption ()
+ (let ((text-quoting-style 'grave))
+ (should (equal
+ (xr-lint "\\(?:a*x[ab]+\\)*")
+ '((14 . "Last item in repetition subsumes first item
(wrapped)"))))
+ (should (equal
+ (xr-lint "\\([ab]*xya?\\)+")
+ '((13 . "First item in repetition subsumes last item
(wrapped)"))))
+ (should (equal
+ (xr-lint "\\(?3:a*xa*\\)\\{7\\}")
+ '((14 . "First item in repetition subsumes last item
(wrapped)"))))
+ ))
+
(ert-deftest xr-skip-set ()
(should (equal (xr-skip-set "0-9a-fA-F+*")
'(any "0-9a-fA-F" "+*")))
diff --git a/xr.el b/xr.el
index 5e7a11f..985f4d8 100644
--- a/xr.el
+++ b/xr.el
@@ -439,6 +439,65 @@ UPPER may be nil, meaning infinity."
(cl-every #'xr--matches-empty-p body))
("" t)))
+(defun xr--adjacent-subsumption (a b)
+ "Check if A subsumes B, or vice versa, or not, assuming they are adjacent.
+Return `a-subsumes-b', `b-subsumes-a' or nil."
+ ;; Check for subsuming repetitions in sequence: (Ra A) (Rb B)
+ ;; where Ra and Rb are repetition operators, and A and B are operands.
+ ;; We conclude that (Ra A) subsumes (Rb B), in the sense that the
+ ;; sequence is equivalent to just (Ra A), if:
+ ;; A matches a superset of B
+ ;; and Ra can match infinitely many times
+ ;; and Rb can match zero times
+ ;; and Rb is non-greedy if Ra is non-greedy.
+ ;; Example: [cd]+c?
+ (let ((a-expr (and (consp a)
+ (memq (car a)
+ '(zero-or-more one-or-more opt *? +? ??))
+ (xr--make-seq (cdr a)))))
+ (when a-expr
+ (let ((b-expr (and (consp b)
+ (memq (car b)
+ '(zero-or-more one-or-more opt *? +? ??))
+ (xr--make-seq (cdr b)))))
+ (when b-expr
+ (let ((a-op (car a))
+ (b-op (car b)))
+ ;; Test the same condition twice, but mirrored.
+ (cond
+ ((and (memq b-op '(zero-or-more opt *? ??))
+ (memq a-op '(zero-or-more one-or-more *? +?))
+ (not (and (memq a-op '(*? +?))
+ (memq b-op '(zero-or-more opt))))
+ (xr--superset-p a-expr b-expr))
+ 'a-subsumes-b)
+ ((and (memq a-op '(zero-or-more opt *? ??))
+ (memq b-op '(zero-or-more one-or-more *? +?))
+ (not (and (memq b-op '(*? +?))
+ (memq a-op '(zero-or-more opt))))
+ (xr--superset-p b-expr a-expr))
+ 'b-subsumes-a))))))))
+
+(defun xr--check-wrap-around-repetition (operand pos warnings)
+ "Whether OPERAND has a wrap-around repetition subsumption case,
+like (* (* X) ... (* X))."
+ (when (and (consp operand)
+ (memq (car operand) '(seq group group-n)))
+ (let* ((operands
+ (if (eq (car operand) 'group-n)
+ (cddr operand)
+ (cdr operand))))
+ (when (cddr operands)
+ (let* ((first (car operands))
+ (last (car (last operands)))
+ (subsumption (xr--adjacent-subsumption first last)))
+ (when subsumption
+ (xr--report
+ warnings pos
+ (if (eq subsumption 'a-subsumes-b)
+ "First item in repetition subsumes last item (wrapped)"
+ "Last item in repetition subsumes first item (wrapped)"))))))))
+
(defun xr--parse-seq (warnings)
(let ((sequence nil)) ; reversed
(while (not (looking-at (rx (or "\\|" "\\)" eos))))
@@ -502,7 +561,11 @@ UPPER may be nil, meaning infinity."
(not (equal operand "")))
(xr--report
warnings (match-beginning 0)
- "Repetition of expression matching an empty string"))))
+ "Repetition of expression matching an empty string")))
+ ;; (* (* X) ... (* X)) etc: wrap-around subsumption
+ (when (member operator '("*" "+" "*?" "+?"))
+ (xr--check-wrap-around-repetition
+ operand (match-beginning 0) warnings)))
(goto-char (match-end 0))
(setq sequence (cons (xr--postfix operator operand)
(cdr sequence))))
@@ -561,6 +624,12 @@ UPPER may be nil, meaning infinity."
(if comma
"Uncounted repetition"
"Implicit zero repetition")))
+ (when (and warnings
+ (if comma
+ (or (not upper) (>= upper 2))
+ (>= lower 2)))
+ (xr--check-wrap-around-repetition
+ operand (match-beginning 0) warnings))
(goto-char (match-end 0))
(setq sequence (cons (xr--repeat lower
(if comma upper lower)
- [elpa] externals/xr updated (434b300 -> 35dbbeb), Mattias Engdegård, 2020/05/03
- [elpa] externals/xr e5b51bf 01/10: Add wrapped subsumption in repeated forms,
Mattias Engdegård <=
- [elpa] externals/xr f3b61ef 05/10: Fix false negative in empty string repetition check, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr c98bb7b 03/10: Handle whitespace and word syntax subsumption in one place, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr c7e7557 07/10: Broaden anchor check to check more paths, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr d752aab 09/10: Use "option" instead of "repetition" in diagnostics for ? and ??, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr c6e12b7 04/10: Handle \w and \W in subsumption checks, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr 21eab3c 06/10: Check for bol, eol and eos in conflict with other expressions, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr 7160235 02/10: Refactor repetition subsumption check to avoid code duplication, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr d0b09e1 08/10: Add filename-specific checks, Mattias Engdegård, 2020/05/03
- [elpa] externals/xr 35dbbeb 10/10: Increment version to 1.19, Mattias Engdegård, 2020/05/03