emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] emacs-24 r117306: HideIfDef mode bug fixes and enhancement


From: Luke Lee
Subject: [Emacs-diffs] emacs-24 r117306: HideIfDef mode bug fixes and enhancements. This is #2 of 3 patches based
Date: Thu, 26 Jun 2014 05:59:39 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 117306 [merge]
revision-id: address@hidden
parent: address@hidden
parent: address@hidden
committer: Luke Lee <address@hidden>
branch nick: emacs-24
timestamp: Thu 2014-06-26 13:56:33 +0800
message:
  HideIfDef mode bug fixes and enhancements. This is #2 of 3 patches based
  on the completed work posted on http://www.emacswiki.org/emacs/HideIfDef.
  
  - Supporting argumented macro expansion.
  - Stringification, tokenization and concatenation of strings and tokens.
  - Add functions to find defines and parse argumented macros into a macro
    tree containing macro name, formal parameters and macro body.
  - On macro evaluation, macros will be applied with actual parameters and
    then got expanded recursively.
  - Merge main trunk changes
  - Apply review changes.
  
  * lisp/progmodes/hideif.el (hif-string-to-number): Merge main trunk
  changes.
  (hif-simple-token-only, hif-tokenize): Commentted in detail mainly for
  performance enhancements.
  (hif-parse-if-exp): Rename to `hif-parse-exp'. Enhanced for macro
  expansion.
  (hif-factor, hif-string-concatenation, intern-safe): Support string
  concatenation and argumented macro expansion.
  (hif-if-valid-identifier-p, hif-define-operator, hif-flatten)
  (hif-expand-token-list, hif-get-argument-list, hif-define-macro)
  (hif-delimit, hif-macro-supply-arguments, hif-invoke, hif-canonicalize)
  (hif-canonicalize-tokens, hif-looking-at-elif, hif-place-macro-invocation)
  (hif-parse-macro-arglist): Mostly new functions for supporting argumented
  macro expansion.
  (hif-string-concatenation, hif-stringify, hif-token-concat)
  (hif-token-stringification, hif-token-concatenation): Stringify and
  concatentation.
  (hif-find-next-relevant): Fix comments
  (hif-ifdef-to-endif, hif-looking-at-elif, hif-hide-line): Bug fix for
  some cases involving #elif.
  (hif-find-define, hif-add-new-defines): New functions for automatically
  scanning of defined symbols.
  (hide-ifdef-guts): Fix for auto defined symbol scanning.
  (hide-ifdef-undef): Fix behavior to match CPP.
modified:
  lisp/progmodes/hideif.el       hideif.el-20091113204419-o5vbwnq5f7feedwu-39
=== modified file 'lisp/progmodes/hideif.el'
--- a/lisp/progmodes/hideif.el  2014-03-14 00:22:33 +0000
+++ b/lisp/progmodes/hideif.el  2014-06-26 05:47:33 +0000
@@ -36,6 +36,8 @@
 ;;
 ;; Hide-ifdef suppresses the display of code that the preprocessor wouldn't
 ;; pass through.  Support complete C/C++ expression and precedence.
+;; It will automatically scans for new #define symbols and macros on the way
+;; parsing.
 ;;
 ;; The hidden code is marked by ellipses (...).  Be
 ;; cautious when editing near ellipses, since the hidden text is
@@ -97,11 +99,12 @@
 ;; Extensively modified by Daniel LaLiberte (while at Gould).
 ;;
 ;; Extensively modified by Luke Lee in 2013 to support complete C expression
-;; evaluation.
+;; evaluation and argumented macro expansion.
 
 ;;; Code:
 
 (require 'cc-mode)
+(require 'cl-lib)
 
 (defgroup hide-ifdef nil
   "Hide selected code within `ifdef'."
@@ -133,6 +136,9 @@
   :group 'hide-ifdef
   :version "23.1")
 
