Mercurial > emacs
comparison lisp/progmodes/cmacexp.el @ 7008:fd080d04dfe2
(c-macro-expansion): changed the algorithm to allow
__FILE__ and __LINE__ directives to be expanded correctly.
(c-macro-expansion): use a forth optional argument DISPLAY meaning
it should print messages at the start and end of preprocessing.
All callers changed.
(c-macro-expand): Don't display message here.
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Thu, 21 Apr 1994 18:40:14 +0000 |
parents | 9d977a1a7147 |
children | 4fd40bd394fe |
comparison
equal
deleted
inserted
replaced
7007:52b0ebfd3191 | 7008:fd080d04dfe2 |
---|---|
1 ;;; cmacexp.el --- expand C macros in a region | 1 ;;; cmacexp.el --- expand C macros in a region |
2 | 2 |
3 ;; Copyright (C) 1992 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1992 Free Software Foundation, Inc. |
4 | 4 |
5 ;; Author: Francesco Potorti` <pot@cnuce.cnr.it> | 5 ;; Author: Francesco Potorti` <pot@cnuce.cnr.it> |
6 ;; Version: $Id: cmacexp.el,v 1.11 1994/04/20 06:12:03 rms Exp rms $ | 6 ;; Version: $Id: cmacexp.el 1.14 1994/04/20 15:50:48 pot Exp $ |
7 ;; Adapted-By: ESR | 7 ;; Adapted-By: ESR |
8 ;; Keywords: c | 8 ;; Keywords: c |
9 | 9 |
10 ;; This file is part of GNU Emacs. | 10 ;; This file is part of GNU Emacs. |
11 | 11 |
23 ;; along with GNU Emacs; see the file COPYING. If not, write to | 23 ;; along with GNU Emacs; see the file COPYING. If not, write to |
24 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | 24 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
25 | 25 |
26 ;; USAGE ============================================================= | 26 ;; USAGE ============================================================= |
27 | 27 |
28 ;; In C mode C-M-x is bound to c-macro-expand. The result of the | 28 ;; In C mode C-C C-e is bound to c-macro-expand. The result of the |
29 ;; expansion is put in a separate buffer. The buffer is put in | 29 ;; expansion is put in a separate buffer. A user option allows the |
30 ;; view-mode if the Inge Frick's view.el is installed. A user option | 30 ;; window displaying the buffer to be optimally sized. |
31 ;; allows the window displaying the buffer to be optimally sized. | |
32 ;; | 31 ;; |
33 ;; When called with a C-u prefix, c-macro-expand replaces the selected | 32 ;; When called with a C-u prefix, c-macro-expand replaces the selected |
34 ;; region with the expansion. Both the preprocessor name and the | 33 ;; region with the expansion. Both the preprocessor name and the |
35 ;; initial flag can be set by the user. If c-macro-prompt-p | 34 ;; initial flag can be set by the user. If c-macro-prompt-flag is set |
36 ;; is set to a non-nil value the user is offered to change the flags | 35 ;; to a non-nil value the user is offered to change the options to the |
37 ;; to the preprocessor each time c-macro-expand is invoked. | 36 ;; preprocessor each time c-macro-expand is invoked. Preprocessor |
38 ;; Preprocessor arguments default to the last ones entered. | 37 ;; arguments default to the last ones entered. If c-macro-prompt-flag |
39 ;; If c-macro-prompt is nil, one must use M-x set-variable to set a | 38 ;; is nil, one must use M-x set-variable to set a different value for |
40 ;; different value for c-macro-cppflags. | 39 ;; c-macro-cppflags. |
41 | 40 |
42 ;; A c-macro-expansion function is provided for non-interactive use. | 41 ;; A c-macro-expansion function is provided for non-interactive use. |
43 | 42 |
44 ;; INSTALLATION ====================================================== | 43 ;; INSTALLATION ====================================================== |
45 | 44 |
46 ;; Put this file on your load-path, byte compile it for increased | 45 ;; Put the following in your ~/.emacs file. |
47 ;; speed and put part or all of the following in your ~/.emacs file. | 46 |
48 | |
49 ;; To make a directory ~/emacs be in front of your load-path: | |
50 ;;(setq load-path (cons (expand-file-name "~/emacs") load-path)) | |
51 ;; | |
52 ;; Suggested keybinding (work only in c-mode): | |
53 ;;(if (boundp 'c-mode-map) | |
54 ;; (define-key c-mode-map "\C-x\C-e" 'c-macro-expand)) | |
55 ;;(if (boundp 'c++-mode-map) | |
56 ;; (define-key c++-mode-map "\C-x\C-e" 'c-macro-expand)) | |
57 ;; | |
58 ;; If you want the *Macroexpansion* window to be not higher than | 47 ;; If you want the *Macroexpansion* window to be not higher than |
59 ;; necessary: | 48 ;; necessary: |
60 ;;(setq c-macro-shrink-window-p t) | 49 ;;(setq c-macro-shrink-window-flag t) |
61 ;; | 50 ;; |
62 ;; If you use a preprocessor other than /lib/cpp (be careful to set a | 51 ;; If you use a preprocessor other than /lib/cpp (be careful to set a |
63 ;; -C option or equivalent in order to make the preprocessor not to | 52 ;; -C option or equivalent in order to make the preprocessor not to |
64 ;; strip the comments): | 53 ;; strip the comments): |
65 ;;(setq c-macro-preprocessor "gpp -C") | 54 ;;(setq c-macro-preprocessor "gpp -C") |
66 ;; | 55 ;; |
67 ;; If you often use a particular set of flags: | 56 ;; If you often use a particular set of flags: |
68 ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG" | 57 ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG" |
69 ;; | 58 ;; |
70 ;; If you want the "Preprocessor arguments: " prompt: | 59 ;; If you want the "Preprocessor arguments: " prompt: |
71 ;;(setq c-macro-prompt-p t) | 60 ;;(setq c-macro-prompt-flag t) |
72 | 61 |
73 ;; BUG REPORTS ======================================================= | 62 ;; BUG REPORTS ======================================================= |
74 | 63 |
75 ;; Please report bugs, suggestions, complaints and so on to | 64 ;; Please report bugs, suggestions, complaints and so on to |
76 ;; pot@cnuce.cnr.it (Francesco Potorti`). | 65 ;; pot@cnuce.cnr.it (Francesco Potorti`). |
81 ;; - #line directives are inserted, so __LINE__ and __FILE__ are | 70 ;; - #line directives are inserted, so __LINE__ and __FILE__ are |
82 ;; correctly expanded. Works even with START inside a string, a | 71 ;; correctly expanded. Works even with START inside a string, a |
83 ;; comment or a region #ifdef'd away by cpp. cpp is invoked with -C, | 72 ;; comment or a region #ifdef'd away by cpp. cpp is invoked with -C, |
84 ;; making comments visible in the expansion. | 73 ;; making comments visible in the expansion. |
85 ;; - All work is done in core memory, no need for temporary files. | 74 ;; - All work is done in core memory, no need for temporary files. |
86 ;; - The /lib/cpp process is run synchronously. This fixes an | |
87 ;; infinite loop bug on Motorola Delta (cpp waiting forever for | |
88 ;; end-of-file, don't know why). Fixes a similar intermittent | |
89 ;; problem on SunOS 4.1. | |
90 | 75 |
91 ;; ACKNOWLEDGEMENTS ================================================== | 76 ;; ACKNOWLEDGEMENTS ================================================== |
92 | 77 |
93 ;; A lot of thanks to Don Maszle who did a great work of testing, bug | 78 ;; A lot of thanks to Don Maszle who did a great work of testing, bug |
94 ;; reporting and suggestion of new features and to Inge Fricks for her | 79 ;; reporting and suggestion of new features. This work has been |
95 ;; help with view.el. This work has been partially inspired by Don | 80 ;; partially inspired by Don Maszle and Jonathan Segal's. |
96 ;; Maszle and Jonathan Segal's. | |
97 | |
98 ;; By the way, I recommend you Inge Frick's view.el. It works like | |
99 ;; the standard view, but *it is not recursive* and has some more | |
100 ;; commands. Moreover it is a minor mode, so you preserve all your | |
101 ;; major mode keybindings (well, not always :). Mail me to obtain a | |
102 ;; copy, or get it by anonymous ftp in fly.cnuce.cnr.it:pub/view.el. | |
103 | 81 |
104 ;; BUGS ============================================================== | 82 ;; BUGS ============================================================== |
105 | 83 |
106 ;; If the start point of the region is inside a macro definition the | 84 ;; If the start point of the region is inside a macro definition the |
107 ;; macro expansion is often inaccurate. | 85 ;; macro expansion is often inaccurate. |
141 (interactive "r\nP") | 119 (interactive "r\nP") |
142 (let ((inbuf (current-buffer)) | 120 (let ((inbuf (current-buffer)) |
143 (displaybuf (if subst | 121 (displaybuf (if subst |
144 (get-buffer c-macro-buffer-name) | 122 (get-buffer c-macro-buffer-name) |
145 (get-buffer-create c-macro-buffer-name))) | 123 (get-buffer-create c-macro-buffer-name))) |
146 (expansion "") | 124 (expansion "")) |
147 (mymsg "")) | |
148 ;; Build the command string. | 125 ;; Build the command string. |
149 (if c-macro-prompt-flag | 126 (if c-macro-prompt-flag |
150 (setq c-macro-cppflags | 127 (setq c-macro-cppflags |
151 (read-string "Preprocessor arguments: " | 128 (read-string "Preprocessor arguments: " |
152 c-macro-cppflags))) | 129 c-macro-cppflags))) |
153 (setq mymsg (format "Invoking %s%s%s on region..." | |
154 c-macro-preprocessor | |
155 (if (string= "" c-macro-cppflags) "" " ") | |
156 c-macro-cppflags)) | |
157 ;; Decide where to display output. | 130 ;; Decide where to display output. |
158 (if (and subst | 131 (if (and subst |
159 (and buffer-read-only (not inhibit-read-only)) | 132 (and buffer-read-only (not inhibit-read-only)) |
160 (not (eq inbuf displaybuf))) | 133 (not (eq inbuf displaybuf))) |
161 (progn | 134 (progn |
164 (sit-for 2) | 137 (sit-for 2) |
165 (setq subst nil) | 138 (setq subst nil) |
166 (or displaybuf | 139 (or displaybuf |
167 (setq displaybuf (get-buffer-create c-macro-buffer-name))))) | 140 (setq displaybuf (get-buffer-create c-macro-buffer-name))))) |
168 ;; Expand the macro and output it. | 141 ;; Expand the macro and output it. |
169 (message mymsg) | |
170 (setq expansion (c-macro-expansion start end | 142 (setq expansion (c-macro-expansion start end |
171 (concat c-macro-preprocessor " " | 143 (concat c-macro-preprocessor " " |
172 c-macro-cppflags))) | 144 c-macro-cppflags) t)) |
173 (message (concat mymsg "done")) | |
174 (if subst | 145 (if subst |
175 (let ((exchange (= (point) start))) | 146 (let ((exchange (= (point) start))) |
176 (delete-region start end) | 147 (delete-region start end) |
177 (insert expansion) | 148 (insert expansion) |
178 (if exchange | 149 (if exchange |
233 (window-height))) | 204 (window-height))) |
234 (goto-char (point-min)) | 205 (goto-char (point-min)) |
235 (select-window oldwin)))))) | 206 (select-window oldwin)))))) |
236 | 207 |
237 | 208 |
238 (defun c-macro-expansion (start end cppcommand) | 209 (defun c-macro-expansion (start end cppcommand &optional display) |
239 "Run a preprocessor on region and return the output as a string. | 210 "Run a preprocessor on region and return the output as a string. |
240 Expand the region between START and END in the current buffer using | 211 Expand the region between START and END in the current buffer using |
241 the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\"). | 212 the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\"). |
242 Be sure to use a -C (don't strip comments) or equivalent option." | 213 Be sure to use a -C (don't strip comments) or equivalent option. |
214 Optional arg DISPLAY non-nil means show messages in the echo area." | |
243 | 215 |
244 ;; Copy the current buffer's contents to a temporary hidden buffer. | 216 ;; Copy the current buffer's contents to a temporary hidden buffer. |
245 ;; Delete from END to end of buffer. Insert a preprocessor #line | 217 ;; Delete from END to end of buffer. Insert a preprocessor #line |
246 ;; directive at START and after each #endif following START that are | 218 ;; directive at START and after each #endif following START that are |
247 ;; not inside a comment or a string. Put all the strings thus | 219 ;; not inside a comment or a string. Put all the strings thus |
257 (filename (if (and buffer-file-name | 229 (filename (if (and buffer-file-name |
258 (string-match (regexp-quote default-directory) | 230 (string-match (regexp-quote default-directory) |
259 buffer-file-name)) | 231 buffer-file-name)) |
260 (substring buffer-file-name (match-end 0)) | 232 (substring buffer-file-name (match-end 0)) |
261 (buffer-name))) | 233 (buffer-name))) |
262 (start-state) | 234 (mymsg (format "Invoking %s%s%s on region..." |
235 c-macro-preprocessor | |
236 (if (string= "" c-macro-cppflags) "" " ") | |
237 c-macro-cppflags)) | |
238 (uniquestring "???!!!???!!! start of c-macro expansion ???!!!???!!!") | |
239 (startlinenum 0) | |
263 (linenum 0) | 240 (linenum 0) |
264 (linelist ())) | 241 (startstat ()) |
242 (startmarker "")) | |
265 (unwind-protect | 243 (unwind-protect |
266 (save-excursion | 244 (save-excursion |
267 (save-restriction | 245 (save-restriction |
268 (widen) | 246 (widen) |
269 (set-buffer outbuf) | 247 (set-buffer outbuf) |
273 (insert-buffer-substring inbuf 1 end)) | 251 (insert-buffer-substring inbuf 1 end)) |
274 | 252 |
275 ;; We have copied inbuf to outbuf. Point is at end of | 253 ;; We have copied inbuf to outbuf. Point is at end of |
276 ;; outbuf. Insert a space at the end, so cpp can correctly | 254 ;; outbuf. Insert a space at the end, so cpp can correctly |
277 ;; parse a token ending at END. | 255 ;; parse a token ending at END. |
278 | |
279 (insert " ") | 256 (insert " ") |
280 | 257 |
281 (save-excursion | 258 ;; Save sexp status and line number at START. |
282 (goto-char start) | 259 (setq startstat (parse-partial-sexp 1 start)) |
283 (setq start-state (parse-partial-sexp 1 (point)))) | 260 (setq startlinenum (+ (count-lines 1 (point)) |
261 (if (bolp) 1 0))) | |
262 | |
284 ;; Now we insert the #line directives after all #endif or | 263 ;; Now we insert the #line directives after all #endif or |
285 ;; #else following START. | 264 ;; #else following START going backward, so the lines we |
265 ;; insert don't change the line numbers. | |
286 ;(switch-to-buffer outbuf) (debug) ;debugging instructions | 266 ;(switch-to-buffer outbuf) (debug) ;debugging instructions |
267 (goto-char (point-max)) | |
287 (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move) | 268 (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move) |
288 (if (equal (nthcdr 3 (parse-partial-sexp start (point) start-state)) | 269 (if (equal (nthcdr 3 (parse-partial-sexp start (point) |
270 nil nil startstat)) | |
289 '(nil nil nil 0 nil)) ;neither in string nor in | 271 '(nil nil nil 0 nil)) ;neither in string nor in |
290 ;comment nor after quote | 272 ;comment nor after quote |
291 (progn | 273 (progn |
292 (goto-char (match-end 0)) | 274 (goto-char (match-end 0)) |
293 ;;; (setq linenum (count-lines 1 (point))) | 275 (setq linenum (+ startlinenum |
294 (setq linelist | 276 (count-lines start (point)))) |
295 ;; This used to be a #line command | 277 (insert (format "\n#line %d \"%s\"\n" linenum filename)) |
296 ;; but it's not guaranteed that the output | 278 (goto-char (match-beginning 0))))) |
297 ;; will have properly matching commands. | 279 |
298 ;; Only the *line numbers* have to agree! | 280 ;; Now we are at START. Insert the first #line directive. |
299 (cons (format "\n???!!!???!!!!\n") | 281 ;; This must work even inside a string or comment, or after a |
300 linelist)) | |
301 (insert (car linelist)) | |
302 (skip-chars-backward "^#") | |
303 (insert "line") | |
304 (goto-char (match-beginning 0))))) | |
305 | |
306 ;; We are at START. Insert the first #line directive. This | |
307 ;; must work even inside a string or comment, or after a | |
308 ;; quote. | 282 ;; quote. |
309 ;;; (setq linenum (+ (count-lines 1 (point)) | 283 (let* ((startinstring (nth 3 startstat)) |
310 ;;; (if (bolp) 1 0))) | 284 (startincomment (nth 4 startstat)) |
311 (setq linelist | 285 (startafterquote (nth 5 startstat)) |
312 (cons | 286 (startinbcomment (nth 7 startstat))) |
313 (let* ((startstat (parse-partial-sexp 1 start)) | 287 (insert (if startafterquote " " "") |
314 (startinstring (nth 3 startstat)) | 288 (cond (startinstring |
315 (startincomment (nth 4 startstat)) | 289 (char-to-string startinstring)) |
316 (startafterquote (nth 5 startstat)) | 290 (startincomment "*/") |
317 (startinbcomment (nth 6 startstat))) | 291 ("")) |
318 (concat (if startafterquote " ") | 292 (format "\n#line %d \"%s\"\n" startlinenum filename) |
319 (cond (startinstring (char-to-string startinstring)) | 293 (setq startmarker |
320 (startincomment "*/")) | 294 (concat uniquestring |
321 (format "\n???!!!???!!!!") | 295 (cond (startinstring |
322 (cond (startinstring (char-to-string startinstring)) | 296 (char-to-string startinstring)) |
323 (startincomment "/*") | 297 (startincomment "/*") |
324 (startinbcomment "//")) | 298 (startinbcomment "//")) |
325 (if startafterquote "\\"))) | 299 (if startafterquote "\\"))))) |
326 linelist)) | |
327 (insert (car linelist)) | |
328 (skip-chars-backward "^#") | |
329 (insert "line") | |
330 | 300 |
331 ;; Call the preprocessor. | 301 ;; Call the preprocessor. |
302 (if display (message mymsg)) | |
332 (call-process-region 1 (point-max) "sh" t t nil "-c" | 303 (call-process-region 1 (point-max) "sh" t t nil "-c" |
333 (concat cppcommand " 2>/dev/null")) | 304 (concat cppcommand " 2>/dev/null")) |
334 | 305 (if display (message (concat mymsg "done"))) |
335 (while (search-backward "\n???!!!???!!!!" nil t) | 306 |
336 (replace-match "")) | 307 ;; Find and delete the mark of the start of the expansion. |
337 | 308 ;; Look for `# nn "file.c"' lines and delete them. |
309 (goto-char (point-min)) | |
310 (search-forward startmarker) | |
311 (delete-region 1 (point)) | |
312 (while (re-search-forward (concat "^# [0-9]+ \"" | |
313 (regexp-quote filename) | |
314 "\"") nil t) | |
315 (beginning-of-line) | |
316 (let ((beg (point))) | |
317 (forward-line 1) | |
318 (delete-region beg (point)))) | |
319 | |
338 ;; Compute the return value, keeping in account the space | 320 ;; Compute the return value, keeping in account the space |
339 ;; inserted at the end of the buffer. | 321 ;; inserted at the end of the buffer. |
340 (buffer-substring (point) (max (point) (- (point-max) 1)))) | 322 (buffer-substring 1 (max 1 (- (point-max) 1)))) |
341 | 323 |
342 ;; Cleanup. | 324 ;; Cleanup. |
343 (kill-buffer outbuf)))) | 325 (kill-buffer outbuf)))) |
344 | 326 |
345 ;;; cmacexp.el ends here | 327 ;;; cmacexp.el ends here |