0

I am trying to search through some babyl files for errors. The files are email messages that appear between ^L and ^_ (Control-L and Control-_), with a special string "\*\*\* EOOH \*\*\*" in between, marking the end of the header. I want to use regexps to find:

EOOH[^^L]*EOOH

ie two EOOH's with no Control-L in between, but when I use that syntax I get the stack overflow. (That’s with search-forward-regexp or isearch-forward-regexp. And by ^L in the brackets I do mean Control-q Control-l.)

I would also like to find two Control-L's with no EOOH in between, sort of like this, though it's wrong syntax:

^L[^"EOOH"]*^L

Can anyone help?

  • 1
    "when I use that syntax I get the stack overflow" -- show us exactly what you are doing. How are you using that syntax? – phils Mar 14 at 8:11
  • Note that [^^L]* is a regular expression that matches any strings not containing the characters ^ and L. The caret right after the opening bracket negates the character set. The star behind the closing bracket means none or arbitrary many matches of the previous token. Those strings with a negated character set do not stop at whitespaces nor newlines. Such strings can get very long and this might cause the stack overflow. To input C-l literally one types C-q C-l. Maybe that already solves your problem. When you type that key sequence you get a colorized ^L as echo in the minibuffer – Tobias Mar 14 at 12:07
4

You're victim of Emacs's use of a backtracking matcher for the regular expression. You can try and reduce the use of stack in the backtracking with the following:

EOOH\([^^LE]*E\)+OOH

This will reduce the stack use from "one push per char" to "one push per E" and may be sufficient to stay within the limit of the available stack space. The way this works is that the backtracking matcher has ad-hoc optimization for the case where a * or + repetition is followed by a character that can't match the beginning of that repetition (i.e. the first char matched by the body of the loop is mutually exclusive with the first char matched after the loop) in which case we know there's no point pushing a backtracking point onto the stack.

The limits of this kind of hacking become obvious when you get to the second part of your question: it's possible to define a regexp that matches "the negation of "EOOH" but it's a real pain in the rear. You're better off writing Elisp code for that. E.g.

(let ((last-was-EOOH nil))
  (while (re-search-forward "\^L\\|EOOH\\(\\)" nil t)
    (let ((is-EOOH (match-beginning 1)))
      (cond
       ((and is-EOOH last-was-EOOH)
        (message "Found two EOOH in a row"))
       ((not (or is-EOOH last-was-EOOH))
        (message "Found two ^L in a row")))
      (setq last-was-EOOH is-EOOH))))
  • Very clear. I appreciate your input. Time to learn a little more e-lisp. – Diagon Mar 15 at 4:08
  • Hm. I added a regexp for ^L<some-characters-but-not-EOOH>^_, but my edit wasn't accepted. (Why not??) I'll add it in this comment: ^L\([^E^L]*E\([^O^L]\|O[^O^L]\|OO[^H^L]\)\)+[^E^L]*.\{0,3\}^_ – Diagon Mar 23 at 1:40
  • @Diagon: because it's not a correction of my answer: make it your own answer. – Stefan Mar 23 at 1:55
  • Yours is the real answer. My addition is just a useful bit of information for anyone who might be curious at the obvious additional question - which I certainly was. Anyway. – Diagon Mar 23 at 3:35
  • @Diagon: there's no such thing as "the real answer". Just put your thing in an answer, and I'm sure some people will like it, even if it may not be a complete answer to the original question (just like my answer is not a complete answer either). – Stefan Mar 23 at 12:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.