+(defcustom hide-ifdef-exclude-define-regexp nil
+  "Ignore #define names if those names match this exclusion pattern."
+  :type 'string)
 
 (defvar hide-ifdef-mode-submap
   ;; Set up the submap that goes after the prefix key.
@@ -356,12 +362,32 @@
 ;;;  The code that understands what ifs and ifdef in files look like.
 
 (defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
+(defconst hif-ifxdef-regexp (concat hif-cpp-prefix "if\\(n\\)?def"))
 (defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
 (defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
+(defconst hif-elif-regexp   (concat hif-cpp-prefix "elif"))
 (defconst hif-else-regexp (concat hif-cpp-prefix "else"))
 (defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
 (defconst hif-ifx-else-endif-regexp
-  (concat hif-ifx-regexp "\\|" hif-else-regexp "\\|" hif-endif-regexp))
+  (concat hif-ifx-regexp "\\|" hif-elif-regexp "\\|" hif-else-regexp "\\|"
+          hif-endif-regexp))
+(defconst hif-macro-expr-prefix-regexp
+  (concat hif-cpp-prefix "\\(if\\(n?def\\)?\\|elif\\|define\\)[ \t]+"))
+
+(defconst hif-white-regexp         "[ \t]*")
+(defconst hif-define-regexp
+  (concat hif-cpp-prefix "\\(define\\|undef\\)"))
+(defconst hif-id-regexp
+  (concat "[[:alpha:]_][[:alnum:]_]*"))
+(defconst hif-macroref-regexp
+  (concat hif-white-regexp "\\(" hif-id-regexp "\\)" hif-white-regexp
+          "\\("
+          "(" hif-white-regexp
+          "\\(" hif-id-regexp "\\)?" hif-white-regexp
+          "\\(" "," hif-white-regexp hif-id-regexp hif-white-regexp "\\)*"
+          "\\(\\.\\.\\.\\)?" hif-white-regexp
+          ")"
+          "\\)?" ))
 
 ;; Used to store the current token and the whole token list during parsing.
 ;; Only bound dynamically.
@@ -397,7 +423,12 @@
     ("/"   . hif-divide)
     ("%"   . hif-modulo)
     ("?"  . hif-conditional)
-    (":"  . hif-colon)))
+    (":"   . hif-colon)
+    (","   . hif-comma)
+    ("#"   . hif-stringify)
+    ("..." . hif-etc)))
+
+(defconst hif-valid-token-list (mapcar 'cdr hif-token-alist))
 
 (defconst hif-token-regexp
   (concat (regexp-opt (mapcar 'car hif-token-alist))
@@ -407,10 +438,29 @@
 
 (defconst hif-string-literal-regexp  "\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
 
+(defun hif-string-to-number (string &optional base)
+  "Like `string-to-number', but it understands non-decimal floats."
+  (if (or (not base) (= base 10))
+      (string-to-number string base)
+    (let* ((parts (split-string string "\\." t "[ \t]+"))
+           (frac (cadr parts))
+           (fraclen (length frac))
+           (quot (expt (if (zerop fraclen)
+                           base
+                           (* base 1.0)) fraclen)))
+      (/ (string-to-number (concat (car parts) frac) base) quot))))
+
+;; The dynamic binding variable `hif-simple-token-only' is shared only by
+;; `hif-tokenize' and `hif-find-define'. The purpose is to prevent 
`hif-tokenize'
+;; from returning one more value to indicate a simple token is scanned. This 
help
+;; speeding up macro evaluation on those very simple cases like integers or
+;; literals.
+;; Check the long comments before `hif-find-define' for more details. [lukelee]
 
 (defun hif-tokenize (start end)
   "Separate string between START and END into a list of tokens."
   (let ((token-list nil))
+    (setq hif-simple-token-only t)
     (with-syntax-table hide-ifdef-syntax-table
       (save-excursion
        (goto-char start)
@@ -423,8 +473,10 @@
            ((looking-at hif-string-literal-regexp)
             (push (substring-no-properties (match-string 1)) token-list)
             (goto-char (match-end 0)))
+
           ((looking-at hif-token-regexp)
-           (let ((token (buffer-substring (point) (match-end 0))))
+            (let ((token (buffer-substring-no-properties
+                          (point) (match-end 0))))
              (goto-char (match-end 0))
              ;; (message "token: %s" token) (sit-for 1)
              (push
@@ -432,22 +484,22 @@
                    (if (string-equal token "defined") 'hif-defined)
                    ;; TODO:
                    ;; 1. postfix 'l', 'll', 'ul' and 'ull'
-                   ;; 2. floating number formats
-                   ;; 3. hexadecimal/octal floats
-                   ;; 4. 098 is interpreted as octal conversion error
-                   ;; FIXME: string-to-number does not convert hex floats
+                   ;; 2. floating number formats (like 1.23e4)
+                   ;; 3. 098 is interpreted as octal conversion error
                    (if (string-match "0x\\([0-9a-fA-F]+\\.?[0-9a-fA-F]*\\)"
                                      token)
-                       (string-to-number (match-string 1 token) 16)) ;; hex
-                   ;; FIXME: string-to-number does not convert octal floats
+                       (hif-string-to-number (match-string 1 token) 16)) ;; hex
                    (if (string-match "\\`0[0-9]+\\(\\.[0-9]+\\)?\\'" token)
-                       (string-to-number token 8)) ;; octal
+                       (hif-string-to-number token 8)) ;; octal
                    (if (string-match "\\`[1-9][0-9]*\\(\\.[0-9]+\\)?\\'"
                                      token)
                        (string-to-number token)) ;; decimal
-                   (intern token))
+                   (prog1 (intern token)
+                     (setq hif-simple-token-only nil)))
                token-list)))
+
           (t (error "Bad #if expression: %s" (buffer-string)))))))
+
     (nreverse token-list)))
 
 ;;------------------------------------------------------------------------
@@ -482,9 +534,115 @@
   "Pop the next token from token-list into the let variable `hif-token'."
   (setq hif-token (pop hif-token-list)))
 
-(defun hif-parse-if-exp (token-list)
-  "Parse the TOKEN-LIST.  Return translated list in prefix form."
-  (let ((hif-token-list token-list))
+(defsubst hif-if-valid-identifier-p (id)
+  (not (or (numberp id)
+           (stringp id))))
+
+(defun hif-define-operator (tokens)
+  "`Upgrade' hif-define xxx to '(hif-define xxx)' so that it won't be
+subsitituted"
+  (let ((result nil)
+        (tok nil))
+    (while (setq tok (pop tokens))
+      (push
+       (if (eq tok 'hif-defined)
+           (progn
+             (setq tok (cadr tokens))
+             (if (eq (car tokens) 'hif-lparen)
+                 (if (and (hif-if-valid-identifier-p tok)
+                          (eq (caddr tokens) 'hif-rparen))
+                     (setq tokens (cdddr tokens))
+                   (error "#define followed by non-identifier: %S" tok))
+               (setq tok (car tokens)
+                     tokens (cdr tokens))
+               (unless (hif-if-valid-identifier-p tok)
+                 (error "#define followed by non-identifier: %S" tok)))
+             (list 'hif-defined 'hif-lparen tok 'hif-rparen))
+         tok)
+       result))
+    (nreverse result)))
+
+(defun hif-flatten (l)
+  "Flatten a tree"
+  (apply #'nconc
+         (mapcar (lambda (x) (if (listp x)
+                                 (hif-flatten x)
+                               (list x))) l)))
+
+(defun hif-expand-token-list (tokens &optional macroname expand_list)
+  "Perform expansion till everything expanded.  No self-reference expansion.
+  EXPAND_LIST is the list of macro names currently being expanded."
+  (catch 'self-referencing
+    (let ((expanded nil)
+          (remains (hif-define-operator
+                    (hif-token-concatenation
+                     (hif-token-stringification tokens))))
+          tok rep)
+      (if macroname
+          (setq expand_list (cons macroname expand_list)))
+      ;; Expanding all tokens till list exhausted
+      (while (setq tok (pop remains))
+        (if (memq tok expand_list)
+            ;; For self-referencing tokens, don't expand it
+            (throw 'self-referencing tokens))
+        (push
+         (cond
+          ((or (memq tok hif-valid-token-list)
+               (numberp tok)
+               (stringp tok))
+           tok)
+
+          ((setq rep (hif-lookup tok))
+           (if (and (listp rep)
+                    (eq (car rep) 'hif-define-macro)) ;; a defined macro
+               ;; Recursively expand it
+               (if (cadr rep) ;; Argument list is not nil
+                   (if (not (eq (car remains) 'hif-lparen))
+                       ;; No argument, no invocation
+                       tok
+                     ;; Argumented macro, get arguments and invoke it.
+                     ;; Dynamically bind hif-token-list and hif-token
+                     ;; for hif-macro-supply-arguments
+                     (let* ((hif-token-list (cdr remains))
+                            (hif-token nil)
+                            (parmlist (mapcar 'hif-expand-token-list
+                                              (hif-get-argument-list
+                                               tok)))
+                            (result
+                             (hif-expand-token-list
+                              (hif-macro-supply-arguments tok parmlist)
+                              tok expand_list)))
+                       (setq remains (cons hif-token hif-token-list))
+                       result))
+                 ;; Argument list is nil, direct expansion
+                 (setq rep (hif-expand-token-list
+                            (caddr rep) ;; Macro's token list
+                            tok expand_list))
+                 ;; Replace all remaining references immediately
+                 (setq remains (substitute tok rep remains))
+                 rep)
+             ;; Lookup tok returns an atom
+             rep))
+
+          ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
+          ;; this token might results in an incomplete expression that
+          ;; cannot be parsed further.
+          ;;((= 1 (hif-defined tok)) ;; defined (hif-defined tok)=1,
+          ;;  ;;but empty (hif-lookup tok)=nil, thus remove this token
+          ;; (setq remains (delete tok remains))
+          ;; nil)
+
+          (t ;; Usual IDs
+           tok))
+
+         expanded))
+
+      (hif-flatten (nreverse expanded)))))
+
+(defun hif-parse-exp (token-list &optional macroname)
+  "Parse the TOKEN-LIST.  Return translated list in prefix form.  MACRONAME
+is applied when invoking macros to prevent self-referencing macros."
+  (let ((hif-token-list (hif-expand-token-list token-list macroname)))
     (hif-nexttoken)
     (prog1
         (and hif-token
@@ -574,7 +732,8 @@
   "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
   (let ((result (hif-logshift-expr))
         (comp-token nil))
-    (while (memq hif-token '(hif-greater hif-less hif-greater-equal 
hif-less-equal))
+    (while (memq hif-token '(hif-greater hif-less hif-greater-equal
+                                         hif-less-equal))
       (setq comp-token hif-token)
       (hif-nexttoken)
       (setq result (list comp-token result (hif-logshift-expr))))
@@ -613,7 +772,8 @@
   result))
 
 (defun hif-factor ()
-  "Parse a factor: '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' 
| 'id(parmlist)' | strings | id."
+  "Parse a factor: '!' factor | '~' factor | '(' expr ')' |
+'defined(' id ')' | 'id(parmlist)' | strings | id."
   (cond
    ((eq hif-token 'hif-not)
     (hif-nexttoken)
@@ -646,6 +806,8 @@
 
    ((numberp hif-token)
     (prog1 hif-token (hif-nexttoken)))
+   ((stringp hif-token)
+    (hif-string-concatenation))
 
    ;; Unary plus/minus.
    ((memq hif-token '(hif-minus hif-plus))
@@ -653,10 +815,91 @@
 
    (t                                  ; identifier
     (let ((ident hif-token))
-      (if (memq ident '(or and))
-         (error "Error: missing identifier"))
       (hif-nexttoken)
-      `(hif-lookup (quote ,ident))))))
+      (if (eq hif-token 'hif-lparen)
+          (hif-place-macro-invocation ident)
+        `(hif-lookup (quote ,ident)))))))
+
+(defun hif-get-argument-list (ident)
+  (let ((nest 0)
+        (parmlist nil) ;; A "token" list of parameters, will later be parsed
+        (parm nil))
+
+    (while (or (not (eq (hif-nexttoken) 'hif-rparen))
+               (/= nest 0))
+      (if (eq (car (last parm)) 'hif-comma)
+          (setq parm nil))
+      (cond
+       ((eq hif-token 'hif-lparen)
+        (setq nest (1+ nest)))
+       ((eq hif-token 'hif-rparen)
+        (setq nest (1- nest)))
+       ((and (eq hif-token 'hif-comma)
+             (= nest 0))
+        (push (nreverse parm) parmlist)
+        (setq parm nil)))
+      (push hif-token parm))
+
+    (push (nreverse parm) parmlist) ;; Okay even if parm is nil
+    (hif-nexttoken) ;; Drop the hif-rparen, get next token
+    (nreverse parmlist)))
+
+(defun hif-place-macro-invocation (ident)
+  (let ((parmlist (hif-get-argument-list ident)))
+    `(hif-invoke (quote ,ident) (quote ,parmlist))))
+
+(defun hif-string-concatenation ()
+  "Parse concatenated strings: string | strings string"
+  (let ((result (substring-no-properties hif-token)))
+    (while (stringp (hif-nexttoken))
+      (setq result (concat
+                    (substring result 0 -1)    ; remove trailing '"'
+                    (substring hif-token 1)))) ; remove leading  '"'
+    result))
+
+(defun hif-define-macro (parmlist token-body)
+  "A marker for defined macro with arguments, cannot be evaluated alone with
+no parameters inputed."
+  ;;TODO: input arguments at run time, use minibuffer to query all arguments
+  (error
+   "Argumented macro cannot be evaluated without passing any parameter."))
+
+(defun hif-stringify (a)
+  "Stringify a number, string or symbol."
+  (cond
+   ((numberp a)
+    (number-to-string a))
+   ((atom a)
+    (symbol-name a))
+   ((stringp a)
+    (concat "\"" a "\""))
+   (t
+    (error "Invalid token to stringify"))))
+
+(defun intern-safe (str)
+  (if (stringp str)
+      (intern str)))
+
+(defun hif-token-concat (a b)
+  "Concatenate two tokens into a longer token, currently support only simple
+token concatenation.  Also support weird (but valid) token concatenation like
+'>' ## '>' becomes '>>'.  Here we take care only those that can be evaluated
+during preprocessing time and ignore all those that can only be evaluated at
+C(++) runtime (like '++', '--' and '+='...)."
+  (if (or (memq a hif-valid-token-list)
+          (memq b hif-valid-token-list))
+      (let* ((ra (car (rassq a hif-token-alist)))
+             (rb (car (rassq b hif-token-alist)))
+             (result (and ra rb
+                          (cdr (assoc (concat ra rb) hif-token-alist)))))
+        (or result
+            ;;(error "Invalid token to concatenate")
+            (error "Concatenating \"%s\" and \"%s\" does not give a valid \
+preprocessing token."
+                   (or ra (symbol-name a))
+                   (or rb (symbol-name b)))))
+    (intern-safe (concat (hif-stringify a)
+                         (hif-stringify b)))))
 
 (defun hif-mathify (val)
   "Treat VAL as a number: if it's t or nil, use 1 or 0."
@@ -719,23 +962,157 @@
         (setq result (funcall hide-ifdef-evaluator e))))
     result))
 
+(defun hif-token-stringification (l)
+  "Scan token list for 'hif-stringify' ('#') token and stringify the next
+token."
+  (let (result)
+    (while l
+      (push (if (eq (car l) 'hif-stringify)
+                (prog1
+                    (if (cadr l)
+                        (hif-stringify (cadr l))
+                      (error "No token to stringify"))
+                  (setq l (cdr l)))
+              (car l))
+            result)
+      (setq l (cdr l)))
+    (nreverse result)))
+
+(defun hif-token-concatenation (l)
+  "Scan token list for 'hif-token-concat' ('##') token and concatenate two
+tokens."
+  (let ((prev nil)
+        result)
+    (while l
+      (while (eq (car l) 'hif-token-concat)
+        (unless prev
+          (error "No token before ## to concatenate"))
+        (unless (cdr l)
+          (error "No token after ## to concatenate"))
+        (setq prev (hif-token-concat prev (cadr l)))
+        (setq l (cddr l)))
+      (if prev
+        (setq result (append result (list prev))))
+      (setq prev (car l)
+            l (cdr l)))
+    (if prev
+        (append result (list prev))
+      result)))
+
+(defun hif-delimit (lis atom)
+  (nconc (mapcan (lambda (l) (list l atom))
+                 (butlast lis))
+         (last lis)))
+
+;; Perform token replacement:
+(defun hif-macro-supply-arguments (macro-name actual-parms)
+  "Expand a macro call, replace ACTUAL-PARMS in the macro body."
+  (let* ((SA                   (assoc macro-name hide-ifdef-env))
+         (macro                (and SA
+                                    (cdr SA)
+                                    (eq (cadr SA) 'hif-define-macro)
+                                    (cddr SA)))
+         (formal-parms         (and macro (car macro)))
+         (macro-body           (and macro (cadr macro)))
+         (hide-ifdef-local-env nil) ; dynamic binding local table
+         actual-count
+         formal-count
+         actual
+         formal
+         etc)
+
+    (when (and actual-parms formal-parms macro-body)
+      ;; For each actual parameter, evaluate each one and associate it
+      ;; with the associated actual parameter, put it into local table and 
finally
+      ;; evaluate the macro body.
+      (if (setq etc (eq (car formal-parms) 'hif-etc))
+          ;; Take care of 'hif-etc first. Prefix 'hif-comma back if needed.
+          (setq formal-parms (cdr formal-parms)))
+      (setq formal-count (length formal-parms)
+            actual-count (length actual-parms))
+
+      (if (> formal-count actual-count)
+          (error "Too few parmameter for macro %S" macro-name)
+        (if (< formal-count actual-count)
+            (or etc
+                (error "Too many parameters for macro %S" macro-name))))
+
+      ;; Perform token replacement on the macro-body on the parameters
+      (while (setq formal (pop formal-parms))
+        ;; Prevent repetitive substitutation, thus cannot use 'subst'
+        ;; for example:
+        ;; #define mac(a,b) (a+b)
+        ;; #define testmac mac(b,y)
+        ;; testmac should expand to (b+y): replace of argument a and b
+        ;; occurs simultaneously, not sequentially. If sequentially,
+        ;; according to the argument order, it will become:
+        ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
+        ;;    becomes (b+b)
+        ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
+        ;;    becomes (y+y).
+        (setq macro-body
+              ;; Unlike 'subst', 'substitute' replace only the top level
+              ;; instead of the whole tree; more importantly, it's not
+              ;; destructive.
+              (substitute (if (and etc (null formal-parms))
+                              (hif-delimit actual-parms 'hif-comma)
+                            (car actual-parms))
+                          formal macro-body))
+        (setq actual-parms (cdr actual-parms)))
+
+      ;; Replacement completed, flatten the whole token list
+      (setq macro-body (hif-flatten macro-body))
+
+      ;; Stringification and token concatenation happens here
+      (hif-token-concatenation (hif-token-stringification macro-body)))))
+
+(defun hif-invoke (macro-name actual-parms)
+  "Invoke a macro by first expanding it, then reparse the macro-body,
+finally invoke the macro."
+    ;; Reparse the macro body and evaluate it
+    (funcall hide-ifdef-evaluator
+             (hif-parse-exp
+              (hif-macro-supply-arguments macro-name actual-parms)
+              macro-name)))
 
 ;;;----------- end of parser -----------------------
 
 
-(defun hif-canonicalize ()
-  "When at beginning of #ifX, return a Lisp expression for its condition."
+(defun hif-canonicalize-tokens (regexp) ;; for debugging
+  "Return the expanded result of the scanned tokens."
   (save-excursion
-    (let ((negate (looking-at hif-ifndef-regexp)))
-      (re-search-forward hif-ifx-regexp)
-      (let* ((tokens (hif-tokenize (point)
-                                  (progn (hif-end-of-line) (point))))
-            (expr (hif-parse-if-exp tokens)))
-       ;; (message "hif-canonicalized: %s" expr)
-       (if negate
-           (list 'hif-not expr)
-         expr)))))
+    (re-search-forward regexp)
+    (let* ((curr-regexp (match-string 0))
+           (defined (string-match hif-ifxdef-regexp curr-regexp))
+           (negate (and defined
+                        (string= (match-string 2 curr-regexp) "n")))
+           (hif-simple-token-only nil) ;; Dynamic binding var for 
`hif-tokenize'
+           (tokens (hif-tokenize (point)
+                                 (progn (hif-end-of-line) (point)))))
+      (if defined
+          (setq tokens (list 'hif-defined tokens)))
+      (if negate
+          (setq tokens (list 'hif-not tokens)))
+      tokens)))
 
+(defun hif-canonicalize (regexp)
+  "When at beginning of `regexp' (i.e. #ifX), return a Lisp expression for
+its condition."
+  (let ((case-fold-search nil))
+    (save-excursion
+      (re-search-forward regexp)
+      (let* ((curr-regexp (match-string 0))
+             (defined (string-match hif-ifxdef-regexp curr-regexp))
+             (negate (and defined
+                          (string= (match-string 2 curr-regexp) "n")))
+             (hif-simple-token-only nil) ;; Dynamic binding for `hif-tokenize'
+             (tokens (hif-tokenize (point)
+                                   (progn (hif-end-of-line) (point)))))
+        (if defined
+            (setq tokens (list 'hif-defined tokens)))
+        (if negate
+            (setq tokens (list 'hif-not tokens)))
+        (hif-parse-exp tokens)))))
 
 (defun hif-find-any-ifX ()
   "Move to next #if..., or #ifndef, at point or after."
@@ -746,10 +1123,10 @@
 
 
 (defun hif-find-next-relevant ()
-  "Move to next #if..., #else, or #endif, after the current line."
+  "Move to next #if..., #elif..., #else, or #endif, after the current line."
   ;; (message "hif-find-next-relevant at %d" (point))
   (end-of-line)
-  ;; avoid infinite recursion by only going to beginning of line if match found
+  ;; Avoid infinite recursion by only going to line-beginning if match found
   (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
       (beginning-of-line)))
 
@@ -757,7 +1134,7 @@
   "Move to previous #if..., #else, or #endif, before the current line."
   ;; (message "hif-find-previous-relevant at %d" (point))
   (beginning-of-line)
-  ;; avoid infinite recursion by only going to beginning of line if match found
+  ;; Avoid infinite recursion by only going to line-beginning if match found
   (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
      (beginning-of-line)))
 
@@ -769,15 +1146,19 @@
 (defun hif-looking-at-else ()
   (looking-at hif-else-regexp))
 
+(defun hif-looking-at-elif ()
+  (looking-at hif-elif-regexp))
 
 
 (defun hif-ifdef-to-endif ()
-  "If positioned at #ifX or #else form, skip to corresponding #endif."
+  "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
   ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
   (hif-find-next-relevant)
   (cond ((hif-looking-at-ifX)
         (hif-ifdef-to-endif) ; find endif of nested if
         (hif-ifdef-to-endif)) ; find outer endif or else
+        ((hif-looking-at-elif)
+         (hif-ifdef-to-endif))
        ((hif-looking-at-else)
         (hif-ifdef-to-endif)) ; find endif following else
        ((hif-looking-at-endif)
@@ -950,7 +1331,7 @@
 ;;; A bit slimy.
 
 (defun hif-hide-line (point)
-  "Hide the line containing point.  Does nothing if `hide-ifdef-lines' is nil."
+  "Hide the line containing point. Does nothing if `hide-ifdef-lines' is nil."
   (when hide-ifdef-lines
     (save-excursion
       (goto-char point)
@@ -994,7 +1375,7 @@
   "Called at #ifX expression, this hides those parts that should be hidden.
 It uses the judgment of `hide-ifdef-evaluator'."
   ;; (message "hif-possibly-hide") (sit-for 1)
-  (let ((test (hif-canonicalize))
+  (let ((test (hif-canonicalize hif-ifx-regexp))
        (range (hif-find-range)))
     ;; (message "test = %s" test) (sit-for 1)
 
@@ -1022,16 +1403,145 @@
     (goto-char (hif-range-end range))
     (end-of-line)))
 
-
+(defun hif-parse-macro-arglist (str)
+  "Parse argument list formatted as '( arg1 [ , argn] [...] )', including
+the '...'.  Return a list of the arguments, if '...' exists the first arg
+will be hif-etc."
+  (let* ((hif-simple-token-only nil) ;; Dynamic binding var for `hif-tokenize'
+         (tokenlist
+          (cdr (hif-tokenize
+                (- (point) (length str)) (point)))) ; remove hif-lparen
+         etc result token)
+    (while (not (eq (setq token (pop tokenlist)) 'hif-rparen))
+      (cond
+       ((eq token 'hif-etc)
+        (setq etc t))
+       ((eq token 'hif-comma)
+        t)
+       (t
+        (push token result))))
+    (if etc
+        (cons 'hif-etc (nreverse result))
+      (nreverse result))))
+
+;; The original version of hideif evaluates the macro early and store the
+;; final values for the defined macro into the symbol database (aka
+;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
+;; tree -> [value]". (The square bracket refers to what's stored in in our
+;; `hide-ifdef-env'.)
+;;
+;; This forbids the evaluation of an argumented macro since the parameters
+;; are applied at run time. In order to support argumented macro I then
+;; postponed the evaluation process one stage and store the "parsed tree"
+;; into symbol database. The evaluation process was then "strings -> tokens
+;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
+;; evaluate the parsed tree everytime when trying to expand the symbol. These
+;; temporarily code changes are obsolete and not in Emacs source repository.
+;;
+;; Furthermore, CPP did allow partial expression to be defined in several
+;; macros and later got concatenated into a complete expression and then
+;; evaluate it. In order to match this behavior I had to postpone one stage
+;; further, otherwise those partial expression will be fail on parsing and
+;; we'll miss all macros that reference it. The evaluation process thus
+;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
+;; performance since we need to parse tokens and evaluate them everytime
+;; when that symbol is referenced.
+;;
+;; In real cases I found a lot portion of macros are "simple macros" that
+;; expand to literals like integers or other symbols. In order to enhance
+;; the performance I use this `hif-simple-token-only' to notify my code and
+;; save the final [value] into symbol database. [lukelee]
+
+(defun hif-find-define (&optional min max)
+  "Parse texts and retrieve all defines within the region MIN and MAX."
+  (interactive)
+  (and min (goto-char min))
+  (and (re-search-forward hif-define-regexp max t)
+       (or
+        (let* ((defining (string= "define" (match-string 2)))
+               (name (and (re-search-forward hif-macroref-regexp max t)
+                          (match-string 1)))
+               (parsed nil)
+               (parmlist (and (match-string 3) ;; First arg id found
+                              (hif-parse-macro-arglist (match-string 2)))))
+          (if defining
+              ;; Ignore name (still need to return 't), or define the name
+              (or (and hide-ifdef-exclude-define-regexp
+                       (string-match hide-ifdef-exclude-define-regexp
+                                     name))
+
+                  (let* ((start (point))
+                         (end   (progn (hif-end-of-line) (point)))
+                         (hif-simple-token-only nil) ;; Dynamic binding
+                         (tokens
+                          (and name
+                               ;; `hif-simple-token-only' is set/clear
+                               ;; only in this block
+                               (condition-case nil
+                                   ;; Prevent C statements like
+                                   ;; 'do { ... } while (0)'
+                                   (hif-tokenize start end)
+                                 (error
+                                  ;; We can't just return nil here since
+                                  ;; this will stop hideif from searching
+                                  ;; for more #defines.
+                                  (setq hif-simple-token-only t)
+                                  (buffer-substring-no-properties
+                                   start end)))))
+                         ;; For simple tokens we save only the parsed result;
+                         ;; otherwise we save the tokens and parse it after
+                         ;; parameter replacement
+                         (expr (and tokens
+                                    ;; `hif-simple-token-only' is checked only
+                                    ;; here.
+                                    (or (and hif-simple-token-only
+                                             (listp tokens)
+                                             (= (length tokens) 1)
+                                             (hif-parse-exp tokens))
+                                        `(hif-define-macro ,parmlist
+                                                           ,tokens))))
+                         (SA (and name
+                                  (assoc (intern name) hide-ifdef-env))))
+                    (and name
+                         (if SA
+                             (or (setcdr SA expr) t)
+                           ;; Lazy evaluation, eval only if hif-lookup find it.
+                           ;; Define it anyway, even if nil it's still in list
+                           ;; and therefore considerred defined
+                           (push (cons (intern name) expr) hide-ifdef-env)))))
+            ;; #undef
+            (and name
+                 (hif-undefine-symbol (intern name))))))
+       t))
+
+
+(defun hif-add-new-defines (&optional min max)
+  "Scan and add all #define macros between MIN and MAX"
+  (interactive)
+  (save-excursion
+    (save-restriction
+      ;; (mark-region min max) ;; for debugging
+      (while (hif-find-define min max)
+        (setf min (point)))
+      (if max (goto-char max)
+        (goto-char (point-max))))))
 
 (defun hide-ifdef-guts ()
   "Does most of the work of `hide-ifdefs'.
 It does not do the work that's pointless to redo on a recursive entry."
   ;; (message "hide-ifdef-guts")
   (save-excursion
+    (let ((case-fold-search nil)
+          min max)
     (goto-char (point-min))
-    (while (hif-find-any-ifX)
-      (hif-possibly-hide))))
+      (setf min (point))
+      (loop do
+            (setf max (hif-find-any-ifX))
+            (hif-add-new-defines min max)
+            (if max
+                (hif-possibly-hide))
+            (setf min (point))
+            while max))))
 
 ;;===%%SF%% hide-ifdef-hiding (End)  ===
 
@@ -1045,7 +1555,8 @@
   (message "Hide-Read-Only %s"
           (if hide-ifdef-read-only "ON" "OFF"))
   (if hide-ifdef-hiding
-      (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
+      (setq buffer-read-only (or hide-ifdef-read-only
+                                 hif-outside-read-only)))
   (force-mode-line-update))
 
 (defun hide-ifdef-toggle-outside-read-only ()
@@ -1081,12 +1592,32 @@
   (hif-set-var var 1)
   (if hide-ifdef-hiding (hide-ifdefs)))
 
-(defun hide-ifdef-undef (var)
+(defun hif-undefine-symbol (var)
+  (setq hide-ifdef-env
+        (delete (assoc var hide-ifdef-env) hide-ifdef-env)))
+
+;;(defun hide-ifdef-undef (var)
+;;  "Undefine a VAR so that #ifdef VAR would not be included."
+;;  (interactive "SUndefine what? ")
+;;  ;;(hif-set-var var nil);;Luke fixed: set it nil is still considered
+;;  ;;defined so #ifdef VAR is still true.
+;;  (hif-undefine-symbol var)
+;;  (if hide-ifdef-hiding (hide-ifdefs)))
+
+(defun hide-ifdef-undef (start end)
   "Undefine a VAR so that #ifdef VAR would not be included."
-  (interactive "SUndefine what? ")
-  (hif-set-var var nil)
-  (if hide-ifdef-hiding (hide-ifdefs)))
-
+  (interactive "r")
+  (let* ((symstr
+          (or (and mark-active
+                   (buffer-substring-no-properties start end))
+              (read-string "Undefine what? " (current-word))))
+         (sym (and symstr
+                   (intern symstr))))
+    (if (zerop (hif-defined sym))
+        (message "`%s' not defined, no need to undefine it" symstr)
+      (hif-undefine-symbol sym)
+      (if hide-ifdef-hiding (hide-ifdefs))
+      (message "`%S' undefined" sym))))
 
 (defun hide-ifdefs (&optional nomsg)
   "Hide the contents of some #ifdefs.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]