Emacs/cc-ide: More flexible ccide-all-includes implementation
g0dil [Thu, 9 Aug 2007 12:13:35 +0000 (12:13 +0000)]
Emacs/cc-ide: Add interactive 'description' argument to ccide-gen-exception
Emacs/cc-ide: Implement C++ argument-declaration parser (recursive descent)

cc-ide/cc-ide.el
cc-ide/cc-parse.el [new file with mode: 0644]
cc-ide/recdesc.el [new file with mode: 0644]

index 67b7a44..50c1778 100644 (file)
 (defvar ccide-default-author "")
 (defvar ccide-default-copyright "")
 
-(defvar ccide-all-includes-guard nil)
+(defvar ccide-all-includes nil
+  "*If non-nil, this is the name of a file to include to fetch all
+includes of a project. This is used if single include files cannot be
+correctly included.")
 
 (defvar ccide-corba-skel-dir "")
 (defvar ccide-corba-idl-dir "")
        point add-file-vars)
     (push-mark)
     (goto-char (point-min))
-    (insert "// Copyright (C) " (number-to-string (nth 5 (decode-time)))
+    (insert "// $Id$\n"
+           "//\n"
+           "// Copyright (C) " (number-to-string (nth 5 (decode-time)))
            " " ccide-default-author "\n"
             ccide-default-copyright
             "\n")
           (insert "/** \\file\n"
                   "    \\brief " (ccide-file-name) " public header */\n\n"
                   "#ifndef " (ccide-file-macro-name) "\n"
-                  "#define " (ccide-file-macro-name) " 1\n\n"
-                   "// Custom includes\n\n"
+                  "#define " (ccide-file-macro-name) " 1\n\n")
+          (if ccide-all-includes
+              (insert "#ifndef " (ccide-file-macro-name ccide-all-includes) "\n"
+                      "#error \"Don't include '" (file-name-nondirectory (buffer-file-name)) "'"
+                      " directly, include '" ccide-all-includes "'\"\n"
+                      "#endif\n\n"))
+          (insert "// Custom includes\n\n"
                    "//#include \"" (ccide-file-name ".mpp") "\"\n"
                    "///////////////////////////////hh.p////////////////////////////////////////\n\n")
           (setq point (point))
           (goto-char (point-max))
           (insert "\n\n///////////////////////////////hh.e////////////////////////////////////////\n")
-          (if ccide-all-includes-guard
+          (if ccide-all-includes
               (insert "#endif\n"
-                      "#if !defined(" ccide-all-includes-guard ") && !defined(" (ccide-file-macro-name) "i_)\n"
+                      "#if !defined(" (ccide-file-macro-name ccide-all-includes) "_decls_) "
+                      "&& !defined(" (ccide-file-macro-name) "i_)\n"
                       "#define " (ccide-file-macro-name) "i_\n"))
           (insert "//#include \"" (ccide-file-name ".cci") "\"\n"
                   "//#include \"" (ccide-file-name ".ct") "\"\n"
                   "//#include \"" (ccide-file-name ".hh") "\"\n"
                   "//#include \"" (ccide-file-name ".ih") "\"\n\n"
                    "// Custom includes\n"
-                   "#include \"" (ccide-file-name ".hh" (ccide-file-name)) "\"\n\n"
+                   "#include \"" (or ccide-all-includes 
+                                    (ccide-file-name ".hh" (ccide-file-name))) "\"\n\n"
                    "#include <boost/test/auto_unit_test.hpp>\n"
                    "#include <boost/test/test_tools.hpp>\n\n"
                   "#define prefix_\n"
               (string-match "\\.cpp$" (buffer-file-name)))
           (insert "/** \\file\n"
                   "    \\brief " (ccide-file-name) " non-inline non-template implementation */\n\n"
-                  "//#include \"" (ccide-file-name ".hh") "\"\n"
+                  (if ccide-all-includes "" "//")
+                  "#include \"" (or ccide-all-includes (ccide-file-name ".hh")) "\"\n"
                   "//#include \"" (ccide-file-name ".ih") "\"\n\n"
                    "// Custom includes\n\n"
                    "//#include \"" (ccide-file-name ".mpp") "\"\n"
@@ -650,7 +663,7 @@ copy constructor, assignment operator and destructor."
     (message name)))
 
 (defun ccide-gen-exception (class &optional description)
-  (interactive "sException name: ")
+  (interactive "sException name: \nsDescription (defaults to full class name): ")
   (beginning-of-line)
   (open-line 1)
   (indent-according-to-mode)
@@ -662,7 +675,11 @@ copy constructor, assignment operator and destructor."
       (insert "struct " class " : public std::exception\n"
              ofs "{ virtual char const * what() const throw() ")
       (setq p (point))
-      (insert "{ return \"" prefix "::" class "\"; } };")
+      (insert "{ return \"" 
+             (if (and description (> (length description) 0))
+                 description
+               (concat prefix "::" class))
+             "\"; } };")
       (if (> (current-column) fill-column)
          (save-excursion
            (goto-char p)
@@ -679,7 +696,8 @@ copy constructor, assignment operator and destructor."
       (progn
        (c-forward-out-of-comment)
        (c-backward-syntactic-ws)
-       (c-backward-sexp)))
+       (c-backward-sexp))
+    (beginning-of-line))
   (c-beginning-of-defun-or-decl)
   (let ((defun (c-get-defun-state))
        (indent (make-string comment-column ? ))
diff --git a/cc-ide/cc-parse.el b/cc-ide/cc-parse.el
new file mode 100644 (file)
index 0000000..529eb56
--- /dev/null
@@ -0,0 +1,135 @@
+;;; cc-parse.el --- Generator for recursive-descent parsers
+;;
+;; Copyright (C) 2007 Stefan Bund
+
+;; cc-parse.el is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2, or (at
+;; your option) any later version.
+
+;; cc-parse.el is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; This is a very rudimentary parser to parse a single C++ argument
+;; declaration. The goal of this parser is, given an argument
+;; declaration find the name of the argument and the position of that
+;; name within the declaration.
+;;
+;; The current version does not support string- or charachter literals
+;; and comments within an argument declaration.
+
+;;;; Code:
+
+(require 'recdesc)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun cc-parse-arg (arg)
+  ;; Returns a cons ( POSITION . NAME ) where POSITION is the position
+  ;; of the argument in ARG and NAME is the name of that
+  ;; argument. Will return 'nil, if the argument name is omitted in
+  ;; the declaration.
+  (recdesc-scan-string 'decl arg))
+
+(recdesc-declare
+
+ (term optional-whitespace ()
+   (match "[ \t\n\r]*"))
+
+ (term word ()
+   (match optional-whitespace)
+   (collect (match "\\w+\\b"))
+   (return (cons (- position (length (car elements)))
+                (car elements))))
+
+ (term symbol ()
+   (match optional-whitespace)
+   (match word)
+   (maybe (repeat (match optional-whitespace)
+                 (match "::")
+                 (match optional-whitespace)
+                 (match word)))
+   (match optional-whitespace)
+   (maybe (match "<")
+         (match tokens)
+         (match optional-whitespace)
+         (match ">")))
+
+ (term tokens ()
+   (match optional-whitespace)
+   (or (and (match "(") (commit) (match tokens) (match ")"))
+       (and (match "\\[") (commit) (match tokens) (match "\\]"))
+       (and (match "{") (commit) (match tokens) (match "}"))
+       (and (match "<") (commit) (match tokens) (match ">"))
+       (match "[^][(){}<>]*")))
+ (term decl ()
+   (match optional-whitespace)
+   (collect (or (match primary)
+               (match group)
+               (match member-pointer)
+               (match modifier)
+               (match word)))
+   (maybe (match arglist))
+   (maybe (repeat (match array)))
+   (match optional-whitespace)
+   (return (car elements)))
+
+ (term primary ()
+   (match optional-whitespace)
+   (match symbol)
+   (match optional-whitespace)
+   (collect (match decl))
+   (return (car elements)))
+
+ (term group ()
+   (match optional-whitespace)
+   (match "(")
+   (collect (match decl))
+   (match optional-whitespace)
+   (match ")")
+   (return (car elements)))
+
+ (term member-pointer ()
+   (match optional-whitespace)
+   (match symbol) 
+   (match optional-whitespace) 
+   (match "::")
+   (match optional-whitespace)
+   (match "*")
+   (commit)
+   (match decl))
+
+ (term modifier ()
+   (match optional-whitespace)
+   (match "const\\|volatile\\|\\*\\|&")
+   (commit)
+   (collect (match decl))
+   (return (car elements)))
+ (term array ()
+   (match optional-whitespace)
+   (match "\\[")
+   (commit)
+   (match tokens)
+   (match optional-whitespace)
+   (match "\\]")
+   (return (car elements)))
+ (term arglist ()
+   (match optional-whitespace)
+   (match "(")
+   (commit)
+   (match tokens)
+   (match optional-whitespace)
+   (match ")")))
+
+;; To debug a term, place the name of the term's function into 'fn',
+;; place the curor at the end of the line and presse C-x C-e. You may
+;; then use 'edebug-defun' on the declaration produced by this.
+;;
+;; (let ((fn 'recdesc@@decl)) (save-excursion (insert (prin1-to-string (symbol-function fn)))) (insert "\n\n") (forward-char 1) (delete-char 6) (insert "defun " (symbol-name fn)) (forward-sexp) (insert "\n") (let ((start (point))) (forward-line 1) (replace-string ")" ")\n" nil start (point)) (indent-region (save-excursion (goto-char start) (forward-line -1) (point)) (point) nil))) 
+
+(provide cc-parse)
diff --git a/cc-ide/recdesc.el b/cc-ide/recdesc.el
new file mode 100644 (file)
index 0000000..90b4c04
--- /dev/null
@@ -0,0 +1,271 @@
+;;; recdesc.el --- Generator for recursive-descent parsers
+;;
+;; Copyright (C) 2000,2007 Stefan Bund
+
+;; recdesc.el is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2, or (at
+;; your option) any later version.
+
+;; recdesc.el is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;;; Commentary:
+
+;; Der Syntax einer term deklaration ist:
+
+;; <term-declaration> := (term <symbol> <arglist> <command>...)
+;;
+;; <command> := <directive>
+;;            | (collect <directive-or-form>)
+;;            | (return <form>)
+;; 
+;; <directive-or-form> := <directive> | <form>
+;; 
+;; <diretive> :=  (match <term> [<form>...])
+;;             |  (or <command>...)
+;;             |  (and <command>...)
+;;             |  (maybe <command>...)
+;;             |  (repeat <command>...)
+;;             |  (commit)
+;;             |  (fail)
+;;             |  (progn <lisp-code>...)
+;;
+;; <term> := <symbol> | <form>
+
+;; 
+;;
+
+;;; Change-Log:
+
+;; $Log: recdesc.el,v $
+;; Revision 1.3  2000/02/13 21:19:56  bund
+;; Erste vollständige version von SqIV2
+;;
+;; Revision 1.2  2000/02/03 10:15:19  bund
+;; *** empty log message ***
+;;
+;; Revision 1.1  2000/02/01 13:26:03  bund
+;; *** empty log message ***
+;;
+;;
+
+;;; Variables:
+
+;;; Code:
+
+(require 'cl)
+
+(declaim (special position))
+(declaim (special string))
+(declaim (special commit-flag))
+(declaim (special saved-position))
+(declaim (special saved-elements))
+(declaim (special elements))
+(declaim (special return-form))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Zunächst der Parser/Scanner zur deklaration von Termen
+
+(defmacro recdesc-deferror (name message &rest classes)
+  `(progn
+     (put ',name 'error-conditions '(error sqi-error ,@classes ,name))
+     (put ',name 'error-message ,message)))
+
+(defmacro recdesc-declare (&rest forms)
+  (cons 'progn
+        (loop for form in forms
+              collect (if (and (consp form)
+                               (eq (car form) 'term)
+                               (consp (cdr form))
+                               (symbolp (cadr form))
+                               (consp (cddr form))
+                               (listp (caddr form)))
+                          (recdesc-declare-term (cadr form)
+                                                    (caddr form)
+                                                    (cdddr form))
+                        (signal 'recdesc-invalid-decl-component
+                                (list form))))))
+
+(recdesc-deferror recdesc-invalid-decl-component
+  "Invalid component in recdesc-declare" recdesc-error)
+
+(defun recdesc-declare-term (name arglist body)
+  (let (return-form)
+    `(defun ,(intern (concat "recdesc@@" (symbol-name name))) ,arglist
+       (let (elements)
+         (or (and ,(recdesc-parse-directive/and body)
+                 ,(if return-form
+                      return-form
+                    '(or elements t))))))))
+
+(defun recdesc-parse-command (command)
+  (if (not (consp command))
+      (signal 'recdesc-invalid-command
+              (list command)))
+  (cond ((eq (car command) 'return)
+         (if (not (consp (cdr command)))
+             (signal 'recdesc-invalid-command
+                     (list command)))
+         (setq return-form (cadr command))
+         t)
+        ((eq (car command) 'collect)
+         (if (not (consp (cdr command)))
+             (signal 'recdesc-invalid-command (list command)))
+         `(recdesc-collect ,(recdesc-parse-directive-or-form (cadr command))))
+        (t (recdesc-parse-directive command))))
+
+(recdesc-deferror recdesc-invalid-command
+  "Invalid scanner command" recdesc-error)
+
+(defun recdesc-parse-directive-or-form (directive-or-form)
+  (or (recdesc-parse-directive directive-or-form t)
+      directive-or-form))
+
+(defun recdesc-parse-directive (directive &optional nosignal)
+  (if (not (consp directive))
+      (if (not nosignal)
+          (signal 'recdesc-invalid-directive
+                  (list directive)))
+    (cond ((eq (car directive) 'match)   (recdesc-parse-directive/match   (cdr directive)))
+          ((eq (car directive) 'or)      (recdesc-parse-directive/or      (cdr directive)))
+          ((eq (car directive) 'and)     (recdesc-parse-directive/and     (cdr directive)))
+          ((eq (car directive) 'maybe)   (recdesc-parse-directive/maybe   (cdr directive)))
+          ((eq (car directive) 'repeat)  (recdesc-parse-directive/repeat  (cdr directive)))
+          ((eq (car directive) 'commit)  (recdesc-parse-directive/commit  (cdr directive)))
+          ((eq (car directive) 'fail)    (recdesc-parse-directive/fail    (cdr directive)))
+          ((eq (car directive) 'progn)   (recdesc-parse-directive/progn   (cdr directive)))
+          (t (if (not nosignal)
+                 (signal 'recdesc-invalid-directive
+                         (list directive)))))))
+
+(recdesc-deferror recdesc-invalid-directive
+  "Invalid scanner directive" recdesc-error)
+
+(defun recdesc-parse-directive/match (args)
+  (if (not (consp args))
+      (signal 'recdesc-invalid-match-arguments
+              (list args)))
+  (if (symbolp (car args))
+      (cons (intern (concat "recdesc@@" (symbol-name (car args))))
+            (cdr args))
+    (if (not (null (cdr args)))
+        (signal 'recdesc-invalid-match-arguments
+                (list args)))
+    (list 'recdesc-match-regexp (car args))))
+
+(recdesc-deferror recdesc-invalid-match-arguments
+  "Invalid arguments to match directive" recdesc-error)
+
+(defun recdesc-parse-directive/or (args)
+  (if (not (consp args))
+      (signal 'recdesc-invalid-or-arguments
+              (list args)))
+  (cons 'or
+        (loop for command in args
+              collect (recdesc-parse-directive/and (list command)))))
+
+(recdesc-deferror recdesc-invalid-or-arguments
+  "Invalid arguments to or directive" recdesc-error)
+
+(defun recdesc-parse-directive/and (args)
+  (if (not (consp args))
+      (signal 'recdesc-invalid-and-arguments
+              (list args)))
+  `(let ((saved-position position)
+         (saved-elements elements)
+         commit-flag)
+     (or (and ,@(loop for command in args
+                      collect (recdesc-parse-command command)))
+         (recdesc-backtrack))))
+
+(recdesc-deferror recdesc-invalid-and-arguments
+  "Invalid arguments to and directive" recdesc-error)
+
+(defun recdesc-parse-directive/maybe (args)
+  (if (not (consp args))
+      (signal 'recdesc-invalid-maybe-arguments
+              (list args)))
+  `(or ,(recdesc-parse-directive/and args) t))
+
+(defun recdesc-parse-directive/repeat (args)
+  `(loop for repeat-item = ,(recdesc-parse-directive/and args)
+         while repeat-item
+         collect repeat-item))
+
+(defun recdesc-parse-directive/commit (args)
+  (if args
+      (signal 'recdesc-invalid-commit-arguments
+              (list args)))
+  `(setq commit-flag t))
+
+(recdesc-deferror recdesc-invalid-commit-arguments
+  "Invalid arguments to commit directive" recdesc-error)
+
+(defun recdesc-parse-directive/fail (args)
+  (if args
+      (signal 'recdesc-invalid-fail-arguments
+              (list args)))
+  '(throw 'recdesc-failed nil))
+
+(recdesc-deferror recdesc-invalid-fail-arguments
+  "Invalid arguments to fail directive" recdesc-error)
+
+(defun recdesc-parse-directive/progn (args)
+  `(progn ,@args))
+
+(put 'term 'lisp-indent-function 'defun)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Die folgenden Funktionen werden von den durch recdesc-declare
+;; erzeugten termen benötigt
+
+(defun recdesc-backtrack ()
+  (progn
+    (if commit-flag
+       (throw 'recdesc-failed nil))
+    (setq position saved-position
+         elements saved-elements)
+    nil))
+
+(defun recdesc-collect (value)
+  (if value
+      (progn
+       (setq elements (nconc elements 
+                             (list value)))
+       value)))
+
+(defun recdesc-match-regexp (regexp)
+  (if (string-match (concat "^" regexp) (substring string position))
+      (let ((start position))
+       (setq position (+ position (match-end 0)))
+       (substring string start position))))
+
+(defun recdesc-match (term &rest args)
+  (apply (intern (concat "recdesc@@" (symbol-name term))) args))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;; Toplevel calls to generated scanners
+
+(defun recdesc-scan-string (term string &rest args)
+  (let ((scanner (intern (concat "recdesc@@"
+                                (symbol-name term))))
+       (position 0)
+       rv)
+    (setq rv (catch 'recdesc-failed
+              (apply scanner args)))
+    (if (not (string-match "^[ \t\n\r]*$" (substring string position)))
+       nil
+      rv)))
+
+(provide 'recdesc)
+
+\f
+;;; Local Variables:
+;;; elisp-project-autoload-file-name: "sqi-autoload.el"
+;;; End: