The following example of stupid but valid C++ code shows that automatic replacement of
throw EXPRESSION;
by throw new Error(EXPRESSION);
is not possible in a roboust way without exploiting lexical information from the buffer.
The comments in the source code indicate the problems.
#include <string>
int main() {
try {
throw /* some comment */
"some message";
} catch(char* s) {
throw; // re-throw: must not be replaced
}
throw std::string("one ") + std::string("two"); // expression
std::string str = "Please \"throw\" the ball;"; /* stuff after throw must not be replaced */
/* Here we throw in some comment. */ // must not be replaced
throw "first" /* " second"; */ " third";
throw "";
int i = 1;
int j = ( i < 1 ? ( throw "Wrong number ", 0 ) : 2 );
return 0;
}
/*
Local Variables:
compile-command: "g++ /temp/test.cc"
tab-indent-mode: nil
End:
*/
The following commented source code handles all those problems.
It handles at least in this simple example all occurences of throw
correctly.
(defvar my-replace-throw-expr-delimiter-re "[;,)]")
(defun my-replace-throw ()
"Replace
throw EXPRESSION;
by
throw new Error(EXPRESSION);"
(interactive)
(goto-char (point-min))
(let (beg end)
(while
(progn
(while (and (setq beg (re-search-forward "\\_<throw\\_>" nil 'noErr))
(or (nth 8 (syntax-ppss)) ;; "throw" is part of comment or string
(progn
(forward-comment most-positive-fixnum) ;; skip all comments and whitespace
(looking-at-p my-replace-throw-expr-delimiter-re))))) ;; not a re-throw
beg)
;; we found a throw with syntactically relevant argument
;; now we collect all the sexps that belong to the argument
(setq beg (point))
(while
(progn
(forward-sexp)
(forward-comment most-positive-fixnum) ;; skip all comments and whitespace
(null (looking-at-p my-replace-throw-expr-delimiter-re)))) ;; until we find the end of the throw argument
(cl-assert (looking-at-p my-replace-throw-expr-delimiter-re) nil
"Expected end of expression.")
(let ((str (buffer-substring beg (point))))
(delete-region beg (point))
(insert
(format "new Error(%s)" str))))))
Calling M-x `my-replace-throw` RET on the above C++ code gives the following result:
#include <string>
int main() {
try {
throw /* some comment */
new Error("some message");
} catch(char* s) {
throw; // re-throw: must not be replaced
}
throw new Error(std::string("one ") + std::string("two")); // expression
std::string str = "Please \"throw\" the ball;"; /* stuff after throw must not be replaced */
/* Here we throw in some comment. */ // must not be replaced
throw new Error("first" /* " second"; */ " third");
throw new Error("");
int i = 1;
int j = ( i < 1 ? ( throw new Error("Wrong number "), 0 ) : 2 );
return 0;
}
/*
Local Variables:
compile-command: "g++ /temp/test.cc"
tab-indent-mode: nil
End:
*/
buffer-substring
.