* lisp/progmodes/hideif.el: Use lexical-binding. Fix up cl-lib usage.
[bpt/emacs.git] / lisp / progmodes / hideif.el
1 ;;; hideif.el --- hides selected code within ifdef -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 1988, 1994, 2001-2014 Free Software Foundation, Inc.
4
5 ;; Author: Brian Marick
6 ;; Daniel LaLiberte <liberte@holonexus.org>
7 ;; Maintainer: emacs-devel@gnu.org
8 ;; Keywords: c, outlines
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; To initialize, toggle the hide-ifdef minor mode with
28 ;;
29 ;; M-x hide-ifdef-mode
30 ;;
31 ;; This will set up key bindings and call hide-ifdef-mode-hook if it
32 ;; has a value. To explicitly hide ifdefs using a buffer-local
33 ;; define list (default empty), type
34 ;;
35 ;; M-x hide-ifdefs or C-c @ h
36 ;;
37 ;; Hide-ifdef suppresses the display of code that the preprocessor wouldn't
38 ;; pass through. Support complete C/C++ expression and precedence.
39 ;; It will automatically scan for new #define symbols and macros on the way
40 ;; parsing.
41 ;;
42 ;; The hidden code is marked by ellipses (...). Be
43 ;; cautious when editing near ellipses, since the hidden text is
44 ;; still in the buffer, and you can move the point into it and modify
45 ;; text unawares.
46 ;; You can make your buffer read-only while hide-ifdef-hiding by setting
47 ;; hide-ifdef-read-only to a non-nil value. You can toggle this
48 ;; variable with hide-ifdef-toggle-read-only (C-c @ C-q).
49 ;;
50 ;; You can undo the effect of hide-ifdefs by typing
51 ;;
52 ;; M-x show-ifdefs or C-c @ s
53 ;;
54 ;; Use M-x hide-ifdef-define (C-c @ d) to define a symbol.
55 ;; Use M-x hide-ifdef-undef (C-c @ u) to undefine a symbol.
56 ;;
57 ;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
58 ;; the display will be updated. Only the define list for the current
59 ;; buffer will be affected. You can save changes to the local define
60 ;; list with hide-ifdef-set-define-alist. This adds entries
61 ;; to hide-ifdef-define-alist.
62 ;;
63 ;; If you have defined a hide-ifdef-mode-hook, you can set
64 ;; up a list of symbols that may be used by hide-ifdefs as in the
65 ;; following example:
66 ;;
67 ;; (add-hook 'hide-ifdef-mode-hook
68 ;; (lambda ()
69 ;; (unless hide-ifdef-define-alist
70 ;; (setq hide-ifdef-define-alist
71 ;; '((list1 ONE TWO)
72 ;; (list2 TWO THREE))))
73 ;; (hide-ifdef-use-define-alist 'list2))) ; use list2 by default
74 ;;
75 ;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
76 ;; another list to use.
77 ;;
78 ;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
79 ;; set hide-ifdef-initially to non-nil.
80 ;;
81 ;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
82 ;; In the absence of highlighting, that might be a bad idea. If you set
83 ;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
84 ;; lines will be displayed. That can be confusing in its own
85 ;; right. Other variations on display are possible, but not much
86 ;; better.
87 ;;
88 ;; You can explicitly hide or show individual ifdef blocks irrespective
89 ;; of the define list by using hide-ifdef-block and show-ifdef-block.
90 ;;
91 ;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
92 ;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
93 ;;
94 ;; If you have minor-mode-alist in your mode line (the default) two labels
95 ;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
96 ;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
97 ;;
98 ;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
99 ;; Extensively modified by Daniel LaLiberte (while at Gould).
100 ;;
101 ;; Extensively modified by Luke Lee in 2013 to support complete C expression
102 ;; evaluation and argumented macro expansion.
103
104 ;;; Code:
105
106 (require 'cc-mode)
107 (require 'cl-lib)
108
109 (defgroup hide-ifdef nil
110 "Hide selected code within `ifdef'."
111 :group 'c)
112
113 (defcustom hide-ifdef-initially nil
114 "Non-nil means call `hide-ifdefs' when Hide-Ifdef mode is first activated."
115 :type 'boolean
116 :group 'hide-ifdef)
117
118 (defcustom hide-ifdef-read-only nil
119 "Set to non-nil if you want buffer to be read-only while hiding text."
120 :type 'boolean
121 :group 'hide-ifdef)
122
123 (defcustom hide-ifdef-lines nil
124 "Non-nil means hide the #ifX, #else, and #endif lines."
125 :type 'boolean
126 :group 'hide-ifdef)
127
128 (defcustom hide-ifdef-shadow nil
129 "Non-nil means shadow text instead of hiding it."
130 :type 'boolean
131 :group 'hide-ifdef
132 :version "23.1")
133
134 (defface hide-ifdef-shadow '((t (:inherit shadow)))
135 "Face for shadowing ifdef blocks."
136 :group 'hide-ifdef
137 :version "23.1")
138
139 (defcustom hide-ifdef-exclude-define-regexp nil
140 "Ignore #define names if those names match this exclusion pattern."
141 :type 'string)
142
143 (defvar hide-ifdef-mode-submap
144 ;; Set up the submap that goes after the prefix key.
145 (let ((map (make-sparse-keymap)))
146 (define-key map "d" 'hide-ifdef-define)
147 (define-key map "u" 'hide-ifdef-undef)
148 (define-key map "D" 'hide-ifdef-set-define-alist)
149 (define-key map "U" 'hide-ifdef-use-define-alist)
150
151 (define-key map "h" 'hide-ifdefs)
152 (define-key map "s" 'show-ifdefs)
153 (define-key map "\C-d" 'hide-ifdef-block)
154 (define-key map "\C-s" 'show-ifdef-block)
155
156 (define-key map "\C-q" 'hide-ifdef-toggle-read-only)
157 (define-key map "\C-w" 'hide-ifdef-toggle-shadowing)
158 (substitute-key-definition
159 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map)
160 map)
161 "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.")
162
163 (defconst hide-ifdef-mode-prefix-key "\C-c@"
164 "Prefix key for all Hide-Ifdef mode commands.")
165
166 (defvar hide-ifdef-mode-map
167 ;; Set up the mode's main map, which leads via the prefix key to the submap.
168 (let ((map (make-sparse-keymap)))
169 (define-key map hide-ifdef-mode-prefix-key hide-ifdef-mode-submap)
170 map)
171 "Keymap used with `hide-ifdef-mode'.")
172
173 (easy-menu-define hide-ifdef-mode-menu hide-ifdef-mode-map
174 "Menu for `hide-ifdef-mode'."
175 '("Hide-Ifdef"
176 ["Hide some ifdefs" hide-ifdefs
177 :help "Hide the contents of some #ifdefs"]
178 ["Show all ifdefs" show-ifdefs
179 :help "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs"]
180 ["Hide ifdef block" hide-ifdef-block
181 :help "Hide the ifdef block (true or false part) enclosing or before the cursor"]
182 ["Show ifdef block" show-ifdef-block
183 :help "Show the ifdef block (true or false part) enclosing or before the cursor"]
184 ["Define a variable..." hide-ifdef-define
185 :help "Define a VAR so that #ifdef VAR would be included"]
186 ["Undefine a variable..." hide-ifdef-undef
187 :help "Undefine a VAR so that #ifdef VAR would not be included"]
188 ["Define an alist..." hide-ifdef-set-define-alist
189 :help "Set the association for NAME to `hide-ifdef-env'"]
190 ["Use an alist..." hide-ifdef-use-define-alist
191 :help "Set `hide-ifdef-env' to the define list specified by NAME"]
192 ["Toggle read only" hide-ifdef-toggle-read-only
193 :style toggle :selected hide-ifdef-read-only
194 :help "Buffer should be read-only while hiding text"]
195 ["Toggle shadowing" hide-ifdef-toggle-shadowing
196 :style toggle :selected hide-ifdef-shadow
197 :help "Text should be shadowed instead of hidden"]))
198
199 (defvar hide-ifdef-hiding nil
200 "Non-nil when text may be hidden.")
201
202 (or (assq 'hide-ifdef-hiding minor-mode-alist)
203 (setq minor-mode-alist
204 (cons '(hide-ifdef-hiding " Hiding")
205 minor-mode-alist)))
206
207 ;; Fix c-mode syntax table so we can recognize whole symbols.
208 (defvar hide-ifdef-syntax-table
209 (let ((st (copy-syntax-table c-mode-syntax-table)))
210 (modify-syntax-entry ?_ "w" st)
211 (modify-syntax-entry ?& "." st)
212 (modify-syntax-entry ?\| "." st)
213 st)
214 "Syntax table used for tokenizing #if expressions.")
215
216 (defvar hide-ifdef-env nil
217 "An alist of defined symbols and their values.")
218
219 (defvar hif-outside-read-only nil
220 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
221
222 ;;;###autoload
223 (define-minor-mode hide-ifdef-mode
224 "Toggle features to hide/show #ifdef blocks (Hide-Ifdef mode).
225 With a prefix argument ARG, enable Hide-Ifdef mode if ARG is
226 positive, and disable it otherwise. If called from Lisp, enable
227 the mode if ARG is omitted or nil.
228
229 Hide-Ifdef mode is a buffer-local minor mode for use with C and
230 C-like major modes. When enabled, code within #ifdef constructs
231 that the C preprocessor would eliminate may be hidden from view.
232 Several variables affect how the hiding is done:
233
234 `hide-ifdef-env'
235 An association list of defined and undefined symbols for the
236 current buffer. Initially, the global value of `hide-ifdef-env'
237 is used.
238
239 `hide-ifdef-define-alist'
240 An association list of defined symbol lists.
241 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
242 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
243 from one of the lists in `hide-ifdef-define-alist'.
244
245 `hide-ifdef-lines'
246 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
247 #endif lines when hiding.
248
249 `hide-ifdef-initially'
250 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
251 is activated.
252
253 `hide-ifdef-read-only'
254 Set to non-nil if you want to make buffers read only while hiding.
255 After `show-ifdefs', read-only status is restored to previous value.
256
257 \\{hide-ifdef-mode-map}"
258 :group 'hide-ifdef :lighter " Ifdef"
259 (if hide-ifdef-mode
260 (progn
261 ;; inherit global values
262 (set (make-local-variable 'hide-ifdef-env)
263 (default-value 'hide-ifdef-env))
264 (set (make-local-variable 'hide-ifdef-hiding)
265 (default-value 'hide-ifdef-hiding))
266 (set (make-local-variable 'hif-outside-read-only) buffer-read-only)
267 (set (make-local-variable 'line-move-ignore-invisible) t)
268 (add-hook 'change-major-mode-hook
269 (lambda () (hide-ifdef-mode -1)) nil t)
270
271 (add-to-invisibility-spec '(hide-ifdef . t))
272
273 (if hide-ifdef-initially
274 (hide-ifdefs)
275 (show-ifdefs)))
276 ;; else end hide-ifdef-mode
277 (kill-local-variable 'line-move-ignore-invisible)
278 (remove-from-invisibility-spec '(hide-ifdef . t))
279 (when hide-ifdef-hiding
280 (show-ifdefs))))
281
282
283 (defun hif-show-all ()
284 "Show all of the text in the current buffer."
285 (interactive)
286 (hif-show-ifdef-region (point-min) (point-max)))
287
288 ;; By putting this on after-revert-hook, we arrange that it only
289 ;; does anything when revert-buffer avoids turning off the mode.
290 ;; (That can happen in VC.)
291 (defun hif-after-revert-function ()
292 (and hide-ifdef-mode hide-ifdef-hiding
293 (hide-ifdefs t)))
294 (add-hook 'after-revert-hook 'hif-after-revert-function)
295
296 (defun hif-end-of-line ()
297 (end-of-line)
298 (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
299 (end-of-line 2)))
300
301 (defun hide-ifdef-region-internal (start end)
302 (remove-overlays start end 'hide-ifdef t)
303 (let ((o (make-overlay start end)))
304 (overlay-put o 'hide-ifdef t)
305 (if hide-ifdef-shadow
306 (overlay-put o 'face 'hide-ifdef-shadow)
307 (overlay-put o 'invisible 'hide-ifdef))))
308
309 (defun hide-ifdef-region (start end)
310 "START is the start of a #if or #else form. END is the ending part.
311 Everything including these lines is made invisible."
312 (save-excursion
313 (goto-char start) (hif-end-of-line) (setq start (point))
314 (goto-char end) (hif-end-of-line) (setq end (point))
315 (hide-ifdef-region-internal start end)))
316
317 (defun hif-show-ifdef-region (start end)
318 "Everything between START and END is made visible."
319 (remove-overlays start end 'hide-ifdef t))
320
321
322 ;;===%%SF%% evaluation (Start) ===
323
324 ;; It is not useful to set this to anything but `eval'.
325 ;; In fact, the variable might as well be eliminated.
326 (defvar hide-ifdef-evaluator 'eval
327 "The function to use to evaluate a form.
328 The evaluator is given a canonical form and returns t if text under
329 that form should be displayed.")
330
331 (defvar hif-undefined-symbol nil
332 "...is by default considered to be false.")
333
334
335 (defun hif-set-var (var value)
336 "Prepend (VAR VALUE) pair to `hide-ifdef-env'."
337 (setq hide-ifdef-env (cons (cons var value) hide-ifdef-env)))
338
339 (declare-function semantic-c-hideif-lookup "semantic/bovine/c" (var))
340 (declare-function semantic-c-hideif-defined "semantic/bovine/c" (var))
341
342 (defun hif-lookup (var)
343 (or (when (bound-and-true-p semantic-c-takeover-hideif)
344 (semantic-c-hideif-lookup var))
345 (let ((val (assoc var hide-ifdef-env)))
346 (if val
347 (cdr val)
348 hif-undefined-symbol))))
349
350 (defun hif-defined (var)
351 (cond
352 ((bound-and-true-p semantic-c-takeover-hideif)
353 (semantic-c-hideif-defined var))
354 ((assoc var hide-ifdef-env) 1)
355 (t 0)))
356
357 ;;===%%SF%% evaluation (End) ===
358
359
360
361 ;;===%%SF%% parsing (Start) ===
362 ;;; The code that understands what ifs and ifdef in files look like.
363
364 (defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
365 (defconst hif-ifxdef-regexp (concat hif-cpp-prefix "if\\(n\\)?def"))
366 (defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
367 (defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
368 (defconst hif-elif-regexp (concat hif-cpp-prefix "elif"))
369 (defconst hif-else-regexp (concat hif-cpp-prefix "else"))
370 (defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
371 (defconst hif-ifx-else-endif-regexp
372 (concat hif-ifx-regexp "\\|" hif-elif-regexp "\\|" hif-else-regexp "\\|"
373 hif-endif-regexp))
374 (defconst hif-macro-expr-prefix-regexp
375 (concat hif-cpp-prefix "\\(if\\(n?def\\)?\\|elif\\|define\\)[ \t]+"))
376
377 (defconst hif-white-regexp "[ \t]*")
378 (defconst hif-define-regexp
379 (concat hif-cpp-prefix "\\(define\\|undef\\)"))
380 (defconst hif-id-regexp
381 (concat "[[:alpha:]_][[:alnum:]_]*"))
382 (defconst hif-macroref-regexp
383 (concat hif-white-regexp "\\(" hif-id-regexp "\\)" hif-white-regexp
384 "\\("
385 "(" hif-white-regexp
386 "\\(" hif-id-regexp "\\)?" hif-white-regexp
387 "\\(" "," hif-white-regexp hif-id-regexp hif-white-regexp "\\)*"
388 "\\(\\.\\.\\.\\)?" hif-white-regexp
389 ")"
390 "\\)?" ))
391
392 ;; Store the current token and the whole token list during parsing.
393 ;; Bound dynamically.
394 (defvar hif-token)
395 (defvar hif-token-list)
396
397 (defconst hif-token-alist
398 '(("||" . hif-or)
399 ("&&" . hif-and)
400 ("|" . hif-logior)
401 ("^" . hif-logxor)
402 ("&" . hif-logand)
403 ("<<" . hif-shiftleft)
404 (">>" . hif-shiftright)
405 ("==" . hif-equal)
406 ;; Note: we include tokens like `=' which aren't supported by CPP's
407 ;; expression syntax, because they are still relevant for the tokenizer,
408 ;; especially in conjunction with ##.
409 ("=" . hif-assign)
410 ("!=" . hif-notequal)
411 ("##" . hif-token-concat)
412 ("!" . hif-not)
413 ("~" . hif-lognot)
414 ("(" . hif-lparen)
415 (")" . hif-rparen)
416 (">" . hif-greater)
417 ("<" . hif-less)
418 (">=" . hif-greater-equal)
419 ("<=" . hif-less-equal)
420 ("+" . hif-plus)
421 ("-" . hif-minus)
422 ("*" . hif-multiply)
423 ("/" . hif-divide)
424 ("%" . hif-modulo)
425 ("?" . hif-conditional)
426 (":" . hif-colon)
427 ("," . hif-comma)
428 ("#" . hif-stringify)
429 ("..." . hif-etc)))
430
431 (defconst hif-valid-token-list (mapcar 'cdr hif-token-alist))
432
433 (defconst hif-token-regexp
434 (concat (regexp-opt (mapcar 'car hif-token-alist))
435 "\\|0x[0-9a-fA-F]+\\.?[0-9a-fA-F]*"
436 "\\|[0-9]+\\.?[0-9]*" ;; decimal/octal
437 "\\|\\w+"))
438
439 (defconst hif-string-literal-regexp "\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
440
441 (defun hif-string-to-number (string &optional base)
442 "Like `string-to-number', but it understands non-decimal floats."
443 (if (or (not base) (= base 10))
444 (string-to-number string base)
445 (let* ((parts (split-string string "\\." t "[ \t]+"))
446 (frac (cadr parts))
447 (fraclen (length frac))
448 (quot (expt (if (zerop fraclen)
449 base
450 (* base 1.0)) fraclen)))
451 (/ (string-to-number (concat (car parts) frac) base) quot))))
452
453 ;; The dynamic binding variable `hif-simple-token-only' is shared only by
454 ;; `hif-tokenize' and `hif-find-define'. The purpose is to prevent `hif-tokenize'
455 ;; from returning one more value to indicate a simple token is scanned. This help
456 ;; speeding up macro evaluation on those very simple cases like integers or
457 ;; literals.
458 ;; Check the long comments before `hif-find-define' for more details. [lukelee]
459 (defvar hif-simple-token-only)
460
461 (defun hif-tokenize (start end)
462 "Separate string between START and END into a list of tokens."
463 (let ((token-list nil))
464 (setq hif-simple-token-only t)
465 (with-syntax-table hide-ifdef-syntax-table
466 (save-excursion
467 (goto-char start)
468 (while (progn (forward-comment (point-max)) (< (point) end))
469 ;; (message "expr-start = %d" expr-start) (sit-for 1)
470 (cond
471 ((looking-at "\\\\\n")
472 (forward-char 2))
473
474 ((looking-at hif-string-literal-regexp)
475 (push (substring-no-properties (match-string 1)) token-list)
476 (goto-char (match-end 0)))
477
478 ((looking-at hif-token-regexp)
479 (let ((token (buffer-substring-no-properties
480 (point) (match-end 0))))
481 (goto-char (match-end 0))
482 ;; (message "token: %s" token) (sit-for 1)
483 (push
484 (or (cdr (assoc token hif-token-alist))
485 (if (string-equal token "defined") 'hif-defined)
486 ;; TODO:
487 ;; 1. postfix 'l', 'll', 'ul' and 'ull'
488 ;; 2. floating number formats (like 1.23e4)
489 ;; 3. 098 is interpreted as octal conversion error
490 (if (string-match "0x\\([0-9a-fA-F]+\\.?[0-9a-fA-F]*\\)"
491 token)
492 (hif-string-to-number (match-string 1 token) 16)) ;; hex
493 (if (string-match "\\`0[0-9]+\\(\\.[0-9]+\\)?\\'" token)
494 (hif-string-to-number token 8)) ;; octal
495 (if (string-match "\\`[1-9][0-9]*\\(\\.[0-9]+\\)?\\'"
496 token)
497 (string-to-number token)) ;; decimal
498 (prog1 (intern token)
499 (setq hif-simple-token-only nil)))
500 token-list)))
501
502 (t (error "Bad #if expression: %s" (buffer-string)))))))
503
504 (nreverse token-list)))
505
506 ;;------------------------------------------------------------------------
507 ;; Translate C preprocessor #if expressions using recursive descent.
508 ;; This parser was limited to the operators &&, ||, !, and "defined".
509 ;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
510 ;;
511 ;; Implement the C language operator precedence table. Add all those
512 ;; missing operators that could be used in macros. Luke Lee 2013-09-04
513
514 ;; | Operator Type | Operator | Associativity |
515 ;; +----------------------+-----------------------------+---------------+
516 ;; | Primary Expression | () [] . -> expr++ expr-- | left-to-right |
517 ;; | Unary Operators | * & + - ! ~ ++expr --expr | right-to-left |
518 ;; | | (typecast) sizeof | |
519 ;; | Binary Operators | * / % | left-to-right |
520 ;; | | + - | |
521 ;; | | >> << | |
522 ;; | | < > <= >= | |
523 ;; | | == != | |
524 ;; | | & | |
525 ;; | | ^ | |
526 ;; | | | | |
527 ;; | | && | |
528 ;; | | || | |
529 ;; | Ternary Operator | ?: | right-to-left |
530 ;; x| Assignment Operators | = += -= *= /= %= >>= <<= &= | right-to-left |
531 ;; | | ^= = | |
532 ;; | Comma | , | left-to-right |
533
534 (defsubst hif-nexttoken ()
535 "Pop the next token from token-list into the let variable `hif-token'."
536 (setq hif-token (pop hif-token-list)))
537
538 (defsubst hif-if-valid-identifier-p (id)
539 (not (or (numberp id)
540 (stringp id))))
541
542 (defun hif-define-operator (tokens)
543 "`Upgrade' hif-define xxx to '(hif-define xxx)' so it won't be subsitituted."
544 (let ((result nil)
545 (tok nil))
546 (while (setq tok (pop tokens))
547 (push
548 (if (eq tok 'hif-defined)
549 (progn
550 (setq tok (cadr tokens))
551 (if (eq (car tokens) 'hif-lparen)
552 (if (and (hif-if-valid-identifier-p tok)
553 (eq (cl-caddr tokens) 'hif-rparen))
554 (setq tokens (cl-cdddr tokens))
555 (error "#define followed by non-identifier: %S" tok))
556 (setq tok (car tokens)
557 tokens (cdr tokens))
558 (unless (hif-if-valid-identifier-p tok)
559 (error "#define followed by non-identifier: %S" tok)))
560 (list 'hif-defined 'hif-lparen tok 'hif-rparen))
561 tok)
562 result))
563 (nreverse result)))
564
565 (defun hif-flatten (l)
566 "Flatten a tree."
567 (apply #'nconc
568 (mapcar (lambda (x) (if (listp x)
569 (hif-flatten x)
570 (list x))) l)))
571
572 (defun hif-expand-token-list (tokens &optional macroname expand_list)
573 "Perform expansion on TOKENS till everything expanded.
574 Self-reference (directly or indirectly) tokens are not expanded.
575 EXPAND_LIST is the list of macro names currently being expanded, use for
576 detecting self-reference."
577 (catch 'self-referencing
578 (let ((expanded nil)
579 (remains (hif-define-operator
580 (hif-token-concatenation
581 (hif-token-stringification tokens))))
582 tok rep)
583 (if macroname
584 (setq expand_list (cons macroname expand_list)))
585 ;; Expanding all tokens till list exhausted
586 (while (setq tok (pop remains))
587 (if (memq tok expand_list)
588 ;; For self-referencing tokens, don't expand it
589 (throw 'self-referencing tokens))
590 (push
591 (cond
592 ((or (memq tok hif-valid-token-list)
593 (numberp tok)
594 (stringp tok))
595 tok)
596
597 ((setq rep (hif-lookup tok))
598 (if (and (listp rep)
599 (eq (car rep) 'hif-define-macro)) ; A defined macro
600 ;; Recursively expand it
601 (if (cadr rep) ; Argument list is not nil
602 (if (not (eq (car remains) 'hif-lparen))
603 ;; No argument, no invocation
604 tok
605 ;; Argumented macro, get arguments and invoke it.
606 ;; Dynamically bind hif-token-list and hif-token
607 ;; for hif-macro-supply-arguments
608 (let* ((hif-token-list (cdr remains))
609 (hif-token nil)
610 (parmlist (mapcar #'hif-expand-token-list
611 (hif-get-argument-list)))
612 (result
613 (hif-expand-token-list
614 (hif-macro-supply-arguments tok parmlist)
615 tok expand_list)))
616 (setq remains (cons hif-token hif-token-list))
617 result))
618 ;; Argument list is nil, direct expansion
619 (setq rep (hif-expand-token-list
620 (cl-caddr rep) ; Macro's token list
621 tok expand_list))
622 ;; Replace all remaining references immediately
623 (setq remains (cl-substitute tok rep remains))
624 rep)
625 ;; Lookup tok returns an atom
626 rep))
627
628 ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
629 ;; this token might results in an incomplete expression that
630 ;; cannot be parsed further.
631 ;;((= 1 (hif-defined tok)) ; defined (hif-defined tok)=1,
632 ;; ;;but empty (hif-lookup tok)=nil, thus remove this token
633 ;; (setq remains (delete tok remains))
634 ;; nil)
635
636 (t ; Usual IDs
637 tok))
638
639 expanded))
640
641 (hif-flatten (nreverse expanded)))))
642
643 (defun hif-parse-exp (token-list &optional macroname)
644 "Parse the TOKEN-LIST.
645 Return translated list in prefix form. MACRONAME is applied when invoking
646 macros to prevent self-reference."
647 (let ((hif-token-list (hif-expand-token-list token-list macroname)))
648 (hif-nexttoken)
649 (prog1
650 (and hif-token
651 (hif-exprlist))
652 (if hif-token ; is there still a token?
653 (error "Error: unexpected token: %s" hif-token)))))
654
655 (defun hif-exprlist ()
656 "Parse an exprlist: expr { ',' expr}."
657 (let ((result (hif-expr)))
658 (if (eq hif-token 'hif-comma)
659 (let ((temp (list result)))
660 (while
661 (progn
662 (hif-nexttoken)
663 (push (hif-expr) temp)
664 (eq hif-token 'hif-comma)))
665 (cons 'hif-comma (nreverse temp)))
666 result)))
667
668 (defun hif-expr ()
669 "Parse an expression as found in #if.
670 expr : or-expr | or-expr '?' expr ':' expr."
671 (let ((result (hif-or-expr))
672 middle)
673 (while (eq hif-token 'hif-conditional)
674 (hif-nexttoken)
675 (setq middle (hif-expr))
676 (if (eq hif-token 'hif-colon)
677 (progn
678 (hif-nexttoken)
679 (setq result (list 'hif-conditional result middle (hif-expr))))
680 (error "Error: unexpected token: %s" hif-token)))
681 result))
682
683 (defun hif-or-expr ()
684 "Parse an or-expr : and-expr | or-expr '||' and-expr."
685 (let ((result (hif-and-expr)))
686 (while (eq hif-token 'hif-or)
687 (hif-nexttoken)
688 (setq result (list 'hif-or result (hif-and-expr))))
689 result))
690
691 (defun hif-and-expr ()
692 "Parse an and-expr : logior-expr | and-expr '&&' logior-expr."
693 (let ((result (hif-logior-expr)))
694 (while (eq hif-token 'hif-and)
695 (hif-nexttoken)
696 (setq result (list 'hif-and result (hif-logior-expr))))
697 result))
698
699 (defun hif-logior-expr ()
700 "Parse a logor-expr : logxor-expr | logor-expr '|' logxor-expr."
701 (let ((result (hif-logxor-expr)))
702 (while (eq hif-token 'hif-logior)
703 (hif-nexttoken)
704 (setq result (list 'hif-logior result (hif-logxor-expr))))
705 result))
706
707 (defun hif-logxor-expr ()
708 "Parse a logxor-expr : logand-expr | logxor-expr '^' logand-expr."
709 (let ((result (hif-logand-expr)))
710 (while (eq hif-token 'hif-logxor)
711 (hif-nexttoken)
712 (setq result (list 'hif-logxor result (hif-logand-expr))))
713 result))
714
715 (defun hif-logand-expr ()
716 "Parse a logand-expr : eq-expr | logand-expr '&' eq-expr."
717 (let ((result (hif-eq-expr)))
718 (while (eq hif-token 'hif-logand)
719 (hif-nexttoken)
720 (setq result (list 'hif-logand result (hif-eq-expr))))
721 result))
722
723 (defun hif-eq-expr ()
724 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
725 (let ((result (hif-comp-expr))
726 (eq-token nil))
727 (while (memq hif-token '(hif-equal hif-notequal))
728 (setq eq-token hif-token)
729 (hif-nexttoken)
730 (setq result (list eq-token result (hif-comp-expr))))
731 result))
732
733 (defun hif-comp-expr ()
734 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
735 (let ((result (hif-logshift-expr))
736 (comp-token nil))
737 (while (memq hif-token '(hif-greater hif-less hif-greater-equal
738 hif-less-equal))
739 (setq comp-token hif-token)
740 (hif-nexttoken)
741 (setq result (list comp-token result (hif-logshift-expr))))
742 result))
743
744 (defun hif-logshift-expr ()
745 "Parse a logshift : math | logshift `<<'|`>>' math."
746 (let ((result (hif-math))
747 (shift-token nil))
748 (while (memq hif-token '(hif-shiftleft hif-shiftright))
749 (setq shift-token hif-token)
750 (hif-nexttoken)
751 (setq result (list shift-token result (hif-math))))
752 result))
753
754 (defun hif-math ()
755 "Parse an expression with + or -.
756 math : muldiv | math '+|-' muldiv."
757 (let ((result (hif-muldiv-expr))
758 (math-op nil))
759 (while (memq hif-token '(hif-plus hif-minus))
760 (setq math-op hif-token)
761 (hif-nexttoken)
762 (setq result (list math-op result (hif-muldiv-expr))))
763 result))
764
765 (defun hif-muldiv-expr ()
766 "Parse an expression with *,/,%.
767 muldiv : factor | muldiv '*|/|%' factor."
768 (let ((result (hif-factor))
769 (math-op nil))
770 (while (memq hif-token '(hif-multiply hif-divide hif-modulo))
771 (setq math-op hif-token)
772 (hif-nexttoken)
773 (setq result (list math-op result (hif-factor))))
774 result))
775
776 (defun hif-factor ()
777 "Parse a factor.
778 factor : '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' |
779 'id(parmlist)' | strings | id."
780 (cond
781 ((eq hif-token 'hif-not)
782 (hif-nexttoken)
783 (list 'hif-not (hif-factor)))
784
785 ((eq hif-token 'hif-lognot)
786 (hif-nexttoken)
787 (list 'hif-lognot (hif-factor)))
788
789 ((eq hif-token 'hif-lparen)
790 (hif-nexttoken)
791 (let ((result (hif-exprlist)))
792 (if (not (eq hif-token 'hif-rparen))
793 (error "Bad token in parenthesized expression: %s" hif-token)
794 (hif-nexttoken)
795 result)))
796
797 ((eq hif-token 'hif-defined)
798 (hif-nexttoken)
799 (let ((paren (when (eq hif-token 'hif-lparen) (hif-nexttoken) t))
800 (ident hif-token))
801 (if (memq hif-token '(or and not hif-defined hif-lparen hif-rparen))
802 (error "Error: unexpected token: %s" hif-token))
803 (when paren
804 (hif-nexttoken)
805 (unless (eq hif-token 'hif-rparen)
806 (error "Error: expected \")\" after identifier")))
807 (hif-nexttoken)
808 `(hif-defined (quote ,ident))))
809
810 ((numberp hif-token)
811 (prog1 hif-token (hif-nexttoken)))
812 ((stringp hif-token)
813 (hif-string-concatenation))
814
815 ;; Unary plus/minus.
816 ((memq hif-token '(hif-minus hif-plus))
817 (list (prog1 hif-token (hif-nexttoken)) 0 (hif-factor)))
818
819 (t ; identifier
820 (let ((ident hif-token))
821 (hif-nexttoken)
822 (if (eq hif-token 'hif-lparen)
823 (hif-place-macro-invocation ident)
824 `(hif-lookup (quote ,ident)))))))
825
826 (defun hif-get-argument-list ()
827 (let ((nest 0)
828 (parmlist nil) ; A "token" list of parameters, will later be parsed
829 (parm nil))
830
831 (while (or (not (eq (hif-nexttoken) 'hif-rparen))
832 (/= nest 0))
833 (if (eq (car (last parm)) 'hif-comma)
834 (setq parm nil))
835 (cond
836 ((eq hif-token 'hif-lparen)
837 (setq nest (1+ nest)))
838 ((eq hif-token 'hif-rparen)
839 (setq nest (1- nest)))
840 ((and (eq hif-token 'hif-comma)
841 (= nest 0))
842 (push (nreverse parm) parmlist)
843 (setq parm nil)))
844 (push hif-token parm))
845
846 (push (nreverse parm) parmlist) ; Okay even if PARM is nil
847 (hif-nexttoken) ; Drop the `hif-rparen', get next token
848 (nreverse parmlist)))
849
850 (defun hif-place-macro-invocation (ident)
851 (let ((parmlist (hif-get-argument-list)))
852 `(hif-invoke (quote ,ident) (quote ,parmlist))))
853
854 (defun hif-string-concatenation ()
855 "Parse concatenated strings: string | strings string."
856 (let ((result (substring-no-properties hif-token)))
857 (while (stringp (hif-nexttoken))
858 (setq result (concat
859 (substring result 0 -1) ; remove trailing '"'
860 (substring hif-token 1)))) ; remove leading '"'
861 result))
862
863 (defun hif-define-macro (_parmlist _token-body)
864 "A marker for defined macro with arguments.
865 This macro cannot be evaluated alone without parameters inputed."
866 ;;TODO: input arguments at run time, use minibuffer to query all arguments
867 (error
868 "Argumented macro cannot be evaluated without passing any parameter"))
869
870 (defun hif-stringify (a)
871 "Stringify a number, string or symbol."
872 (cond
873 ((numberp a)
874 (number-to-string a))
875 ((atom a)
876 (symbol-name a))
877 ((stringp a)
878 (concat "\"" a "\""))
879 (t
880 (error "Invalid token to stringify"))))
881
882 (defun intern-safe (str)
883 (if (stringp str)
884 (intern str)))
885
886 (defun hif-token-concat (a b)
887 "Concatenate two tokens into a longer token.
888 Currently support only simple token concatenation. Also support weird (but
889 valid) token concatenation like '>' ## '>' becomes '>>'. Here we take care only
890 those that can be evaluated during preprocessing time and ignore all those that
891 can only be evaluated at C(++) runtime (like '++', '--' and '+='...)."
892 (if (or (memq a hif-valid-token-list)
893 (memq b hif-valid-token-list))
894 (let* ((ra (car (rassq a hif-token-alist)))
895 (rb (car (rassq b hif-token-alist)))
896 (result (and ra rb
897 (cdr (assoc (concat ra rb) hif-token-alist)))))
898 (or result
899 ;;(error "Invalid token to concatenate")
900 (error "Concatenating \"%s\" and \"%s\" does not give a valid \
901 preprocessing token"
902 (or ra (symbol-name a))
903 (or rb (symbol-name b)))))
904 (intern-safe (concat (hif-stringify a)
905 (hif-stringify b)))))
906
907 (defun hif-mathify (val)
908 "Treat VAL as a number: if it's t or nil, use 1 or 0."
909 (cond ((eq val t) 1)
910 ((null val) 0)
911 (t val)))
912
913 (defun hif-conditional (a b c)
914 (if (not (zerop (hif-mathify a))) (hif-mathify b) (hif-mathify c)))
915 (defun hif-and (a b)
916 (and (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
917 (defun hif-or (a b)
918 (or (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
919 (defun hif-not (a)
920 (zerop (hif-mathify a)))
921 (defun hif-lognot (a)
922 (lognot (hif-mathify a)))
923
924 (defmacro hif-mathify-binop (fun)
925 `(lambda (a b)
926 ,(format "Like `%s' but treat t and nil as 1 and 0." fun)
927 (,fun (hif-mathify a) (hif-mathify b))))
928
929 (defun hif-shiftleft (a b)
930 (setq a (hif-mathify a))
931 (setq b (hif-mathify b))
932 (if (< a 0)
933 (ash a b)
934 (lsh a b)))
935
936 (defun hif-shiftright (a b)
937 (setq a (hif-mathify a))
938 (setq b (hif-mathify b))
939 (if (< a 0)
940 (ash a (- b))
941 (lsh a (- b))))
942
943
944 (defalias 'hif-multiply (hif-mathify-binop *))
945 (defalias 'hif-divide (hif-mathify-binop /))
946 (defalias 'hif-modulo (hif-mathify-binop %))
947 (defalias 'hif-plus (hif-mathify-binop +))
948 (defalias 'hif-minus (hif-mathify-binop -))
949 (defalias 'hif-equal (hif-mathify-binop =))
950 (defalias 'hif-notequal (hif-mathify-binop /=))
951 (defalias 'hif-greater (hif-mathify-binop >))
952 (defalias 'hif-less (hif-mathify-binop <))
953 (defalias 'hif-greater-equal (hif-mathify-binop >=))
954 (defalias 'hif-less-equal (hif-mathify-binop <=))
955 (defalias 'hif-logior (hif-mathify-binop logior))
956 (defalias 'hif-logxor (hif-mathify-binop logxor))
957 (defalias 'hif-logand (hif-mathify-binop logand))
958
959
960 (defun hif-comma (&rest expr)
961 "Evaluate a list of EXPR, return the result of the last item."
962 (let ((result nil))
963 (dolist (e expr)
964 (ignore-errors
965 (setq result (funcall hide-ifdef-evaluator e))))
966 result))
967
968 (defun hif-token-stringification (l)
969 "Scan token list for `hif-stringify' ('#') token and stringify the next token."
970 (let (result)
971 (while l
972 (push (if (eq (car l) 'hif-stringify)
973 (prog1
974 (if (cadr l)
975 (hif-stringify (cadr l))
976 (error "No token to stringify"))
977 (setq l (cdr l)))
978 (car l))
979 result)
980 (setq l (cdr l)))
981 (nreverse result)))
982
983 (defun hif-token-concatenation (l)
984 "Scan token list for `hif-token-concat' ('##') token and concatenate two tokens."
985 (let ((prev nil)
986 result)
987 (while l
988 (while (eq (car l) 'hif-token-concat)
989 (unless prev
990 (error "No token before ## to concatenate"))
991 (unless (cdr l)
992 (error "No token after ## to concatenate"))
993 (setq prev (hif-token-concat prev (cadr l)))
994 (setq l (cddr l)))
995 (if prev
996 (setq result (append result (list prev))))
997 (setq prev (car l)
998 l (cdr l)))
999 (if prev
1000 (append result (list prev))
1001 result)))
1002
1003 (defun hif-delimit (lis atom)
1004 (nconc (cl-mapcan (lambda (l) (list l atom))
1005 (butlast lis))
1006 (last lis)))
1007
1008 ;; Perform token replacement:
1009 (defun hif-macro-supply-arguments (macro-name actual-parms)
1010 "Expand a macro call, replace ACTUAL-PARMS in the macro body."
1011 (let* ((SA (assoc macro-name hide-ifdef-env))
1012 (macro (and SA
1013 (cdr SA)
1014 (eq (cadr SA) 'hif-define-macro)
1015 (cddr SA)))
1016 (formal-parms (and macro (car macro)))
1017 (macro-body (and macro (cadr macro)))
1018 actual-count
1019 formal-count
1020 formal
1021 etc)
1022
1023 (when (and actual-parms formal-parms macro-body)
1024 ;; For each actual parameter, evaluate each one and associate it
1025 ;; with an actual parameter, put it into local table and finally
1026 ;; evaluate the macro body.
1027 (if (setq etc (eq (car formal-parms) 'hif-etc))
1028 ;; Take care of `hif-etc' first. Prefix `hif-comma' back if needed.
1029 (setq formal-parms (cdr formal-parms)))
1030 (setq formal-count (length formal-parms)
1031 actual-count (length actual-parms))
1032
1033 (if (> formal-count actual-count)
1034 (error "Too few parmameter for macro %S" macro-name)
1035 (if (< formal-count actual-count)
1036 (or etc
1037 (error "Too many parameters for macro %S" macro-name))))
1038
1039 ;; Perform token replacement on the MACRO-BODY with the parameters
1040 (while (setq formal (pop formal-parms))
1041 ;; Prevent repetitive substitutation, thus cannot use `subst'
1042 ;; for example:
1043 ;; #define mac(a,b) (a+b)
1044 ;; #define testmac mac(b,y)
1045 ;; testmac should expand to (b+y): replace of argument a and b
1046 ;; occurs simultaneously, not sequentially. If sequentially,
1047 ;; according to the argument order, it will become:
1048 ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
1049 ;; becomes (b+b)
1050 ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
1051 ;; becomes (y+y).
1052 (setq macro-body
1053 ;; Unlike `subst', `substitute' replace only the top level
1054 ;; instead of the whole tree; more importantly, it's not
1055 ;; destructive.
1056 (cl-substitute (if (and etc (null formal-parms))
1057 (hif-delimit actual-parms 'hif-comma)
1058 (car actual-parms))
1059 formal macro-body))
1060 (setq actual-parms (cdr actual-parms)))
1061
1062 ;; Replacement completed, flatten the whole token list
1063 (setq macro-body (hif-flatten macro-body))
1064
1065 ;; Stringification and token concatenation happens here
1066 (hif-token-concatenation (hif-token-stringification macro-body)))))
1067
1068 (defun hif-invoke (macro-name actual-parms)
1069 "Invoke a macro by expanding it, reparse macro-body and finally invoke it."
1070 ;; Reparse the macro body and evaluate it
1071 (funcall hide-ifdef-evaluator
1072 (hif-parse-exp
1073 (hif-macro-supply-arguments macro-name actual-parms)
1074 macro-name)))
1075
1076 ;;;----------- end of parser -----------------------
1077
1078
1079 (defun hif-canonicalize-tokens (regexp) ; For debugging
1080 "Return the expanded result of the scanned tokens."
1081 (save-excursion
1082 (re-search-forward regexp)
1083 (let* ((curr-regexp (match-string 0))
1084 (defined (string-match hif-ifxdef-regexp curr-regexp))
1085 (negate (and defined
1086 (string= (match-string 2 curr-regexp) "n")))
1087 (hif-simple-token-only nil) ;; Dynamic binding var for `hif-tokenize'
1088 (tokens (hif-tokenize (point)
1089 (progn (hif-end-of-line) (point)))))
1090 (if defined
1091 (setq tokens (list 'hif-defined tokens)))
1092 (if negate
1093 (setq tokens (list 'hif-not tokens)))
1094 tokens)))
1095
1096 (defun hif-canonicalize (regexp)
1097 "Return a Lisp expression for its condition by scanning current buffer.
1098 Do this when cursor is at the beginning of `regexp' (i.e. #ifX)."
1099 (let ((case-fold-search nil))
1100 (save-excursion
1101 (re-search-forward regexp)
1102 (let* ((curr-regexp (match-string 0))
1103 (defined (string-match hif-ifxdef-regexp curr-regexp))
1104 (negate (and defined
1105 (string= (match-string 2 curr-regexp) "n")))
1106 (hif-simple-token-only nil) ; Dynamic binding for `hif-tokenize'
1107 (tokens (hif-tokenize (point)
1108 (progn (hif-end-of-line) (point)))))
1109 (if defined
1110 (setq tokens (list 'hif-defined tokens)))
1111 (if negate
1112 (setq tokens (list 'hif-not tokens)))
1113 (hif-parse-exp tokens)))))
1114
1115 (defun hif-find-any-ifX ()
1116 "Move to next #if..., or #ifndef, at point or after."
1117 ;; (message "find ifX at %d" (point))
1118 (prog1
1119 (re-search-forward hif-ifx-regexp (point-max) t)
1120 (beginning-of-line)))
1121
1122
1123 (defun hif-find-next-relevant ()
1124 "Move to next #if..., #elif..., #else, or #endif, after the current line."
1125 ;; (message "hif-find-next-relevant at %d" (point))
1126 (end-of-line)
1127 ;; Avoid infinite recursion by only going to line-beginning if match found
1128 (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
1129 (beginning-of-line)))
1130
1131 (defun hif-find-previous-relevant ()
1132 "Move to previous #if..., #else, or #endif, before the current line."
1133 ;; (message "hif-find-previous-relevant at %d" (point))
1134 (beginning-of-line)
1135 ;; Avoid infinite recursion by only going to line-beginning if match found
1136 (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
1137 (beginning-of-line)))
1138
1139
1140 (defun hif-looking-at-ifX ()
1141 (looking-at hif-ifx-regexp)) ; Should eventually see #if
1142 (defun hif-looking-at-endif ()
1143 (looking-at hif-endif-regexp))
1144 (defun hif-looking-at-else ()
1145 (looking-at hif-else-regexp))
1146
1147 (defun hif-looking-at-elif ()
1148 (looking-at hif-elif-regexp))
1149
1150
1151 (defun hif-ifdef-to-endif ()
1152 "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
1153 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
1154 (hif-find-next-relevant)
1155 (cond ((hif-looking-at-ifX)
1156 (hif-ifdef-to-endif) ; Find endif of nested if
1157 (hif-ifdef-to-endif)) ; Find outer endif or else
1158 ((hif-looking-at-elif)
1159 (hif-ifdef-to-endif))
1160 ((hif-looking-at-else)
1161 (hif-ifdef-to-endif)) ; Find endif following else
1162 ((hif-looking-at-endif)
1163 'done)
1164 (t
1165 (error "Mismatched #ifdef #endif pair"))))
1166
1167
1168 (defun hif-endif-to-ifdef ()
1169 "If positioned at #endif form, skip backward to corresponding #ifX."
1170 ;; (message "hif-endif-to-ifdef at %d" (point))
1171 (let ((start (point)))
1172 (hif-find-previous-relevant)
1173 (if (= start (point))
1174 (error "Mismatched #ifdef #endif pair")))
1175 (cond ((hif-looking-at-endif)
1176 (hif-endif-to-ifdef) ; find beginning of nested if
1177 (hif-endif-to-ifdef)) ; find beginning of outer if or else
1178 ((hif-looking-at-else)
1179 (hif-endif-to-ifdef))
1180 ((hif-looking-at-ifX)
1181 'done)
1182 (t))) ; never gets here
1183
1184
1185 (defun forward-ifdef (&optional arg)
1186 "Move point to beginning of line of the next ifdef-endif.
1187 With argument, do this that many times."
1188 (interactive "p")
1189 (or arg (setq arg 1))
1190 (if (< arg 0) (backward-ifdef (- arg))
1191 (while (< 0 arg)
1192 (setq arg (- arg))
1193 (let ((start (point)))
1194 (unless (hif-looking-at-ifX)
1195 (hif-find-next-relevant))
1196 (if (hif-looking-at-ifX)
1197 (hif-ifdef-to-endif)
1198 (goto-char start)
1199 (error "No following #ifdef"))))))
1200
1201
1202 (defun backward-ifdef (&optional arg)
1203 "Move point to beginning of the previous ifdef-endif.
1204 With argument, do this that many times."
1205 (interactive "p")
1206 (or arg (setq arg 1))
1207 (if (< arg 0) (forward-ifdef (- arg))
1208 (while (< 0 arg)
1209 (setq arg (1- arg))
1210 (beginning-of-line)
1211 (let ((start (point)))
1212 (unless (hif-looking-at-endif)
1213 (hif-find-previous-relevant))
1214 (if (hif-looking-at-endif)
1215 (hif-endif-to-ifdef)
1216 (goto-char start)
1217 (error "No previous #ifdef"))))))
1218
1219
1220 (defun down-ifdef ()
1221 "Move point to beginning of nested ifdef or else-part."
1222 (interactive)
1223 (let ((start (point)))
1224 (hif-find-next-relevant)
1225 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
1226 ()
1227 (goto-char start)
1228 (error "No following #ifdef"))))
1229
1230
1231 (defun up-ifdef ()
1232 "Move point to beginning of enclosing ifdef or else-part."
1233 (interactive)
1234 (beginning-of-line)
1235 (let ((start (point)))
1236 (unless (hif-looking-at-endif)
1237 (hif-find-previous-relevant))
1238 (if (hif-looking-at-endif)
1239 (hif-endif-to-ifdef))
1240 (if (= start (point))
1241 (error "No previous #ifdef"))))
1242
1243 (defun next-ifdef (&optional arg)
1244 "Move to the beginning of the next #ifX, #else, or #endif.
1245 With argument, do this that many times."
1246 (interactive "p")
1247 (or arg (setq arg 1))
1248 (if (< arg 0) (previous-ifdef (- arg))
1249 (while (< 0 arg)
1250 (setq arg (1- arg))
1251 (hif-find-next-relevant)
1252 (when (eolp)
1253 (beginning-of-line)
1254 (error "No following #ifdefs, #elses, or #endifs")))))
1255
1256 (defun previous-ifdef (&optional arg)
1257 "Move to the beginning of the previous #ifX, #else, or #endif.
1258 With argument, do this that many times."
1259 (interactive "p")
1260 (or arg (setq arg 1))
1261 (if (< arg 0) (next-ifdef (- arg))
1262 (while (< 0 arg)
1263 (setq arg (1- arg))
1264 (let ((start (point)))
1265 (hif-find-previous-relevant)
1266 (if (= start (point))
1267 (error "No previous #ifdefs, #elses, or #endifs"))))))
1268
1269
1270 ;;===%%SF%% parsing (End) ===
1271
1272
1273 ;;===%%SF%% hide-ifdef-hiding (Start) ===
1274
1275
1276 ;;; A range is a structure with four components:
1277 ;;; ELSE-P True if there was an else clause for the ifdef.
1278 ;;; START The start of the range. (beginning of line)
1279 ;;; ELSE The else marker (beginning of line)
1280 ;;; Only valid if ELSE-P is true.
1281 ;;; END The end of the range. (beginning of line)
1282
1283 (defsubst hif-make-range (start end &optional else)
1284 (list start else end))
1285
1286 (defsubst hif-range-start (range) (elt range 0))
1287 (defsubst hif-range-else (range) (elt range 1))
1288 (defsubst hif-range-end (range) (elt range 2))
1289
1290
1291
1292 ;;; Find-Range
1293 ;;; The workhorse, it delimits the #if region. Reasonably simple:
1294 ;;; Skip until an #else or #endif is found, remembering positions. If
1295 ;;; an #else was found, skip some more, looking for the true #endif.
1296
1297 (defun hif-find-range ()
1298 "Return a Range structure describing the current #if region.
1299 Point is left unchanged."
1300 ;; (message "hif-find-range at %d" (point))
1301 (save-excursion
1302 (beginning-of-line)
1303 (let ((start (point))
1304 (else nil)
1305 (end nil))
1306 ;; Part one. Look for either #endif or #else.
1307 ;; This loop-and-a-half dedicated to E. Dijkstra.
1308 (while (progn
1309 (hif-find-next-relevant)
1310 (hif-looking-at-ifX)) ; Skip nested ifdef
1311 (hif-ifdef-to-endif))
1312 ;; Found either a #else or an #endif.
1313 (cond ((hif-looking-at-else)
1314 (setq else (point)))
1315 (t
1316 (setq end (point)))) ; (line-end-position)
1317 ;; If found #else, look for #endif.
1318 (when else
1319 (while (progn
1320 (hif-find-next-relevant)
1321 (hif-looking-at-ifX)) ; Skip nested ifdef
1322 (hif-ifdef-to-endif))
1323 (if (hif-looking-at-else)
1324 (error "Found two elses in a row? Broken!"))
1325 (setq end (point))) ; (line-end-position)
1326 (hif-make-range start end else))))
1327
1328
1329 ;; A bit slimy.
1330
1331 (defun hif-hide-line (point)
1332 "Hide the line containing point.
1333 Does nothing if `hide-ifdef-lines' is nil."
1334 (when hide-ifdef-lines
1335 (save-excursion
1336 (goto-char point)
1337 (hide-ifdef-region-internal
1338 (line-beginning-position) (progn (hif-end-of-line) (point))))))
1339
1340
1341 ;;; Hif-Possibly-Hide
1342 ;;; There are four cases. The #ifX expression is "taken" if it
1343 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
1344 ;;; inside the #ifdef would be included when the program was
1345 ;;; compiled.
1346 ;;;
1347 ;;; Case 1: #ifX taken, and there's an #else.
1348 ;;; The #else part must be hidden. The #if (then) part must be
1349 ;;; processed for nested #ifX's.
1350 ;;; Case 2: #ifX taken, and there's no #else.
1351 ;;; The #if part must be processed for nested #ifX's.
1352 ;;; Case 3: #ifX not taken, and there's an #else.
1353 ;;; The #if part must be hidden. The #else part must be processed
1354 ;;; for nested #ifs.
1355 ;;; Case 4: #ifX not taken, and there's no #else.
1356 ;;; The #ifX part must be hidden.
1357 ;;;
1358 ;;; Further processing is done by narrowing to the relevant region
1359 ;;; and just recursively calling hide-ifdef-guts.
1360 ;;;
1361 ;;; When hif-possibly-hide returns, point is at the end of the
1362 ;;; possibly-hidden range.
1363
1364 (defun hif-recurse-on (start end)
1365 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
1366 (save-excursion
1367 (save-restriction
1368 (goto-char start)
1369 (end-of-line)
1370 (narrow-to-region (point) end)
1371 (hide-ifdef-guts))))
1372
1373 (defun hif-possibly-hide ()
1374 "Called at #ifX expression, this hides those parts that should be hidden.
1375 It uses the judgment of `hide-ifdef-evaluator'."
1376 ;; (message "hif-possibly-hide") (sit-for 1)
1377 (let ((test (hif-canonicalize hif-ifx-regexp))
1378 (range (hif-find-range)))
1379 ;; (message "test = %s" test) (sit-for 1)
1380
1381 (hif-hide-line (hif-range-end range))
1382 (if (not (hif-not (funcall hide-ifdef-evaluator test)))
1383 (cond ((hif-range-else range) ; case 1
1384 (hif-hide-line (hif-range-else range))
1385 (hide-ifdef-region (hif-range-else range)
1386 (1- (hif-range-end range)))
1387 (hif-recurse-on (hif-range-start range)
1388 (hif-range-else range)))
1389 (t ; case 2
1390 (hif-recurse-on (hif-range-start range)
1391 (hif-range-end range))))
1392 (cond ((hif-range-else range) ; case 3
1393 (hif-hide-line (hif-range-else range))
1394 (hide-ifdef-region (hif-range-start range)
1395 (1- (hif-range-else range)))
1396 (hif-recurse-on (hif-range-else range)
1397 (hif-range-end range)))
1398 (t ; case 4
1399 (hide-ifdef-region (point)
1400 (1- (hif-range-end range))))))
1401 (hif-hide-line (hif-range-start range)) ; Always hide start.
1402 (goto-char (hif-range-end range))
1403 (end-of-line)))
1404
1405 (defun hif-parse-macro-arglist (str)
1406 "Parse argument list formatted as '( arg1 [ , argn] [...] )'.
1407 The '...' is also included. Return a list of the arguments, if '...' exists the
1408 first arg will be `hif-etc'."
1409 (let* ((hif-simple-token-only nil) ; Dynamic binding var for `hif-tokenize'
1410 (tokenlist
1411 (cdr (hif-tokenize
1412 (- (point) (length str)) (point)))) ; Remove `hif-lparen'
1413 etc result token)
1414 (while (not (eq (setq token (pop tokenlist)) 'hif-rparen))
1415 (cond
1416 ((eq token 'hif-etc)
1417 (setq etc t))
1418 ((eq token 'hif-comma)
1419 t)
1420 (t
1421 (push token result))))
1422 (if etc
1423 (cons 'hif-etc (nreverse result))
1424 (nreverse result))))
1425
1426 ;; The original version of hideif evaluates the macro early and store the
1427 ;; final values for the defined macro into the symbol database (aka
1428 ;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
1429 ;; tree -> [value]". (The square bracket refers to what's stored in in our
1430 ;; `hide-ifdef-env'.)
1431 ;;
1432 ;; This forbids the evaluation of an argumented macro since the parameters
1433 ;; are applied at run time. In order to support argumented macro I then
1434 ;; postponed the evaluation process one stage and store the "parsed tree"
1435 ;; into symbol database. The evaluation process was then "strings -> tokens
1436 ;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
1437 ;; evaluate the parsed tree everytime when trying to expand the symbol. These
1438 ;; temporarily code changes are obsolete and not in Emacs source repository.
1439 ;;
1440 ;; Furthermore, CPP did allow partial expression to be defined in several
1441 ;; macros and later got concatenated into a complete expression and then
1442 ;; evaluate it. In order to match this behavior I had to postpone one stage
1443 ;; further, otherwise those partial expression will be fail on parsing and
1444 ;; we'll miss all macros that reference it. The evaluation process thus
1445 ;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
1446 ;; performance since we need to parse tokens and evaluate them everytime
1447 ;; when that symbol is referenced.
1448 ;;
1449 ;; In real cases I found a lot portion of macros are "simple macros" that
1450 ;; expand to literals like integers or other symbols. In order to enhance
1451 ;; the performance I use this `hif-simple-token-only' to notify my code and
1452 ;; save the final [value] into symbol database. [lukelee]
1453
1454 (defun hif-find-define (&optional min max)
1455 "Parse texts and retrieve all defines within the region MIN and MAX."
1456 (interactive)
1457 (and min (goto-char min))
1458 (and (re-search-forward hif-define-regexp max t)
1459 (or
1460 (let* ((defining (string= "define" (match-string 2)))
1461 (name (and (re-search-forward hif-macroref-regexp max t)
1462 (match-string 1)))
1463 (parmlist (and (match-string 3) ; First arg id found
1464 (hif-parse-macro-arglist (match-string 2)))))
1465 (if defining
1466 ;; Ignore name (still need to return 't), or define the name
1467 (or (and hide-ifdef-exclude-define-regexp
1468 (string-match hide-ifdef-exclude-define-regexp
1469 name))
1470
1471 (let* ((start (point))
1472 (end (progn (hif-end-of-line) (point)))
1473 (hif-simple-token-only nil) ; Dynamic binding
1474 (tokens
1475 (and name
1476 ;; `hif-simple-token-only' is set/clear
1477 ;; only in this block
1478 (condition-case nil
1479 ;; Prevent C statements like
1480 ;; 'do { ... } while (0)'
1481 (hif-tokenize start end)
1482 (error
1483 ;; We can't just return nil here since
1484 ;; this will stop hideif from searching
1485 ;; for more #defines.
1486 (setq hif-simple-token-only t)
1487 (buffer-substring-no-properties
1488 start end)))))
1489 ;; For simple tokens we save only the parsed result;
1490 ;; otherwise we save the tokens and parse it after
1491 ;; parameter replacement
1492 (expr (and tokens
1493 ;; `hif-simple-token-only' is checked only
1494 ;; here.
1495 (or (and hif-simple-token-only
1496 (listp tokens)
1497 (= (length tokens) 1)
1498 (hif-parse-exp tokens))
1499 `(hif-define-macro ,parmlist
1500 ,tokens))))
1501 (SA (and name
1502 (assoc (intern name) hide-ifdef-env))))
1503 (and name
1504 (if SA
1505 (or (setcdr SA expr) t)
1506 ;; Lazy evaluation, eval only if hif-lookup find it.
1507 ;; Define it anyway, even if nil it's still in list
1508 ;; and therefore considerred defined
1509 (push (cons (intern name) expr) hide-ifdef-env)))))
1510 ;; #undef
1511 (and name
1512 (hif-undefine-symbol (intern name))))))
1513 t))
1514
1515
1516 (defun hif-add-new-defines (&optional min max)
1517 "Scan and add all #define macros between MIN and MAX."
1518 (interactive)
1519 (save-excursion
1520 (save-restriction
1521 ;; (mark-region min max) ;; for debugging
1522 (while (hif-find-define min max)
1523 (setf min (point)))
1524 (if max (goto-char max)
1525 (goto-char (point-max))))))
1526
1527 (defun hide-ifdef-guts ()
1528 "Does most of the work of `hide-ifdefs'.
1529 It does not do the work that's pointless to redo on a recursive entry."
1530 ;; (message "hide-ifdef-guts")
1531 (save-excursion
1532 (let ((case-fold-search nil)
1533 min max)
1534 (goto-char (point-min))
1535 (setf min (point))
1536 (cl-loop do
1537 (setf max (hif-find-any-ifX))
1538 (hif-add-new-defines min max)
1539 (if max
1540 (hif-possibly-hide))
1541 (setf min (point))
1542 while max))))
1543
1544 ;;===%%SF%% hide-ifdef-hiding (End) ===
1545
1546
1547 ;;===%%SF%% exports (Start) ===
1548
1549 (defun hide-ifdef-toggle-read-only ()
1550 "Toggle `hide-ifdef-read-only'."
1551 (interactive)
1552 (setq hide-ifdef-read-only (not hide-ifdef-read-only))
1553 (message "Hide-Read-Only %s"
1554 (if hide-ifdef-read-only "ON" "OFF"))
1555 (if hide-ifdef-hiding
1556 (setq buffer-read-only (or hide-ifdef-read-only
1557 hif-outside-read-only)))
1558 (force-mode-line-update))
1559
1560 (defun hide-ifdef-toggle-outside-read-only ()
1561 "Replacement for `toggle-read-only' within Hide-Ifdef mode."
1562 (interactive)
1563 (setq hif-outside-read-only (not hif-outside-read-only))
1564 (message "Read only %s"
1565 (if hif-outside-read-only "ON" "OFF"))
1566 (setq buffer-read-only
1567 (or (and hide-ifdef-hiding hide-ifdef-read-only)
1568 hif-outside-read-only))
1569 (force-mode-line-update))
1570
1571 (defun hide-ifdef-toggle-shadowing ()
1572 "Toggle shadowing."
1573 (interactive)
1574 (set (make-local-variable 'hide-ifdef-shadow) (not hide-ifdef-shadow))
1575 (message "Shadowing %s" (if hide-ifdef-shadow "ON" "OFF"))
1576 (save-restriction
1577 (widen)
1578 (dolist (overlay (overlays-in (point-min) (point-max)))
1579 (when (overlay-get overlay 'hide-ifdef)
1580 (if hide-ifdef-shadow
1581 (progn
1582 (overlay-put overlay 'invisible nil)
1583 (overlay-put overlay 'face 'hide-ifdef-shadow))
1584 (overlay-put overlay 'face nil)
1585 (overlay-put overlay 'invisible 'hide-ifdef))))))
1586
1587 (defun hide-ifdef-define (var)
1588 "Define a VAR so that #ifdef VAR would be included."
1589 (interactive "SDefine what? ")
1590 (hif-set-var var 1)
1591 (if hide-ifdef-hiding (hide-ifdefs)))
1592
1593 (defun hif-undefine-symbol (var)
1594 (setq hide-ifdef-env
1595 (delete (assoc var hide-ifdef-env) hide-ifdef-env)))
1596
1597
1598 (defun hide-ifdef-undef (start end)
1599 "Undefine a VAR so that #ifdef VAR would not be included."
1600 (interactive "r")
1601 (let* ((symstr
1602 (or (and mark-active
1603 (buffer-substring-no-properties start end))
1604 (read-string "Undefine what? " (current-word))))
1605 (sym (and symstr
1606 (intern symstr))))
1607 (if (zerop (hif-defined sym))
1608 (message "`%s' not defined, no need to undefine it" symstr)
1609 (hif-undefine-symbol sym)
1610 (if hide-ifdef-hiding (hide-ifdefs))
1611 (message "`%S' undefined" sym))))
1612
1613 (defun hide-ifdefs (&optional nomsg)
1614 "Hide the contents of some #ifdefs.
1615 Assume that defined symbols have been added to `hide-ifdef-env'.
1616 The text hidden is the text that would not be included by the C
1617 preprocessor if it were given the file with those symbols defined.
1618
1619 Turn off hiding by calling `show-ifdefs'."
1620
1621 (interactive)
1622 (message "Hiding...")
1623 (setq hif-outside-read-only buffer-read-only)
1624 (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
1625 (if hide-ifdef-hiding
1626 (show-ifdefs)) ; Otherwise, deep confusion.
1627 (setq hide-ifdef-hiding t)
1628 (hide-ifdef-guts)
1629 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only))
1630 (or nomsg
1631 (message "Hiding done")))
1632
1633
1634 (defun show-ifdefs ()
1635 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
1636 (interactive)
1637 (setq buffer-read-only hif-outside-read-only)
1638 (hif-show-all)
1639 (setq hide-ifdef-hiding nil))
1640
1641
1642 (defun hif-find-ifdef-block ()
1643 "Utility to hide and show ifdef block.
1644 Return as (TOP . BOTTOM) the extent of ifdef block."
1645 (let (max-bottom)
1646 (cons (save-excursion
1647 (beginning-of-line)
1648 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1649 (up-ifdef))
1650 (prog1 (point)
1651 (hif-ifdef-to-endif)
1652 (setq max-bottom (1- (point)))))
1653 (save-excursion
1654 (beginning-of-line)
1655 (unless (hif-looking-at-endif)
1656 (hif-find-next-relevant))
1657 (while (hif-looking-at-ifX)
1658 (hif-ifdef-to-endif)
1659 (hif-find-next-relevant))
1660 (min max-bottom (1- (point)))))))
1661
1662
1663 (defun hide-ifdef-block ()
1664 "Hide the ifdef block (true or false part) enclosing or before the cursor."
1665 (interactive)
1666 (unless hide-ifdef-mode (hide-ifdef-mode 1))
1667 (let ((top-bottom (hif-find-ifdef-block)))
1668 (hide-ifdef-region (car top-bottom) (cdr top-bottom))
1669 (when hide-ifdef-lines
1670 (hif-hide-line (car top-bottom))
1671 (hif-hide-line (1+ (cdr top-bottom))))
1672 (setq hide-ifdef-hiding t))
1673 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
1674
1675 (defun show-ifdef-block ()
1676 "Show the ifdef block (true or false part) enclosing or before the cursor."
1677 (interactive)
1678 (let ((top-bottom (hif-find-ifdef-block)))
1679 (if hide-ifdef-lines
1680 (hif-show-ifdef-region
1681 (save-excursion
1682 (goto-char (car top-bottom)) (line-beginning-position))
1683 (save-excursion
1684 (goto-char (1+ (cdr top-bottom)))
1685 (hif-end-of-line) (point)))
1686 (hif-show-ifdef-region (1- (car top-bottom)) (cdr top-bottom)))))
1687
1688
1689 ;;; definition alist support
1690
1691 (defvar hide-ifdef-define-alist nil
1692 "A global assoc list of pre-defined symbol lists.")
1693
1694 (defun hif-compress-define-list (env)
1695 "Compress the define list ENV into a list of defined symbols only."
1696 (let ((new-defs nil))
1697 (dolist (def env new-defs)
1698 (if (hif-lookup (car def)) (push (car def) new-defs)))))
1699
1700 (defun hide-ifdef-set-define-alist (name)
1701 "Set the association for NAME to `hide-ifdef-env'."
1702 (interactive "SSet define list: ")
1703 (push (cons name (hif-compress-define-list hide-ifdef-env))
1704 hide-ifdef-define-alist))
1705
1706 (defun hide-ifdef-use-define-alist (name)
1707 "Set `hide-ifdef-env' to the define list specified by NAME."
1708 (interactive
1709 (list (completing-read "Use define list: "
1710 (mapcar (lambda (x) (symbol-name (car x)))
1711 hide-ifdef-define-alist)
1712 nil t)))
1713 (if (stringp name) (setq name (intern name)))
1714 (let ((define-list (assoc name hide-ifdef-define-alist)))
1715 (if define-list
1716 (setq hide-ifdef-env
1717 (mapcar (lambda (arg) (cons arg t))
1718 (cdr define-list)))
1719 (error "No define list for %s" name))
1720 (if hide-ifdef-hiding (hide-ifdefs))))
1721
1722 (provide 'hideif)
1723
1724 ;;; hideif.el ends here