* lisp/files.el (minibuffer-with-setup-hook): Evaluate the first arg eagerly.
[emacs.git] / lisp / progmodes / hideif.el
blob4b78c08690a084b3adf3ae4cb7c955a923cc7cfd
1 ;;; hideif.el --- hides selected code within ifdef
3 ;; Copyright (C) 1988, 1994, 2001-2014 Free Software Foundation, Inc.
5 ;; Author: Brian Marick
6 ;; Daniel LaLiberte <liberte@holonexus.org>
7 ;; Maintainer: emacs-devel@gnu.org
8 ;; Keywords: c, outlines
10 ;; This file is part of GNU Emacs.
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.
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.
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/>.
25 ;;; Commentary:
27 ;; To initialize, toggle the hide-ifdef minor mode with
29 ;; M-x hide-ifdef-mode
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
35 ;; M-x hide-ifdefs or C-c @ h
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.
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).
50 ;; You can undo the effect of hide-ifdefs by typing
52 ;; M-x show-ifdefs or C-c @ s
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.
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.
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:
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
75 ;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
76 ;; another list to use.
78 ;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
79 ;; set hide-ifdef-initially to non-nil.
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.
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.
91 ;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
92 ;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
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).
98 ;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
99 ;; Extensively modified by Daniel LaLiberte (while at Gould).
101 ;; Extensively modified by Luke Lee in 2013 to support complete C expression
102 ;; evaluation and argumented macro expansion.
104 ;;; Code:
106 (require 'cc-mode)
107 (require 'cl-lib)
109 (defgroup hide-ifdef nil
110 "Hide selected code within `ifdef'."
111 :group 'c)
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)
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)
123 (defcustom hide-ifdef-lines nil
124 "Non-nil means hide the #ifX, #else, and #endif lines."
125 :type 'boolean
126 :group 'hide-ifdef)
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")
134 (defface hide-ifdef-shadow '((t (:inherit shadow)))
135 "Face for shadowing ifdef blocks."
136 :group 'hide-ifdef
137 :version "23.1")
139 (defcustom hide-ifdef-exclude-define-regexp nil
140 "Ignore #define names if those names match this exclusion pattern."
141 :type 'string)
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)
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)
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'.")
163 (defconst hide-ifdef-mode-prefix-key "\C-c@"
164 "Prefix key for all Hide-Ifdef mode commands.")
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'.")
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"]))
199 (defvar hide-ifdef-hiding nil
200 "Non-nil when text may be hidden.")
202 (or (assq 'hide-ifdef-hiding minor-mode-alist)
203 (setq minor-mode-alist
204 (cons '(hide-ifdef-hiding " Hiding")
205 minor-mode-alist)))
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)
214 "Syntax table used for tokenizing #if expressions.")
216 (defvar hide-ifdef-env nil
217 "An alist of defined symbols and their values.")
219 (defvar hif-outside-read-only nil
220 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
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.
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:
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.
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'.
245 `hide-ifdef-lines'
246 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
247 #endif lines when hiding.
249 `hide-ifdef-initially'
250 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
251 is activated.
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.
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)
271 (add-to-invisibility-spec '(hide-ifdef . t))
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))))
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)))
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)
296 (defun hif-end-of-line ()
297 (end-of-line)
298 (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
299 (end-of-line 2)))
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))))
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)))
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))
322 ;;===%%SF%% evaluation (Start) ===
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.")
331 (defvar hif-undefined-symbol nil
332 "...is by default considered to be false.")
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)))
339 (declare-function semantic-c-hideif-lookup "semantic/bovine/c" (var))
340 (declare-function semantic-c-hideif-defined "semantic/bovine/c" (var))
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))))
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)))
357 ;;===%%SF%% evaluation (End) ===
361 ;;===%%SF%% parsing (Start) ===
362 ;;; The code that understands what ifs and ifdef in files look like.
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]+"))
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
390 "\\)?" ))
392 ;; Store the current token and the whole token list during parsing.
393 ;; Bound dynamically.
394 (defvar hif-token)
395 (defvar hif-token-list)
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)))
431 (defconst hif-valid-token-list (mapcar 'cdr hif-token-alist))
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+"))
439 (defconst hif-string-literal-regexp "\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
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))))
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)
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))
474 ((looking-at hif-string-literal-regexp)
475 (push (substring-no-properties (match-string 1)) token-list)
476 (goto-char (match-end 0)))
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)))
502 (t (error "Bad #if expression: %s" (buffer-string)))))))
504 (nreverse token-list)))
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
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
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 |
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)))
538 (defsubst hif-if-valid-identifier-p (id)
539 (not (or (numberp id)
540 (stringp id))))
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 (caddr tokens) 'hif-rparen))
554 (setq tokens (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)))
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)))
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)
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
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 tok)))
613 (result
614 (hif-expand-token-list
615 (hif-macro-supply-arguments tok parmlist)
616 tok expand_list)))
617 (setq remains (cons hif-token hif-token-list))
618 result))
619 ;; Argument list is nil, direct expansion
620 (setq rep (hif-expand-token-list
621 (caddr rep) ; Macro's token list
622 tok expand_list))
623 ;; Replace all remaining references immediately
624 (setq remains (substitute tok rep remains))
625 rep)
626 ;; Lookup tok returns an atom
627 rep))
629 ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
630 ;; this token might results in an incomplete expression that
631 ;; cannot be parsed further.
632 ;;((= 1 (hif-defined tok)) ; defined (hif-defined tok)=1,
633 ;; ;;but empty (hif-lookup tok)=nil, thus remove this token
634 ;; (setq remains (delete tok remains))
635 ;; nil)
637 (t ; Usual IDs
638 tok))
640 expanded))
642 (hif-flatten (nreverse expanded)))))
644 (defun hif-parse-exp (token-list &optional macroname)
645 "Parse the TOKEN-LIST.
646 Return translated list in prefix form. MACRONAME is applied when invoking
647 macros to prevent self-reference."
648 (let ((hif-token-list (hif-expand-token-list token-list macroname)))
649 (hif-nexttoken)
650 (prog1
651 (and hif-token
652 (hif-exprlist))
653 (if hif-token ; is there still a token?
654 (error "Error: unexpected token: %s" hif-token)))))
656 (defun hif-exprlist ()
657 "Parse an exprlist: expr { ',' expr}."
658 (let ((result (hif-expr)))
659 (if (eq hif-token 'hif-comma)
660 (let ((temp (list result)))
661 (while
662 (progn
663 (hif-nexttoken)
664 (push (hif-expr) temp)
665 (eq hif-token 'hif-comma)))
666 (cons 'hif-comma (nreverse temp)))
667 result)))
669 (defun hif-expr ()
670 "Parse an expression as found in #if.
671 expr : or-expr | or-expr '?' expr ':' expr."
672 (let ((result (hif-or-expr))
673 middle)
674 (while (eq hif-token 'hif-conditional)
675 (hif-nexttoken)
676 (setq middle (hif-expr))
677 (if (eq hif-token 'hif-colon)
678 (progn
679 (hif-nexttoken)
680 (setq result (list 'hif-conditional result middle (hif-expr))))
681 (error "Error: unexpected token: %s" hif-token)))
682 result))
684 (defun hif-or-expr ()
685 "Parse an or-expr : and-expr | or-expr '||' and-expr."
686 (let ((result (hif-and-expr)))
687 (while (eq hif-token 'hif-or)
688 (hif-nexttoken)
689 (setq result (list 'hif-or result (hif-and-expr))))
690 result))
692 (defun hif-and-expr ()
693 "Parse an and-expr : logior-expr | and-expr '&&' logior-expr."
694 (let ((result (hif-logior-expr)))
695 (while (eq hif-token 'hif-and)
696 (hif-nexttoken)
697 (setq result (list 'hif-and result (hif-logior-expr))))
698 result))
700 (defun hif-logior-expr ()
701 "Parse a logor-expr : logxor-expr | logor-expr '|' logxor-expr."
702 (let ((result (hif-logxor-expr)))
703 (while (eq hif-token 'hif-logior)
704 (hif-nexttoken)
705 (setq result (list 'hif-logior result (hif-logxor-expr))))
706 result))
708 (defun hif-logxor-expr ()
709 "Parse a logxor-expr : logand-expr | logxor-expr '^' logand-expr."
710 (let ((result (hif-logand-expr)))
711 (while (eq hif-token 'hif-logxor)
712 (hif-nexttoken)
713 (setq result (list 'hif-logxor result (hif-logand-expr))))
714 result))
716 (defun hif-logand-expr ()
717 "Parse a logand-expr : eq-expr | logand-expr '&' eq-expr."
718 (let ((result (hif-eq-expr)))
719 (while (eq hif-token 'hif-logand)
720 (hif-nexttoken)
721 (setq result (list 'hif-logand result (hif-eq-expr))))
722 result))
724 (defun hif-eq-expr ()
725 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
726 (let ((result (hif-comp-expr))
727 (eq-token nil))
728 (while (memq hif-token '(hif-equal hif-notequal))
729 (setq eq-token hif-token)
730 (hif-nexttoken)
731 (setq result (list eq-token result (hif-comp-expr))))
732 result))
734 (defun hif-comp-expr ()
735 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
736 (let ((result (hif-logshift-expr))
737 (comp-token nil))
738 (while (memq hif-token '(hif-greater hif-less hif-greater-equal
739 hif-less-equal))
740 (setq comp-token hif-token)
741 (hif-nexttoken)
742 (setq result (list comp-token result (hif-logshift-expr))))
743 result))
745 (defun hif-logshift-expr ()
746 "Parse a logshift : math | logshift `<<'|`>>' math."
747 (let ((result (hif-math))
748 (shift-token nil))
749 (while (memq hif-token '(hif-shiftleft hif-shiftright))
750 (setq shift-token hif-token)
751 (hif-nexttoken)
752 (setq result (list shift-token result (hif-math))))
753 result))
755 (defun hif-math ()
756 "Parse an expression with + or -.
757 math : muldiv | math '+|-' muldiv."
758 (let ((result (hif-muldiv-expr))
759 (math-op nil))
760 (while (memq hif-token '(hif-plus hif-minus))
761 (setq math-op hif-token)
762 (hif-nexttoken)
763 (setq result (list math-op result (hif-muldiv-expr))))
764 result))
766 (defun hif-muldiv-expr ()
767 "Parse an expression with *,/,%.
768 muldiv : factor | muldiv '*|/|%' factor."
769 (let ((result (hif-factor))
770 (math-op nil))
771 (while (memq hif-token '(hif-multiply hif-divide hif-modulo))
772 (setq math-op hif-token)
773 (hif-nexttoken)
774 (setq result (list math-op result (hif-factor))))
775 result))
777 (defun hif-factor ()
778 "Parse a factor.
779 factor : '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' |
780 'id(parmlist)' | strings | id."
781 (cond
782 ((eq hif-token 'hif-not)
783 (hif-nexttoken)
784 (list 'hif-not (hif-factor)))
786 ((eq hif-token 'hif-lognot)
787 (hif-nexttoken)
788 (list 'hif-lognot (hif-factor)))
790 ((eq hif-token 'hif-lparen)
791 (hif-nexttoken)
792 (let ((result (hif-exprlist)))
793 (if (not (eq hif-token 'hif-rparen))
794 (error "Bad token in parenthesized expression: %s" hif-token)
795 (hif-nexttoken)
796 result)))
798 ((eq hif-token 'hif-defined)
799 (hif-nexttoken)
800 (let ((paren (when (eq hif-token 'hif-lparen) (hif-nexttoken) t))
801 (ident hif-token))
802 (if (memq hif-token '(or and not hif-defined hif-lparen hif-rparen))
803 (error "Error: unexpected token: %s" hif-token))
804 (when paren
805 (hif-nexttoken)
806 (unless (eq hif-token 'hif-rparen)
807 (error "Error: expected \")\" after identifier")))
808 (hif-nexttoken)
809 `(hif-defined (quote ,ident))))
811 ((numberp hif-token)
812 (prog1 hif-token (hif-nexttoken)))
813 ((stringp hif-token)
814 (hif-string-concatenation))
816 ;; Unary plus/minus.
817 ((memq hif-token '(hif-minus hif-plus))
818 (list (prog1 hif-token (hif-nexttoken)) 0 (hif-factor)))
820 (t ; identifier
821 (let ((ident hif-token))
822 (hif-nexttoken)
823 (if (eq hif-token 'hif-lparen)
824 (hif-place-macro-invocation ident)
825 `(hif-lookup (quote ,ident)))))))
827 (defun hif-get-argument-list (ident)
828 (let ((nest 0)
829 (parmlist nil) ; A "token" list of parameters, will later be parsed
830 (parm nil))
832 (while (or (not (eq (hif-nexttoken) 'hif-rparen))
833 (/= nest 0))
834 (if (eq (car (last parm)) 'hif-comma)
835 (setq parm nil))
836 (cond
837 ((eq hif-token 'hif-lparen)
838 (setq nest (1+ nest)))
839 ((eq hif-token 'hif-rparen)
840 (setq nest (1- nest)))
841 ((and (eq hif-token 'hif-comma)
842 (= nest 0))
843 (push (nreverse parm) parmlist)
844 (setq parm nil)))
845 (push hif-token parm))
847 (push (nreverse parm) parmlist) ; Okay even if PARM is nil
848 (hif-nexttoken) ; Drop the `hif-rparen', get next token
849 (nreverse parmlist)))
851 (defun hif-place-macro-invocation (ident)
852 (let ((parmlist (hif-get-argument-list ident)))
853 `(hif-invoke (quote ,ident) (quote ,parmlist))))
855 (defun hif-string-concatenation ()
856 "Parse concatenated strings: string | strings string."
857 (let ((result (substring-no-properties hif-token)))
858 (while (stringp (hif-nexttoken))
859 (setq result (concat
860 (substring result 0 -1) ; remove trailing '"'
861 (substring hif-token 1)))) ; remove leading '"'
862 result))
864 (defun hif-define-macro (parmlist token-body)
865 "A marker for defined macro with arguments.
866 This macro cannot be evaluated alone without parameters inputed."
867 ;;TODO: input arguments at run time, use minibuffer to query all arguments
868 (error
869 "Argumented macro cannot be evaluated without passing any parameter"))
871 (defun hif-stringify (a)
872 "Stringify a number, string or symbol."
873 (cond
874 ((numberp a)
875 (number-to-string a))
876 ((atom a)
877 (symbol-name a))
878 ((stringp a)
879 (concat "\"" a "\""))
881 (error "Invalid token to stringify"))))
883 (defun intern-safe (str)
884 (if (stringp str)
885 (intern str)))
887 (defun hif-token-concat (a b)
888 "Concatenate two tokens into a longer token.
889 Currently support only simple token concatenation. Also support weird (but
890 valid) token concatenation like '>' ## '>' becomes '>>'. Here we take care only
891 those that can be evaluated during preprocessing time and ignore all those that
892 can only be evaluated at C(++) runtime (like '++', '--' and '+='...)."
893 (if (or (memq a hif-valid-token-list)
894 (memq b hif-valid-token-list))
895 (let* ((ra (car (rassq a hif-token-alist)))
896 (rb (car (rassq b hif-token-alist)))
897 (result (and ra rb
898 (cdr (assoc (concat ra rb) hif-token-alist)))))
899 (or result
900 ;;(error "Invalid token to concatenate")
901 (error "Concatenating \"%s\" and \"%s\" does not give a valid \
902 preprocessing token"
903 (or ra (symbol-name a))
904 (or rb (symbol-name b)))))
905 (intern-safe (concat (hif-stringify a)
906 (hif-stringify b)))))
908 (defun hif-mathify (val)
909 "Treat VAL as a number: if it's t or nil, use 1 or 0."
910 (cond ((eq val t) 1)
911 ((null val) 0)
912 (t val)))
914 (defun hif-conditional (a b c)
915 (if (not (zerop (hif-mathify a))) (hif-mathify b) (hif-mathify c)))
916 (defun hif-and (a b)
917 (and (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
918 (defun hif-or (a b)
919 (or (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
920 (defun hif-not (a)
921 (zerop (hif-mathify a)))
922 (defun hif-lognot (a)
923 (lognot (hif-mathify a)))
925 (defmacro hif-mathify-binop (fun)
926 `(lambda (a b)
927 ,(format "Like `%s' but treat t and nil as 1 and 0." fun)
928 (,fun (hif-mathify a) (hif-mathify b))))
930 (defun hif-shiftleft (a b)
931 (setq a (hif-mathify a))
932 (setq b (hif-mathify b))
933 (if (< a 0)
934 (ash a b)
935 (lsh a b)))
937 (defun hif-shiftright (a b)
938 (setq a (hif-mathify a))
939 (setq b (hif-mathify b))
940 (if (< a 0)
941 (ash a (- b))
942 (lsh a (- b))))
945 (defalias 'hif-multiply (hif-mathify-binop *))
946 (defalias 'hif-divide (hif-mathify-binop /))
947 (defalias 'hif-modulo (hif-mathify-binop %))
948 (defalias 'hif-plus (hif-mathify-binop +))
949 (defalias 'hif-minus (hif-mathify-binop -))
950 (defalias 'hif-equal (hif-mathify-binop =))
951 (defalias 'hif-notequal (hif-mathify-binop /=))
952 (defalias 'hif-greater (hif-mathify-binop >))
953 (defalias 'hif-less (hif-mathify-binop <))
954 (defalias 'hif-greater-equal (hif-mathify-binop >=))
955 (defalias 'hif-less-equal (hif-mathify-binop <=))
956 (defalias 'hif-logior (hif-mathify-binop logior))
957 (defalias 'hif-logxor (hif-mathify-binop logxor))
958 (defalias 'hif-logand (hif-mathify-binop logand))
961 (defun hif-comma (&rest expr)
962 "Evaluate a list of EXPR, return the result of the last item."
963 (let ((result nil))
964 (dolist (e expr)
965 (ignore-errors
966 (setq result (funcall hide-ifdef-evaluator e))))
967 result))
969 (defun hif-token-stringification (l)
970 "Scan token list for `hif-stringify' ('#') token and stringify the next token."
971 (let (result)
972 (while l
973 (push (if (eq (car l) 'hif-stringify)
974 (prog1
975 (if (cadr l)
976 (hif-stringify (cadr l))
977 (error "No token to stringify"))
978 (setq l (cdr l)))
979 (car l))
980 result)
981 (setq l (cdr l)))
982 (nreverse result)))
984 (defun hif-token-concatenation (l)
985 "Scan token list for `hif-token-concat' ('##') token and concatenate two tokens."
986 (let ((prev nil)
987 result)
988 (while l
989 (while (eq (car l) 'hif-token-concat)
990 (unless prev
991 (error "No token before ## to concatenate"))
992 (unless (cdr l)
993 (error "No token after ## to concatenate"))
994 (setq prev (hif-token-concat prev (cadr l)))
995 (setq l (cddr l)))
996 (if prev
997 (setq result (append result (list prev))))
998 (setq prev (car l)
999 l (cdr l)))
1000 (if prev
1001 (append result (list prev))
1002 result)))
1004 (defun hif-delimit (lis atom)
1005 (nconc (mapcan (lambda (l) (list l atom))
1006 (butlast lis))
1007 (last lis)))
1009 ;; Perform token replacement:
1010 (defun hif-macro-supply-arguments (macro-name actual-parms)
1011 "Expand a macro call, replace ACTUAL-PARMS in the macro body."
1012 (let* ((SA (assoc macro-name hide-ifdef-env))
1013 (macro (and SA
1014 (cdr SA)
1015 (eq (cadr SA) 'hif-define-macro)
1016 (cddr SA)))
1017 (formal-parms (and macro (car macro)))
1018 (macro-body (and macro (cadr macro)))
1019 actual-count
1020 formal-count
1021 actual
1022 formal
1023 etc)
1025 (when (and actual-parms formal-parms macro-body)
1026 ;; For each actual parameter, evaluate each one and associate it
1027 ;; with an actual parameter, put it into local table and finally
1028 ;; evaluate the macro body.
1029 (if (setq etc (eq (car formal-parms) 'hif-etc))
1030 ;; Take care of `hif-etc' first. Prefix `hif-comma' back if needed.
1031 (setq formal-parms (cdr formal-parms)))
1032 (setq formal-count (length formal-parms)
1033 actual-count (length actual-parms))
1035 (if (> formal-count actual-count)
1036 (error "Too few parmameter for macro %S" macro-name)
1037 (if (< formal-count actual-count)
1038 (or etc
1039 (error "Too many parameters for macro %S" macro-name))))
1041 ;; Perform token replacement on the MACRO-BODY with the parameters
1042 (while (setq formal (pop formal-parms))
1043 ;; Prevent repetitive substitutation, thus cannot use `subst'
1044 ;; for example:
1045 ;; #define mac(a,b) (a+b)
1046 ;; #define testmac mac(b,y)
1047 ;; testmac should expand to (b+y): replace of argument a and b
1048 ;; occurs simultaneously, not sequentially. If sequentially,
1049 ;; according to the argument order, it will become:
1050 ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
1051 ;; becomes (b+b)
1052 ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
1053 ;; becomes (y+y).
1054 (setq macro-body
1055 ;; Unlike `subst', `substitute' replace only the top level
1056 ;; instead of the whole tree; more importantly, it's not
1057 ;; destructive.
1058 (substitute (if (and etc (null formal-parms))
1059 (hif-delimit actual-parms 'hif-comma)
1060 (car actual-parms))
1061 formal macro-body))
1062 (setq actual-parms (cdr actual-parms)))
1064 ;; Replacement completed, flatten the whole token list
1065 (setq macro-body (hif-flatten macro-body))
1067 ;; Stringification and token concatenation happens here
1068 (hif-token-concatenation (hif-token-stringification macro-body)))))
1070 (defun hif-invoke (macro-name actual-parms)
1071 "Invoke a macro by expanding it, reparse macro-body and finally invoke it."
1072 ;; Reparse the macro body and evaluate it
1073 (funcall hide-ifdef-evaluator
1074 (hif-parse-exp
1075 (hif-macro-supply-arguments macro-name actual-parms)
1076 macro-name)))
1078 ;;;----------- end of parser -----------------------
1081 (defun hif-canonicalize-tokens (regexp) ; For debugging
1082 "Return the expanded result of the scanned tokens."
1083 (save-excursion
1084 (re-search-forward regexp)
1085 (let* ((curr-regexp (match-string 0))
1086 (defined (string-match hif-ifxdef-regexp curr-regexp))
1087 (negate (and defined
1088 (string= (match-string 2 curr-regexp) "n")))
1089 (hif-simple-token-only nil) ;; Dynamic binding var for `hif-tokenize'
1090 (tokens (hif-tokenize (point)
1091 (progn (hif-end-of-line) (point)))))
1092 (if defined
1093 (setq tokens (list 'hif-defined tokens)))
1094 (if negate
1095 (setq tokens (list 'hif-not tokens)))
1096 tokens)))
1098 (defun hif-canonicalize (regexp)
1099 "Return a Lisp expression for its condition by scanning current buffer.
1100 Do this when cursor is at the beginning of `regexp' (i.e. #ifX)."
1101 (let ((case-fold-search nil))
1102 (save-excursion
1103 (re-search-forward regexp)
1104 (let* ((curr-regexp (match-string 0))
1105 (defined (string-match hif-ifxdef-regexp curr-regexp))
1106 (negate (and defined
1107 (string= (match-string 2 curr-regexp) "n")))
1108 (hif-simple-token-only nil) ; Dynamic binding for `hif-tokenize'
1109 (tokens (hif-tokenize (point)
1110 (progn (hif-end-of-line) (point)))))
1111 (if defined
1112 (setq tokens (list 'hif-defined tokens)))
1113 (if negate
1114 (setq tokens (list 'hif-not tokens)))
1115 (hif-parse-exp tokens)))))
1117 (defun hif-find-any-ifX ()
1118 "Move to next #if..., or #ifndef, at point or after."
1119 ;; (message "find ifX at %d" (point))
1120 (prog1
1121 (re-search-forward hif-ifx-regexp (point-max) t)
1122 (beginning-of-line)))
1125 (defun hif-find-next-relevant ()
1126 "Move to next #if..., #elif..., #else, or #endif, after the current line."
1127 ;; (message "hif-find-next-relevant at %d" (point))
1128 (end-of-line)
1129 ;; Avoid infinite recursion by only going to line-beginning if match found
1130 (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
1131 (beginning-of-line)))
1133 (defun hif-find-previous-relevant ()
1134 "Move to previous #if..., #else, or #endif, before the current line."
1135 ;; (message "hif-find-previous-relevant at %d" (point))
1136 (beginning-of-line)
1137 ;; Avoid infinite recursion by only going to line-beginning if match found
1138 (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
1139 (beginning-of-line)))
1142 (defun hif-looking-at-ifX ()
1143 (looking-at hif-ifx-regexp)) ; Should eventually see #if
1144 (defun hif-looking-at-endif ()
1145 (looking-at hif-endif-regexp))
1146 (defun hif-looking-at-else ()
1147 (looking-at hif-else-regexp))
1149 (defun hif-looking-at-elif ()
1150 (looking-at hif-elif-regexp))
1153 (defun hif-ifdef-to-endif ()
1154 "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
1155 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
1156 (hif-find-next-relevant)
1157 (cond ((hif-looking-at-ifX)
1158 (hif-ifdef-to-endif) ; Find endif of nested if
1159 (hif-ifdef-to-endif)) ; Find outer endif or else
1160 ((hif-looking-at-elif)
1161 (hif-ifdef-to-endif))
1162 ((hif-looking-at-else)
1163 (hif-ifdef-to-endif)) ; Find endif following else
1164 ((hif-looking-at-endif)
1165 'done)
1167 (error "Mismatched #ifdef #endif pair"))))
1170 (defun hif-endif-to-ifdef ()
1171 "If positioned at #endif form, skip backward to corresponding #ifX."
1172 ;; (message "hif-endif-to-ifdef at %d" (point))
1173 (let ((start (point)))
1174 (hif-find-previous-relevant)
1175 (if (= start (point))
1176 (error "Mismatched #ifdef #endif pair")))
1177 (cond ((hif-looking-at-endif)
1178 (hif-endif-to-ifdef) ; find beginning of nested if
1179 (hif-endif-to-ifdef)) ; find beginning of outer if or else
1180 ((hif-looking-at-else)
1181 (hif-endif-to-ifdef))
1182 ((hif-looking-at-ifX)
1183 'done)
1184 (t))) ; never gets here
1187 (defun forward-ifdef (&optional arg)
1188 "Move point to beginning of line of the next ifdef-endif.
1189 With argument, do this that many times."
1190 (interactive "p")
1191 (or arg (setq arg 1))
1192 (if (< arg 0) (backward-ifdef (- arg))
1193 (while (< 0 arg)
1194 (setq arg (- arg))
1195 (let ((start (point)))
1196 (unless (hif-looking-at-ifX)
1197 (hif-find-next-relevant))
1198 (if (hif-looking-at-ifX)
1199 (hif-ifdef-to-endif)
1200 (goto-char start)
1201 (error "No following #ifdef"))))))
1204 (defun backward-ifdef (&optional arg)
1205 "Move point to beginning of the previous ifdef-endif.
1206 With argument, do this that many times."
1207 (interactive "p")
1208 (or arg (setq arg 1))
1209 (if (< arg 0) (forward-ifdef (- arg))
1210 (while (< 0 arg)
1211 (setq arg (1- arg))
1212 (beginning-of-line)
1213 (let ((start (point)))
1214 (unless (hif-looking-at-endif)
1215 (hif-find-previous-relevant))
1216 (if (hif-looking-at-endif)
1217 (hif-endif-to-ifdef)
1218 (goto-char start)
1219 (error "No previous #ifdef"))))))
1222 (defun down-ifdef ()
1223 "Move point to beginning of nested ifdef or else-part."
1224 (interactive)
1225 (let ((start (point)))
1226 (hif-find-next-relevant)
1227 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
1229 (goto-char start)
1230 (error "No following #ifdef"))))
1233 (defun up-ifdef ()
1234 "Move point to beginning of enclosing ifdef or else-part."
1235 (interactive)
1236 (beginning-of-line)
1237 (let ((start (point)))
1238 (unless (hif-looking-at-endif)
1239 (hif-find-previous-relevant))
1240 (if (hif-looking-at-endif)
1241 (hif-endif-to-ifdef))
1242 (if (= start (point))
1243 (error "No previous #ifdef"))))
1245 (defun next-ifdef (&optional arg)
1246 "Move to the beginning of the next #ifX, #else, or #endif.
1247 With argument, do this that many times."
1248 (interactive "p")
1249 (or arg (setq arg 1))
1250 (if (< arg 0) (previous-ifdef (- arg))
1251 (while (< 0 arg)
1252 (setq arg (1- arg))
1253 (hif-find-next-relevant)
1254 (when (eolp)
1255 (beginning-of-line)
1256 (error "No following #ifdefs, #elses, or #endifs")))))
1258 (defun previous-ifdef (&optional arg)
1259 "Move to the beginning of the previous #ifX, #else, or #endif.
1260 With argument, do this that many times."
1261 (interactive "p")
1262 (or arg (setq arg 1))
1263 (if (< arg 0) (next-ifdef (- arg))
1264 (while (< 0 arg)
1265 (setq arg (1- arg))
1266 (let ((start (point)))
1267 (hif-find-previous-relevant)
1268 (if (= start (point))
1269 (error "No previous #ifdefs, #elses, or #endifs"))))))
1272 ;;===%%SF%% parsing (End) ===
1275 ;;===%%SF%% hide-ifdef-hiding (Start) ===
1278 ;;; A range is a structure with four components:
1279 ;;; ELSE-P True if there was an else clause for the ifdef.
1280 ;;; START The start of the range. (beginning of line)
1281 ;;; ELSE The else marker (beginning of line)
1282 ;;; Only valid if ELSE-P is true.
1283 ;;; END The end of the range. (beginning of line)
1285 (defsubst hif-make-range (start end &optional else)
1286 (list start else end))
1288 (defsubst hif-range-start (range) (elt range 0))
1289 (defsubst hif-range-else (range) (elt range 1))
1290 (defsubst hif-range-end (range) (elt range 2))
1294 ;;; Find-Range
1295 ;;; The workhorse, it delimits the #if region. Reasonably simple:
1296 ;;; Skip until an #else or #endif is found, remembering positions. If
1297 ;;; an #else was found, skip some more, looking for the true #endif.
1299 (defun hif-find-range ()
1300 "Return a Range structure describing the current #if region.
1301 Point is left unchanged."
1302 ;; (message "hif-find-range at %d" (point))
1303 (save-excursion
1304 (beginning-of-line)
1305 (let ((start (point))
1306 (else nil)
1307 (end nil))
1308 ;; Part one. Look for either #endif or #else.
1309 ;; This loop-and-a-half dedicated to E. Dijkstra.
1310 (while (progn
1311 (hif-find-next-relevant)
1312 (hif-looking-at-ifX)) ; Skip nested ifdef
1313 (hif-ifdef-to-endif))
1314 ;; Found either a #else or an #endif.
1315 (cond ((hif-looking-at-else)
1316 (setq else (point)))
1318 (setq end (point)))) ; (line-end-position)
1319 ;; If found #else, look for #endif.
1320 (when else
1321 (while (progn
1322 (hif-find-next-relevant)
1323 (hif-looking-at-ifX)) ; Skip nested ifdef
1324 (hif-ifdef-to-endif))
1325 (if (hif-looking-at-else)
1326 (error "Found two elses in a row? Broken!"))
1327 (setq end (point))) ; (line-end-position)
1328 (hif-make-range start end else))))
1331 ;; A bit slimy.
1333 (defun hif-hide-line (point)
1334 "Hide the line containing point.
1335 Does nothing if `hide-ifdef-lines' is nil."
1336 (when hide-ifdef-lines
1337 (save-excursion
1338 (goto-char point)
1339 (hide-ifdef-region-internal
1340 (line-beginning-position) (progn (hif-end-of-line) (point))))))
1343 ;;; Hif-Possibly-Hide
1344 ;;; There are four cases. The #ifX expression is "taken" if it
1345 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
1346 ;;; inside the #ifdef would be included when the program was
1347 ;;; compiled.
1349 ;;; Case 1: #ifX taken, and there's an #else.
1350 ;;; The #else part must be hidden. The #if (then) part must be
1351 ;;; processed for nested #ifX's.
1352 ;;; Case 2: #ifX taken, and there's no #else.
1353 ;;; The #if part must be processed for nested #ifX's.
1354 ;;; Case 3: #ifX not taken, and there's an #else.
1355 ;;; The #if part must be hidden. The #else part must be processed
1356 ;;; for nested #ifs.
1357 ;;; Case 4: #ifX not taken, and there's no #else.
1358 ;;; The #ifX part must be hidden.
1360 ;;; Further processing is done by narrowing to the relevant region
1361 ;;; and just recursively calling hide-ifdef-guts.
1363 ;;; When hif-possibly-hide returns, point is at the end of the
1364 ;;; possibly-hidden range.
1366 (defun hif-recurse-on (start end)
1367 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
1368 (save-excursion
1369 (save-restriction
1370 (goto-char start)
1371 (end-of-line)
1372 (narrow-to-region (point) end)
1373 (hide-ifdef-guts))))
1375 (defun hif-possibly-hide ()
1376 "Called at #ifX expression, this hides those parts that should be hidden.
1377 It uses the judgment of `hide-ifdef-evaluator'."
1378 ;; (message "hif-possibly-hide") (sit-for 1)
1379 (let ((test (hif-canonicalize hif-ifx-regexp))
1380 (range (hif-find-range)))
1381 ;; (message "test = %s" test) (sit-for 1)
1383 (hif-hide-line (hif-range-end range))
1384 (if (not (hif-not (funcall hide-ifdef-evaluator test)))
1385 (cond ((hif-range-else range) ; case 1
1386 (hif-hide-line (hif-range-else range))
1387 (hide-ifdef-region (hif-range-else range)
1388 (1- (hif-range-end range)))
1389 (hif-recurse-on (hif-range-start range)
1390 (hif-range-else range)))
1391 (t ; case 2
1392 (hif-recurse-on (hif-range-start range)
1393 (hif-range-end range))))
1394 (cond ((hif-range-else range) ; case 3
1395 (hif-hide-line (hif-range-else range))
1396 (hide-ifdef-region (hif-range-start range)
1397 (1- (hif-range-else range)))
1398 (hif-recurse-on (hif-range-else range)
1399 (hif-range-end range)))
1400 (t ; case 4
1401 (hide-ifdef-region (point)
1402 (1- (hif-range-end range))))))
1403 (hif-hide-line (hif-range-start range)) ; Always hide start.
1404 (goto-char (hif-range-end range))
1405 (end-of-line)))
1407 (defun hif-parse-macro-arglist (str)
1408 "Parse argument list formatted as '( arg1 [ , argn] [...] )'.
1409 The '...' is also included. Return a list of the arguments, if '...' exists the
1410 first arg will be `hif-etc'."
1411 (let* ((hif-simple-token-only nil) ; Dynamic binding var for `hif-tokenize'
1412 (tokenlist
1413 (cdr (hif-tokenize
1414 (- (point) (length str)) (point)))) ; Remove `hif-lparen'
1415 etc result token)
1416 (while (not (eq (setq token (pop tokenlist)) 'hif-rparen))
1417 (cond
1418 ((eq token 'hif-etc)
1419 (setq etc t))
1420 ((eq token 'hif-comma)
1423 (push token result))))
1424 (if etc
1425 (cons 'hif-etc (nreverse result))
1426 (nreverse result))))
1428 ;; The original version of hideif evaluates the macro early and store the
1429 ;; final values for the defined macro into the symbol database (aka
1430 ;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
1431 ;; tree -> [value]". (The square bracket refers to what's stored in in our
1432 ;; `hide-ifdef-env'.)
1434 ;; This forbids the evaluation of an argumented macro since the parameters
1435 ;; are applied at run time. In order to support argumented macro I then
1436 ;; postponed the evaluation process one stage and store the "parsed tree"
1437 ;; into symbol database. The evaluation process was then "strings -> tokens
1438 ;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
1439 ;; evaluate the parsed tree everytime when trying to expand the symbol. These
1440 ;; temporarily code changes are obsolete and not in Emacs source repository.
1442 ;; Furthermore, CPP did allow partial expression to be defined in several
1443 ;; macros and later got concatenated into a complete expression and then
1444 ;; evaluate it. In order to match this behavior I had to postpone one stage
1445 ;; further, otherwise those partial expression will be fail on parsing and
1446 ;; we'll miss all macros that reference it. The evaluation process thus
1447 ;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
1448 ;; performance since we need to parse tokens and evaluate them everytime
1449 ;; when that symbol is referenced.
1451 ;; In real cases I found a lot portion of macros are "simple macros" that
1452 ;; expand to literals like integers or other symbols. In order to enhance
1453 ;; the performance I use this `hif-simple-token-only' to notify my code and
1454 ;; save the final [value] into symbol database. [lukelee]
1456 (defun hif-find-define (&optional min max)
1457 "Parse texts and retrieve all defines within the region MIN and MAX."
1458 (interactive)
1459 (and min (goto-char min))
1460 (and (re-search-forward hif-define-regexp max t)
1462 (let* ((defining (string= "define" (match-string 2)))
1463 (name (and (re-search-forward hif-macroref-regexp max t)
1464 (match-string 1)))
1465 (parsed nil)
1466 (parmlist (and (match-string 3) ; First arg id found
1467 (hif-parse-macro-arglist (match-string 2)))))
1468 (if defining
1469 ;; Ignore name (still need to return 't), or define the name
1470 (or (and hide-ifdef-exclude-define-regexp
1471 (string-match hide-ifdef-exclude-define-regexp
1472 name))
1474 (let* ((start (point))
1475 (end (progn (hif-end-of-line) (point)))
1476 (hif-simple-token-only nil) ; Dynamic binding
1477 (tokens
1478 (and name
1479 ;; `hif-simple-token-only' is set/clear
1480 ;; only in this block
1481 (condition-case nil
1482 ;; Prevent C statements like
1483 ;; 'do { ... } while (0)'
1484 (hif-tokenize start end)
1485 (error
1486 ;; We can't just return nil here since
1487 ;; this will stop hideif from searching
1488 ;; for more #defines.
1489 (setq hif-simple-token-only t)
1490 (buffer-substring-no-properties
1491 start end)))))
1492 ;; For simple tokens we save only the parsed result;
1493 ;; otherwise we save the tokens and parse it after
1494 ;; parameter replacement
1495 (expr (and tokens
1496 ;; `hif-simple-token-only' is checked only
1497 ;; here.
1498 (or (and hif-simple-token-only
1499 (listp tokens)
1500 (= (length tokens) 1)
1501 (hif-parse-exp tokens))
1502 `(hif-define-macro ,parmlist
1503 ,tokens))))
1504 (SA (and name
1505 (assoc (intern name) hide-ifdef-env))))
1506 (and name
1507 (if SA
1508 (or (setcdr SA expr) t)
1509 ;; Lazy evaluation, eval only if hif-lookup find it.
1510 ;; Define it anyway, even if nil it's still in list
1511 ;; and therefore considerred defined
1512 (push (cons (intern name) expr) hide-ifdef-env)))))
1513 ;; #undef
1514 (and name
1515 (hif-undefine-symbol (intern name))))))
1519 (defun hif-add-new-defines (&optional min max)
1520 "Scan and add all #define macros between MIN and MAX."
1521 (interactive)
1522 (save-excursion
1523 (save-restriction
1524 ;; (mark-region min max) ;; for debugging
1525 (while (hif-find-define min max)
1526 (setf min (point)))
1527 (if max (goto-char max)
1528 (goto-char (point-max))))))
1530 (defun hide-ifdef-guts ()
1531 "Does most of the work of `hide-ifdefs'.
1532 It does not do the work that's pointless to redo on a recursive entry."
1533 ;; (message "hide-ifdef-guts")
1534 (save-excursion
1535 (let ((case-fold-search nil)
1536 min max)
1537 (goto-char (point-min))
1538 (setf min (point))
1539 (loop do
1540 (setf max (hif-find-any-ifX))
1541 (hif-add-new-defines min max)
1542 (if max
1543 (hif-possibly-hide))
1544 (setf min (point))
1545 while max))))
1547 ;;===%%SF%% hide-ifdef-hiding (End) ===
1550 ;;===%%SF%% exports (Start) ===
1552 (defun hide-ifdef-toggle-read-only ()
1553 "Toggle `hide-ifdef-read-only'."
1554 (interactive)
1555 (setq hide-ifdef-read-only (not hide-ifdef-read-only))
1556 (message "Hide-Read-Only %s"
1557 (if hide-ifdef-read-only "ON" "OFF"))
1558 (if hide-ifdef-hiding
1559 (setq buffer-read-only (or hide-ifdef-read-only
1560 hif-outside-read-only)))
1561 (force-mode-line-update))
1563 (defun hide-ifdef-toggle-outside-read-only ()
1564 "Replacement for `toggle-read-only' within Hide-Ifdef mode."
1565 (interactive)
1566 (setq hif-outside-read-only (not hif-outside-read-only))
1567 (message "Read only %s"
1568 (if hif-outside-read-only "ON" "OFF"))
1569 (setq buffer-read-only
1570 (or (and hide-ifdef-hiding hide-ifdef-read-only)
1571 hif-outside-read-only))
1572 (force-mode-line-update))
1574 (defun hide-ifdef-toggle-shadowing ()
1575 "Toggle shadowing."
1576 (interactive)
1577 (set (make-local-variable 'hide-ifdef-shadow) (not hide-ifdef-shadow))
1578 (message "Shadowing %s" (if hide-ifdef-shadow "ON" "OFF"))
1579 (save-restriction
1580 (widen)
1581 (dolist (overlay (overlays-in (point-min) (point-max)))
1582 (when (overlay-get overlay 'hide-ifdef)
1583 (if hide-ifdef-shadow
1584 (progn
1585 (overlay-put overlay 'invisible nil)
1586 (overlay-put overlay 'face 'hide-ifdef-shadow))
1587 (overlay-put overlay 'face nil)
1588 (overlay-put overlay 'invisible 'hide-ifdef))))))
1590 (defun hide-ifdef-define (var)
1591 "Define a VAR so that #ifdef VAR would be included."
1592 (interactive "SDefine what? ")
1593 (hif-set-var var 1)
1594 (if hide-ifdef-hiding (hide-ifdefs)))
1596 (defun hif-undefine-symbol (var)
1597 (setq hide-ifdef-env
1598 (delete (assoc var hide-ifdef-env) hide-ifdef-env)))
1601 (defun hide-ifdef-undef (start end)
1602 "Undefine a VAR so that #ifdef VAR would not be included."
1603 (interactive "r")
1604 (let* ((symstr
1605 (or (and mark-active
1606 (buffer-substring-no-properties start end))
1607 (read-string "Undefine what? " (current-word))))
1608 (sym (and symstr
1609 (intern symstr))))
1610 (if (zerop (hif-defined sym))
1611 (message "`%s' not defined, no need to undefine it" symstr)
1612 (hif-undefine-symbol sym)
1613 (if hide-ifdef-hiding (hide-ifdefs))
1614 (message "`%S' undefined" sym))))
1616 (defun hide-ifdefs (&optional nomsg)
1617 "Hide the contents of some #ifdefs.
1618 Assume that defined symbols have been added to `hide-ifdef-env'.
1619 The text hidden is the text that would not be included by the C
1620 preprocessor if it were given the file with those symbols defined.
1622 Turn off hiding by calling `show-ifdefs'."
1624 (interactive)
1625 (message "Hiding...")
1626 (setq hif-outside-read-only buffer-read-only)
1627 (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
1628 (if hide-ifdef-hiding
1629 (show-ifdefs)) ; Otherwise, deep confusion.
1630 (setq hide-ifdef-hiding t)
1631 (hide-ifdef-guts)
1632 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only))
1633 (or nomsg
1634 (message "Hiding done")))
1637 (defun show-ifdefs ()
1638 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
1639 (interactive)
1640 (setq buffer-read-only hif-outside-read-only)
1641 (hif-show-all)
1642 (setq hide-ifdef-hiding nil))
1645 (defun hif-find-ifdef-block ()
1646 "Utility to hide and show ifdef block.
1647 Return as (TOP . BOTTOM) the extent of ifdef block."
1648 (let (max-bottom)
1649 (cons (save-excursion
1650 (beginning-of-line)
1651 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1652 (up-ifdef))
1653 (prog1 (point)
1654 (hif-ifdef-to-endif)
1655 (setq max-bottom (1- (point)))))
1656 (save-excursion
1657 (beginning-of-line)
1658 (unless (hif-looking-at-endif)
1659 (hif-find-next-relevant))
1660 (while (hif-looking-at-ifX)
1661 (hif-ifdef-to-endif)
1662 (hif-find-next-relevant))
1663 (min max-bottom (1- (point)))))))
1666 (defun hide-ifdef-block ()
1667 "Hide the ifdef block (true or false part) enclosing or before the cursor."
1668 (interactive)
1669 (unless hide-ifdef-mode (hide-ifdef-mode 1))
1670 (let ((top-bottom (hif-find-ifdef-block)))
1671 (hide-ifdef-region (car top-bottom) (cdr top-bottom))
1672 (when hide-ifdef-lines
1673 (hif-hide-line (car top-bottom))
1674 (hif-hide-line (1+ (cdr top-bottom))))
1675 (setq hide-ifdef-hiding t))
1676 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
1678 (defun show-ifdef-block ()
1679 "Show the ifdef block (true or false part) enclosing or before the cursor."
1680 (interactive)
1681 (let ((top-bottom (hif-find-ifdef-block)))
1682 (if hide-ifdef-lines
1683 (hif-show-ifdef-region
1684 (save-excursion
1685 (goto-char (car top-bottom)) (line-beginning-position))
1686 (save-excursion
1687 (goto-char (1+ (cdr top-bottom)))
1688 (hif-end-of-line) (point)))
1689 (hif-show-ifdef-region (1- (car top-bottom)) (cdr top-bottom)))))
1692 ;;; definition alist support
1694 (defvar hide-ifdef-define-alist nil
1695 "A global assoc list of pre-defined symbol lists.")
1697 (defun hif-compress-define-list (env)
1698 "Compress the define list ENV into a list of defined symbols only."
1699 (let ((new-defs nil))
1700 (dolist (def env new-defs)
1701 (if (hif-lookup (car def)) (push (car def) new-defs)))))
1703 (defun hide-ifdef-set-define-alist (name)
1704 "Set the association for NAME to `hide-ifdef-env'."
1705 (interactive "SSet define list: ")
1706 (push (cons name (hif-compress-define-list hide-ifdef-env))
1707 hide-ifdef-define-alist))
1709 (defun hide-ifdef-use-define-alist (name)
1710 "Set `hide-ifdef-env' to the define list specified by NAME."
1711 (interactive
1712 (list (completing-read "Use define list: "
1713 (mapcar (lambda (x) (symbol-name (car x)))
1714 hide-ifdef-define-alist)
1715 nil t)))
1716 (if (stringp name) (setq name (intern name)))
1717 (let ((define-list (assoc name hide-ifdef-define-alist)))
1718 (if define-list
1719 (setq hide-ifdef-env
1720 (mapcar (lambda (arg) (cons arg t))
1721 (cdr define-list)))
1722 (error "No define list for %s" name))
1723 (if hide-ifdef-hiding (hide-ifdefs))))
1725 (provide 'hideif)
1727 ;;; hideif.el ends here