;;; regex-tests.el --- tests for regex.c -*- lexical-binding: t; -*- ;; Copyright (C) 2016 Free Software Foundation, Inc. ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . (require 'ert) (require 'cl) (defmacro regex-tests-generic-line (comment-char test-file whitelist &rest body) "Reads a line of the test file TEST-FILE, skipping comments (defined by COMMENT-CHAR), and evaluates the tests in this line as defined in the BODY. Line numbers in the WHITELIST are known failures, and are skipped." `(with-temp-buffer (modify-syntax-entry ?_ "w;; ") ; tests expect _ to be a word (insert-file-contents ,(concat (file-name-directory (buffer-file-name)) test-file)) (let ((case-fold-search nil) (line-number 1) (whitelist-idx 0)) (goto-char (point-min)) (while (not (eobp)) (let ((start (point))) (end-of-line) (narrow-to-region start (point)) (goto-char (point-min)) (when (and ;; ignore comments (save-excursion (re-search-forward ,(concat "^[^" (string comment-char) "]") nil t)) ;; skip lines in the whitelist (let ((whitelist-next (condition-case nil (aref ,whitelist whitelist-idx) (args-out-of-range nil)))) (cond ;; whitelist exhausted. do process this line ((null whitelist-next) t) ;; we're not yet at the next whitelist element. do ;; process this line ((< line-number whitelist-next) t) ;; we're past the next whitelist element. This ;; shouldn't happen ((> line-number whitelist-next) (error (format "We somehow skipped the next whitelist element: line %d" whitelist-next))) ;; we're at the next whitelist element. Skip this ;; line, and advance the whitelist index (t (setq whitelist-idx (1+ whitelist-idx)) nil)))) ,@body) (widen) (forward-line) (beginning-of-line) (setq line-number (1+ line-number))))))) (defun regex-tests-compare (string what-failed bounds-ref &optional substring-ref) "I just ran a search, looking at STRING. WHAT-FAILED describes what failed, if anything; valid values are 'search-failed, 'compilation-failed and nil. I compare the beginning/end of each group with their expected values. This is done with either BOUNDS-REF or SUBSTRING-REF; one of those should be non-nil. BOUNDS-REF is a sequence \[start-ref0 end-ref0 start-ref1 end-ref1 ....] while SUBSTRING-REF is the expected substring obtained by indexing the input string by start/end-ref. If the search was supposed to fail then start-ref0/substring-ref0 is 'search-failed. If the search wasn't even supposed to compile successfully, then start-ref0/substring-ref0 is 'compilation-failed. If I only care about a match succeeding, this can be set to t. This function returns a string that describes the failure, or nil on success" (when (or (and bounds-ref substring-ref) (not (or bounds-ref substring-ref))) (error "Exactly one of bounds-ref and bounds-ref should be non-nil")) (let ((what-failed-ref (car (or bounds-ref substring-ref)))) (cond ((eq what-failed 'search-failed) (cond ((eq what-failed-ref 'search-failed) nil) ((eq what-failed-ref 'compilation-failed) "Expected pattern failure; but no match") (t "Expected match; but no match"))) ((eq what-failed 'compilation-failed) (cond ((eq what-failed-ref 'search-failed) "Expected no match; but pattern failure") ((eq what-failed-ref 'compilation-failed) nil) (t "Expected match; but pattern failure"))) ;; The regex match succeeded ((eq what-failed-ref 'search-failed) "Expected no match; but match") ((eq what-failed-ref 'compilation-failed) "Expected pattern failure; but match") ;; The regex match succeeded, as expected. I now check all the ;; bounds (t (let ((idx 0) msg ref next-ref-function compare-ref-function mismatched-ref-function) (if bounds-ref (setq ref bounds-ref next-ref-function (lambda (x) (cddr x)) compare-ref-function (lambda (ref start-pos end-pos) (or (eq (car ref) t) (and (eq start-pos (car ref)) (eq end-pos (cadr ref))))) mismatched-ref-function (lambda (ref start-pos end-pos) (format "beginning/end positions: %d/%s and %d/%s" start-pos (car ref) end-pos (cadr ref)))) (setq ref substring-ref next-ref-function (lambda (x) (cdr x)) compare-ref-function (lambda (ref start-pos end-pos) (or (eq (car ref) t) (string= (substring string start-pos end-pos) (car ref)))) mismatched-ref-function (lambda (ref start-pos end-pos) (format "beginning/end positions: %d/%s and %d/%s" start-pos (car ref) end-pos (cadr ref))))) (while (not (or (null ref) msg)) (let ((start (match-beginning idx)) (end (match-end idx))) (when (not (funcall compare-ref-function ref start end)) (setq msg (format "Have expected match, but mismatch in group %d: %s" idx (funcall mismatched-ref-function ref start end)))) (setq ref (funcall next-ref-function ref) idx (1+ idx)))) (or msg nil)))))) (defun regex-tests-match (pattern string bounds-ref &optional substring-ref) "I match the given STRING against PATTERN. I compare the beginning/end of each group with their expected values. BOUNDS-REF is a sequence [start-ref0 end-ref0 start-ref1 end-ref1 ....]. If the search was supposed to fail then start-ref0 is 'search-failed. If the search wasn't even supposed to compile successfully, then start-ref0 is 'compilation-failed. This function returns a string that describes the failure, or nil on success" (if (string-match "\\[\\([\\.=]\\)..?\\1\\]" pattern) ;; Skipping test: [.x.] and [=x=] forms not supported by emacs nil (regex-tests-compare string (condition-case nil (if (string-match pattern string) nil 'search-failed) ('invalid-regexp 'compilation-failed)) bounds-ref substring-ref))) (defconst regex-tests-re-even-escapes "\\(?:^\\|[^\\\\]\\)\\(?:\\\\\\\\\\)*" "Regex that matches an even number of \\ characters") (defconst regex-tests-re-odd-escapes (concat regex-tests-re-even-escapes "\\\\") "Regex that matches an odd number of \\ characters") (defun regex-tests-unextend (pattern) "Basic conversion from extended regexen to emacs ones. This is mostly a hack that adds \\ to () and | and {}, and removes it if it already exists. We also change \\S (and \\s) to \\S- (and \\s-) because extended regexen see the former as whitespace, but emacs requires an extra symbol character" (with-temp-buffer (insert pattern) (goto-char (point-min)) (while (re-search-forward "[()|{}]" nil t) ;; point is past special character. If it is escaped, unescape ;; it (if (save-excursion (re-search-backward (concat regex-tests-re-odd-escapes ".\\=") nil t)) ;; This special character is preceded by an odd number of \, ;; so I unescape it by removing the last one (progn (forward-char -2) (delete-char 1) (forward-char 1)) ;; This special character is preceded by an even (possibly 0) ;; number of \. I add an escape (forward-char -1) (insert "\\") (forward-char 1))) ;; convert \s to \s- (goto-char (point-min)) (while (re-search-forward (concat regex-tests-re-odd-escapes "[Ss]") nil t) (insert "-")) (buffer-string))) (defun regex-tests-BOOST-frob-escapes (s ispattern) "Mangle \\ the way it is done in frob_escapes() in regex-tests-BOOST.c in glibc: \\t, \\n, \\r are interpreted; \\\\, \\^, \{, \\|, \} are unescaped for the string (not pattern)" ;; this is all similar to (regex-tests-unextend) (with-temp-buffer (insert s) (let ((interpret-list (list "t" "n" "r"))) (while interpret-list (goto-char (point-min)) (while (re-search-forward (concat "\\(" regex-tests-re-even-escapes "\\)" "\\\\" (car interpret-list)) nil t) (replace-match (concat "\\1" (car (read-from-string (concat "\"\\" (car interpret-list) "\"")))))) (setq interpret-list (cdr interpret-list)))) (when (not ispattern) ;; unescape \\, \^, \{, \|, \} (let ((unescape-list (list "\\\\" "^" "{" "|" "}"))) (while unescape-list (goto-char (point-min)) (while (re-search-forward (concat "\\(" regex-tests-re-even-escapes "\\)" "\\\\" (car unescape-list)) nil t) (replace-match (concat "\\1" (car unescape-list)))) (setq unescape-list (cdr unescape-list)))) ) (buffer-string))) (defconst regex-tests-BOOST-whitelist [ ;; emacs is more stringent with regexen involving unbalanced ) 63 65 69 ;; in emacs, regex . doesn't match \n 91 ;; emacs is more forgiving with * and ? that don't apply to ;; characters 107 108 109 122 123 124 140 141 142 ;; emacs accepts regexen with {} 161 ;; emacs doesn't fail on bogus ranges such as [3-1] or [1-3-5] 222 223 ;; emacs doesn't match (ab*)[ab]*\1 greedily: only 4 chars of ;; ababaaa match 284 294 ;; ambiguous groupings are ambiguous 443 444 445 446 448 449 450 ;; emacs doesn't know how to handle weird ranges such as [a-Z] and ;; [[:alpha:]-a] 539 580 581 ;; emacs matches non-greedy regex ab.*? non-greedily 639 677 712 ] "Line numbers in the boost test that should be skipped. These are false-positive test failures that represent known/benign differences in behavior.") ;; - Format ;; - Comments are lines starting with ; ;; - Lines starting with - set options passed to regcomp() and regexec(): ;; - if no "REG_BASIC" is found, with have an extended regex ;; - These set a flag: ;; - REG_ICASE ;; - REG_NEWLINE ;; - REG_NOTBOL ;; - REG_NOTEOL ;; ;; - Test lines are ;; pattern string start0 end0 start1 end1 ... ;; ;; - pattern, string can have escapes ;; - string can have whitespace if enclosed in "" ;; - if string is "!", then the pattern is supposed to fail compilation ;; - start/end are of group0, group1, etc. group 0 is the full match ;; - start<0 indicates "no match" ;; - start is the 0-based index of the first character ;; - end is the 0-based index of the first character past the group (defun regex-tests-BOOST () (let (failures basic icase newline notbol noteol) (regex-tests-generic-line ?; "regex-resources/BOOST.tests" regex-tests-BOOST-whitelist (if (save-excursion (re-search-forward "^-" nil t)) (setq basic (save-excursion (re-search-forward "REG_BASIC" nil t)) icase (save-excursion (re-search-forward "REG_ICASE" nil t)) newline (save-excursion (re-search-forward "REG_NEWLINE" nil t)) notbol (save-excursion (re-search-forward "REG_NOTBOL" nil t)) noteol (save-excursion (re-search-forward "REG_NOTEOL" nil t))) (save-excursion (or (re-search-forward "\\(\\S-+\\)\\s-+\"\\(.*\\)\"\\s-+?\\(.+\\)" nil t) (re-search-forward "\\(\\S-+\\)\\s-+\\(\\S-+\\)\\s-+?\\(.+\\)" nil t) (re-search-forward "\\(\\S-+\\)\\s-+\\(!\\)" nil t))) (let* ((pattern-raw (match-string 1)) (string-raw (match-string 2)) (positions-raw (match-string 3)) (pattern (regex-tests-BOOST-frob-escapes pattern-raw t)) (string (regex-tests-BOOST-frob-escapes string-raw nil)) (positions (if (string= string "!") (list 'compilation-failed 0) (mapcar (lambda (x) (let ((x (string-to-number x))) (if (< x 0) nil x))) (split-string positions-raw))))) (when (null (car positions)) (setcar positions 'search-failed)) (when (not basic) (setq pattern (regex-tests-unextend pattern))) ;; great. I now have all the data parsed. Let's use it to do ;; stuff (let* ((case-fold-search icase) (msg (regex-tests-match pattern string positions))) (if (and ;; Skipping test: notbol/noteol not supported (not notbol) (not noteol) msg) ;; store failure (setq failures (cons (format "line number %d: Regex '%s': %s" line-number pattern msg) failures))))))) failures)) (defconst regex-tests-PCRE-whitelist [ ;; ambiguous groupings are ambiguous 610 611 1154 1157 1160 1168 1171 1176 1179 1182 1185 1188 1193 1196 1203 ] "Line numbers in the PCRE test that should be skipped. These are false-positive test failures that represent known/benign differences in behavior.") ;; - Format ;; ;; regex ;; input_string ;; group_num: group_match | "No match" ;; input_string ;; group_num: group_match | "No match" ;; input_string ;; group_num: group_match | "No match" ;; input_string ;; group_num: group_match | "No match" ;; ... (defun regex-tests-PCRE () (let (failures pattern icase string what-failed matches-observed) (regex-tests-generic-line ?# "regex-resources/PCRE.tests" regex-tests-PCRE-whitelist (cond ;; pattern ((save-excursion (re-search-forward "^/\\(.*\\)/\\(.*i?\\)$" nil t)) (setq icase (string= "i" (match-string 2)) pattern (regex-tests-unextend (match-string 1)))) ;; string. read it in, match against pattern, and save all the results ((save-excursion (re-search-forward "^ \\(.*\\)" nil t)) (let ((case-fold-search icase)) (setq string (match-string 1) ;; the regex match under test what-failed (condition-case nil (if (string-match pattern string) nil 'search-failed) ('invalid-regexp 'compilation-failed)) matches-observed (loop for x from 0 to 20 collect (and (not what-failed) (or (match-string x string) ""))))) nil) ;; verification line: failed match ((save-excursion (re-search-forward "^No match" nil t)) (unless what-failed (setq failures (cons (format "line number %d: Regex '%s': Expected no match; but match" line-number pattern) failures)))) ;; verification line: succeeded match ((save-excursion (re-search-forward "^ *\\([0-9]+\\): \\(.*\\)" nil t)) (let* ((match-ref (match-string 2)) (idx (string-to-number (match-string 1)))) (if what-failed "Expected match; but no match" (unless (string= match-ref (elt matches-observed idx)) (setq failures (cons (format "line number %d: Regex '%s': Have expected match, but group %d is wrong: '%s'/'%s'" line-number pattern idx match-ref (elt matches-observed idx)) failures)))))) ;; reset (t (setq pattern nil) nil))) failures)) (defconst regex-tests-PTESTS-whitelist [ ;; emacs doesn't barf on weird ranges such as [b-a], but simply ;; fails to match 138 ;; emacs doesn't see DEL (0x78) as a [:cntrl:] character 168 ] "Line numbers in the PTESTS test that should be skipped. These are false-positive test failures that represent known/benign differences in behavior.") ;; - Format ;; - fields separated by ¦ (note: this is not a |) ;; - start¦end¦pattern¦string ;; - start is the 1-based index of the first character ;; - end is the 1-based index of the last character (defun regex-tests-PTESTS () (let (failures) (regex-tests-generic-line ?# "regex-resources/PTESTS" regex-tests-PTESTS-whitelist (let* ((fields (split-string (buffer-string) "¦")) ;; string has 1-based index of first char in the ;; match. -1 means "no match". -2 means "invalid ;; regex". ;; ;; start-ref is 0-based index of first char in the ;; match ;; ;; string==0 is a special case, and I have to treat ;; it as start-ref = 0 (start-ref (let ((raw (string-to-number (elt fields 0)))) (cond ((= raw -2) 'compilation-failed) ((= raw -1) 'search-failed) ((= raw 0) 0) (t (1- raw))))) ;; string has 1-based index of last char in the ;; match. end-ref is 0-based index of first char past ;; the match (end-ref (string-to-number (elt fields 1))) (pattern (elt fields 2)) (string (elt fields 3))) (let ((msg (regex-tests-match pattern string (list start-ref end-ref)))) (when msg (setq failures (cons (format "line number %d: Regex '%s': %s" line-number pattern msg) failures)))))) failures)) (defconst regex-tests-TESTS-whitelist [ ;; emacs doesn't barf on weird ranges such as [b-a], but simply ;; fails to match 42 ;; emacs is more forgiving with * and ? that don't apply to ;; characters 57 58 59 60 ;; emacs is more stringent with regexen involving unbalanced ) 67 ] "Line numbers in the TESTS test that should be skipped. These are false-positive test failures that represent known/benign differences in behavior.") ;; - Format ;; - fields separated by :. Watch for [\[:xxx:]] ;; - expected:pattern:string ;; ;; expected: ;; | 0 | successful match | ;; | 1 | failed match | ;; | 2 | regcomp() should fail | (defun regex-tests-TESTS () (let (failures) (regex-tests-generic-line ?# "regex-resources/TESTS" regex-tests-TESTS-whitelist (if (save-excursion (re-search-forward "^\\([^:]+\\):\\(.*\\):\\([^:]*\\)$" nil t)) (let* ((what-failed (let ((raw (string-to-number (match-string 1)))) (cond ((= raw 2) 'compilation-failed) ((= raw 1) 'search-failed) (t t)))) (string (match-string 3)) (pattern (regex-tests-unextend (match-string 2)))) (let ((msg (regex-tests-match pattern string nil (list what-failed)))) (when msg (setq failures (cons (format "line number %d: Regex '%s': %s" line-number pattern msg) failures))))) (error "Error parsing TESTS file line: '%s'" (buffer-string)))) failures)) (ert-deftest regex-tests-glibc () "Tests of the regular expression engine. This evaluates the BOOST, PCRE, PTESTS and TESTS test cases from glibc." (should-not (regex-tests-BOOST)) (should-not (regex-tests-PCRE)) (should-not (regex-tests-PTESTS)) (should-not (regex-tests-TESTS))) (defun regex-tests--should-match (case-fold dir re str) "Does the given RE match the STR? Returns boolean value. CASE-FOLD indicates whether a global case-fold-search should be enabled (acceptable values are 'no-case-fold and 'yes-case-fold). DIR indicates the direction of search (acceptable values are 'forward and 'backward). " (let ((case-fold-search (cond ((eq case-fold 'no-case-fold) nil) ((eq case-fold 'yes-case-fold) t) (t (error (format "Unknown case-fold value: %s" case-fold)))))) (with-temp-buffer (insert str) (cond ((eq dir 'forward) (progn (goto-char (point-min)) (re-search-forward re nil t))) ((eq dir 'backward) (progn (goto-char (point-max)) (re-search-backward re nil t))) (t (error (format "Unknown dir value: %s" dir))))))) (defun regex-tests--check-match (case-fold re str should-match &rest should-begend) "Checks to make sure a regex match did/did not match as it was supposed to, and that all the start/end points were correct. These bounds are given in SHOULD-BEGEND, and are 1-based. " (dolist (dir (list 'forward 'backward)) (if should-match (should (regex-tests--should-match case-fold dir re str)) (should-not (regex-tests--should-match case-fold dir re str))) (let ((idx 0)) (while should-begend (should (= (match-beginning idx) (car should-begend))) (should (= (match-end idx) (cadr should-begend))) (setq should-begend (cddr should-begend) idx (1+ idx)))))) (defun regex-tests-should-match (case-fold re str &rest should-begend) "Checks to make sure a regex match did match, and that all the start/end points were correct. These bounds are given in SHOULD-BEGEND, and are 1-based. " (apply 'regex-tests--check-match case-fold re str t should-begend)) (defun regex-tests-should-not-match (case-fold re str) "Checks to make sure a regex match did not match." (funcall 'regex-tests--check-match case-fold re str nil)) (ert-deftest regex-tests-case-fold () "Tests of the case-fold embedded modifier." (let ((str "abcABC")) (regex-tests-should-match 'no-case-fold "\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)bC" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)bC" str 2 4) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)Bc" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)bC" str) (regex-tests-should-not-match 'yes-case-fold "\\(?-i\\)Bc" str) (regex-tests-should-not-match 'yes-case-fold "\\(?-i\\)bC" str) (regex-tests-should-match 'no-case-fold "B\\(?i\\)c" str 5 7) (regex-tests-should-match 'no-case-fold "b\\(?i\\)C" str 2 4) (regex-tests-should-match 'yes-case-fold "B\\(?i\\)c" str 2 4) (regex-tests-should-match 'yes-case-fold "b\\(?i\\)C" str 2 4) (regex-tests-should-not-match 'no-case-fold "B\\(?-i\\)c" str) (regex-tests-should-not-match 'no-case-fold "b\\(?-i\\)C" str) (regex-tests-should-match 'yes-case-fold "B\\(?-i\\)c" str 2 4) (regex-tests-should-match 'yes-case-fold "b\\(?-i\\)C" str 5 7) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|\\(?i\\)bC" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|\\(?i\\)bC" str 2 4) (regex-tests-should-not-match 'no-case-fold "\\(?i\\)x\\|\\(?-i\\)Bc" str) (regex-tests-should-not-match 'no-case-fold "\\(?i\\)x\\|\\(?-i\\)bC" str) (regex-tests-should-not-match 'yes-case-fold "\\(?i\\)x\\|\\(?-i\\)Bc" str) (regex-tests-should-not-match 'yes-case-fold "\\(?i\\)x\\|\\(?-i\\)bC" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|B\\(?i\\)c" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|b\\(?i\\)C" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|B\\(?i\\)c" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|b\\(?i\\)C" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|B\\(?-i\\)c" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)x\\|b\\(?-i\\)C" str 5 7) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|B\\(?-i\\)c" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)x\\|b\\(?-i\\)C" str 5 7) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)C" str 5 7 5 6) (regex-tests-should-not-match 'no-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)B" str) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)c" str 2 4 2 3) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)b" str 1 3 1 2) (regex-tests-should-match 'yes-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)C" str 2 4 2 3) (regex-tests-should-match 'yes-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)B" str 1 3 1 2) (regex-tests-should-match 'yes-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)c" str 2 4 2 3) (regex-tests-should-match 'yes-case-fold "\\(\\(?i\\)x\\|\\(?-i\\)a\\|\\(?i\\)B\\)b" str 1 3 1 2)) (let ((str "ABCabc")) (regex-tests-should-match 'no-case-fold "\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)bC" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)Bc" str 2 4) (regex-tests-should-match 'yes-case-fold "\\(?i\\)bC" str 2 4) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)Bc" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)bC" str) (regex-tests-should-not-match 'yes-case-fold "\\(?-i\\)Bc" str) (regex-tests-should-not-match 'yes-case-fold "\\(?-i\\)bC" str) (regex-tests-should-match 'no-case-fold "B\\(?i\\)c" str 2 4) (regex-tests-should-match 'no-case-fold "b\\(?i\\)C" str 5 7) (regex-tests-should-match 'yes-case-fold "B\\(?i\\)c" str 2 4) (regex-tests-should-match 'yes-case-fold "b\\(?i\\)C" str 2 4) (regex-tests-should-not-match 'no-case-fold "B\\(?-i\\)c" str) (regex-tests-should-not-match 'no-case-fold "b\\(?-i\\)C" str) (regex-tests-should-match 'yes-case-fold "B\\(?-i\\)c" str 5 7) (regex-tests-should-match 'yes-case-fold "b\\(?-i\\)C" str 2 4)) (let ((str "12abcdef")) (regex-tests-should-match 'no-case-fold "abc" str 3 6) (regex-tests-should-not-match 'no-case-fold "aBc" str) (regex-tests-should-not-match 'no-case-fold "a\\(?-i\\)Bc" str) (regex-tests-should-match 'no-case-fold "a\\(?i\\)B\\(?-i\\)c" str 3 6) (regex-tests-should-not-match 'no-case-fold "a\\(?i\\)B\\(?-i\\)C" str) (regex-tests-should-match 'no-case-fold "a\\(?:\\(?i\\)B\\)c" str 3 6) (regex-tests-should-not-match 'no-case-fold "a\\(?:\\(?i\\)B\\)C" str) (regex-tests-should-match 'no-case-fold "a\\(\\(?i\\)B\\)c" str 3 6 4 5) (regex-tests-should-not-match 'no-case-fold "a\\(\\(?i\\)B\\)C" str) (regex-tests-should-not-match 'no-case-fold "A\\(?:\\(?i\\)B\\)c" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)A\\(?:\\(?i\\)B\\)C" str 3 6) (regex-tests-should-not-match 'no-case-fold "\\(?i\\)A\\(?:\\(?-i\\)B\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)A\\(?:\\(?-i\\)b\\)C" str 3 6) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)A\\(?:\\(?i\\)B\\)C" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)a\\(?:\\(?i\\)B\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?-i\\)a\\(?:\\(?i\\)B\\)c" str 3 6) (regex-tests-should-not-match 'no-case-fold "A\\(?:\\(?i\\)[B-B]\\)c" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)A\\(?:\\(?i\\)[B-B]\\)C" str 3 6) (regex-tests-should-not-match 'no-case-fold "\\(?i\\)A\\(?:\\(?-i\\)[B-B]\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)A\\(?:\\(?-i\\)[b-b]\\)C" str 3 6) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)A\\(?:\\(?i\\)[B-B]\\)C" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)a\\(?:\\(?i\\)[B-B]\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?-i\\)a\\(?:\\(?i\\)[B-B]\\)c" str 3 6) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(?:\\(?i\\)B\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(?:\\(?i\\)B\\)c" str 4 6) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)B\\)c" str 4 6 4 5)) (let ((str "12axxxabcdef")) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(?:\\(?i\\)B\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(?:\\(?i\\)B\\)c" str 8 10) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)B\\)c" str 8 10 8 9)) (let ((str "12aBcxxxABCabcdef")) ;; These required changes in analyze_first() and re_search_2() to ;; track case-fold inside fastmap[] (regex-tests-should-match 'no-case-fold "\\(?i\\)B\\(?-i\\)C" str 10 12) (regex-tests-should-match 'yes-case-fold "B\\(?-i\\)C" str 10 12) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)B\\)C" str 10 12 10 11) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)B\\)c" str 4 6 4 5) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(\\(?-i\\)b\\)C" str) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)B\\)C" str 10 12 10 11) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)B\\)c" str 4 6 4 5) (regex-tests-should-not-match 'no-case-fold "\\(\\(?-i\\)b\\)C" str) (regex-tests-should-match 'no-case-fold "\\(?i\\)[B-B]\\(?-i\\)C" str 10 12) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)[B-B]\\)C" str 10 12 10 11) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)[B-B]\\)c" str 4 6 4 5) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(\\(?-i\\)[b-b]\\)C" str) (regex-tests-should-not-match 'no-case-fold "\\(?i\\)[^B-B]\\(?-i\\)C" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)[^B-B]\\)C" str) (regex-tests-should-not-match 'no-case-fold "\\(?-i\\)\\(\\(?i\\)[^B-B]\\)c" str) (regex-tests-should-match 'no-case-fold "\\(?-i\\)\\(\\(?-i\\)[^b-b]\\)C" str 10 12 10 11) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)B\\)C" str 10 12 10 11) (regex-tests-should-match 'no-case-fold "\\(\\(?i\\)B\\)c" str 4 6 4 5)) (let ((str "abc")) (regex-tests-should-match 'no-case-fold "^\\(?i\\)abc" str 1 4) (regex-tests-should-match 'no-case-fold "\\(?i\\)^abc" str 1 4) (regex-tests-should-match 'no-case-fold "abc\\(?i\\)$" str 1 4) (regex-tests-should-match 'no-case-fold "abc$\\(?i\\)" str 1 4)))