summaryrefslogtreecommitdiff
path: root/lisp/cedet/srecode/compile.el
diff options
context:
space:
mode:
authorChong Yidong <cyd@stupidchicken.com>2009-09-20 21:06:41 +0000
committerChong Yidong <cyd@stupidchicken.com>2009-09-20 21:06:41 +0000
commit4d902e6f13f6bf5d304a0cbcff33e2780a825206 (patch)
tree20c5dbf4febbaff55e22b4fa0e950cf552e88e70 /lisp/cedet/srecode/compile.el
parent70702e9b0ea781fb955c66320c935bc0a8e1d0f1 (diff)
downloademacs-4d902e6f13f6bf5d304a0cbcff33e2780a825206.tar.gz
lisp/cedet/srecode.el:
lisp/cedet/srecode/*.el: test/cedet/srecode-tests.el: New files lisp/files.el (auto-mode-alist): Use srecode-template-mode for .srt files. lisp/cedet/semantic/bovine/scm.el: Add local vars section for autoloading.
Diffstat (limited to 'lisp/cedet/srecode/compile.el')
-rw-r--r--lisp/cedet/srecode/compile.el640
1 files changed, 640 insertions, 0 deletions
diff --git a/lisp/cedet/srecode/compile.el b/lisp/cedet/srecode/compile.el
new file mode 100644
index 00000000000..f744b052162
--- /dev/null
+++ b/lisp/cedet/srecode/compile.el
@@ -0,0 +1,640 @@
+;;; srecode/compile --- Compilation of srecode template files.
+
+;;; Copyright (C) 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <zappo@gnu.org>
+;; Keywords: codegeneration
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Compile a Semantic Recoder template file.
+;;
+;; Template files are parsed using a Semantic/Wisent parser into
+;; a tag table. The code therin is then further parsed down using
+;; a regular expression parser.
+;;
+;; The output are a series of EIEIO objects which represent the
+;; templates in a way that could be inserted later.
+
+(require 'semantic)
+(require 'eieio)
+(require 'eieio-base)
+(require 'srecode)
+(require 'srecode/table)
+
+(declare-function srecode-template-inserter-newline-child-p "srecode/insert")
+(declare-function srecode-create-section-dictionary "srecode/dictionary")
+(declare-function srecode-dictionary-compound-variable "srecode/dictionary")
+
+;;; Code:
+
+;;; Template Class
+;;
+;; Templatets describe a patter of text that can be inserted into a
+;; buffer.
+;;
+(defclass srecode-template (eieio-named)
+ ((context :initarg :context
+ :initform nil
+ :documentation
+ "Context this template belongs to.")
+ (args :initarg :args
+ :documentation
+ "List of arguments that this template requires.")
+ (code :initarg :code
+ :documentation
+ "Compiled text from the template.")
+ (dictionary :initarg :dictionary
+ :type (or null srecode-dictionary)
+ :documentation
+ "List of section dictinaries.
+The compiled template can contain lists of section dictionaries,
+or values that are expected to be passed down into different
+section macros. The template section dictionaries are merged in with
+any incomming dictionaries values.")
+ (binding :initarg :binding
+ :documentation
+ "Preferred keybinding for this template in `srecode-minor-mode-map'.")
+ (active :allocation :class
+ :initform nil
+ :documentation
+ "During template insertion, this is the stack of active templates.
+The top-most template is the 'active' template. Use the accessor methods
+for push, pop, and peek for the active template.")
+ (table :initarg :table
+ :documentation
+ "The table this template lives in.")
+ )
+ "Class defines storage for semantic recoder templates.")
+
+(defun srecode-flush-active-templates ()
+ "Flush the active template storage.
+Useful if something goes wrong in SRecode, and the active tempalte
+stack is broken."
+ (interactive)
+ (if (oref srecode-template active)
+ (when (y-or-n-p (format "%d active templates. Flush? "
+ (length (oref srecode-template active))))
+ (oset-default srecode-template active nil))
+ (message "No active templates to flush."))
+ )
+
+;;; Inserters
+;;
+;; Each inserter object manages a different thing that
+;; might be inserted into a template output stream.
+;;
+;; The 'srecode-insert-method' on each inserter does the actual
+;; work, and the smaller, simple inserter object is saved in
+;; the compiled templates.
+;;
+;; See srecode-insert.el for the specialized classes.
+;;
+(defclass srecode-template-inserter (eieio-named)
+ ((secondname :initarg :secondname
+ :type (or null string)
+ :documentation
+ "If there is a colon in the inserter's name, it represents
+additional static argument data."))
+ "This represents an item to be inserted via a template macro.
+Plain text strings are not handled via this baseclass."
+ :abstract t)
+
+(defmethod srecode-parse-input ((ins srecode-template-inserter)
+ tag input STATE)
+ "For the template inserter INS, parse INPUT.
+Shorten input only by the amount needed.
+Return the remains of INPUT.
+STATE is the current compilation state."
+ input)
+
+(defmethod srecode-match-end ((ins srecode-template-inserter) name)
+ "For the template inserter INS, do I end a section called NAME?"
+ nil)
+
+(defmethod srecode-inserter-apply-state ((ins srecode-template-inserter) STATE)
+ "For the template inserter INS, apply information from STATE."
+ nil)
+
+(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter)
+ escape-start escape-end)
+ "Insert an example using inserter INS.
+Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
+ (princ " ")
+ (princ escape-start)
+ (when (and (slot-exists-p ins 'key) (oref ins key))
+ (princ (format "%c" (oref ins key))))
+ (princ "VARNAME")
+ (princ escape-end)
+ (terpri)
+ )
+
+
+;;; Compile State
+(defclass srecode-compile-state ()
+ ((context :initform "declaration"
+ :documentation "The active context.")
+ (prompts :initform nil
+ :documentation "The active prompts.")
+ (escape_start :initform "{{"
+ :documentation "The starting escape sequence.")
+ (escape_end :initform "}}"
+ :documentation "The ending escape sequence.")
+ )
+ "Current state of the compile.")
+
+(defmethod srecode-compile-add-prompt ((state srecode-compile-state)
+ prompttag)
+ "Add PROMPTTAG to the current list of prompts."
+ (with-slots (prompts) state
+ (let ((match (assoc (semantic-tag-name prompttag) prompts))
+ (newprompts prompts))
+ (when match
+ (let ((tmp prompts))
+ (setq newprompts nil)
+ (while tmp
+ (when (not (string= (car (car tmp))
+ (car prompttag)))
+ (setq newprompts (cons (car tmp)
+ newprompts)))
+ (setq tmp (cdr tmp)))))
+ (setq prompts (cons prompttag newprompts)))
+ ))
+
+;;; TEMPLATE COMPILER
+;;
+(defun srecode-compile-file (fname)
+ "Compile the templates from the file FNAME."
+ (let ((peb (get-file-buffer fname)))
+ (save-excursion
+ ;; Make whatever it is local.
+ (if (not peb)
+ (set-buffer (semantic-find-file-noselect fname))
+ (set-buffer peb))
+ ;; Do the compile.
+ (srecode-compile-templates)
+ ;; Trash the buffer if we had to read it in.
+ (if (not peb)
+ (kill-buffer (current-buffer)))
+ )))
+
+;;;###autoload
+(defun srecode-compile-templates ()
+ "Compile a semantic recode template file into a mode-local variable."
+ (interactive)
+ (require 'srecode-insert)
+ (message "Compiling template %s..."
+ (file-name-nondirectory (buffer-file-name)))
+ (let ((tags (semantic-fetch-tags))
+ (tag nil)
+ (class nil)
+ (table nil)
+ (STATE (srecode-compile-state (file-name-nondirectory
+ (buffer-file-name))))
+ (mode nil)
+ (application nil)
+ (priority nil)
+ (vars nil)
+ )
+
+ ;;
+ ;; COMPILE
+ ;;
+ (while tags
+ (setq tag (car tags)
+ class (semantic-tag-class tag))
+ ;; What type of item is it?
+ (cond
+ ;; CONTEXT tags specify the context all future tags
+ ;; belong to.
+ ((eq class 'context)
+ (oset STATE context (semantic-tag-name tag))
+ )
+
+ ;; PROMPT tags specify prompts for dictionary ? inserters
+ ;; which appear in the following templates
+ ((eq class 'prompt)
+ (srecode-compile-add-prompt STATE tag)
+ )
+
+ ;; VARIABLE tags can specify operational control
+ ((eq class 'variable)
+ (let* ((name (semantic-tag-name tag))
+ (value (semantic-tag-variable-default tag))
+ (firstvalue (car value)))
+ ;; If it is a single string, and one value, then
+ ;; look to see if it is one of our special variables.
+ (if (and (= (length value) 1) (stringp firstvalue))
+ (cond ((string= name "mode")
+ (setq mode (intern firstvalue)))
+ ((string= name "escape_start")
+ (oset STATE escape_start firstvalue)
+ )
+ ((string= name "escape_end")
+ (oset STATE escape_end firstvalue)
+ )
+ ((string= name "application")
+ (setq application (read firstvalue)))
+ ((string= name "priority")
+ (setq priority (read firstvalue)))
+ (t
+ ;; Assign this into some table of variables.
+ (setq vars (cons (cons name firstvalue) vars))
+ ))
+ ;; If it isn't a single string, then the value of the
+ ;; variable belongs to a compound dictionary value.
+ ;;
+ ;; Create a compound dictionary value from "value".
+ (require 'srecode/dictionary)
+ (let ((cv (srecode-dictionary-compound-variable
+ name :value value)))
+ (setq vars (cons (cons name cv) vars)))
+ ))
+ )
+
+ ;; FUNCTION tags are really templates.
+ ((eq class 'function)
+ (setq table (cons (srecode-compile-one-template-tag tag STATE)
+ table))
+ )
+
+ ;; Ooops
+ (t (error "Unknown TAG class %s" class))
+ )
+ ;; Continue
+ (setq tags (cdr tags)))
+
+ ;; MSG - Before install since nreverse whacks our list.
+ (message "%d templates compiled for %s"
+ (length table) mode)
+
+ ;;
+ ;; APPLY TO MODE
+ ;;
+ (if (not mode)
+ (error "You must specify a MODE for your templates"))
+
+ ;;
+ ;; Calculate priority
+ ;;
+ (if (not priority)
+ (let ((d (file-name-directory (buffer-file-name)))
+ (sd (file-name-directory (locate-library "srecode")))
+ (defaultdelta (if (eq mode 'default) 20 0)))
+ (if (string= d sd)
+ (setq priority (+ 80 defaultdelta))
+ (setq priority (+ 30 defaultdelta)))
+ (message "Templates %s has estimated priority of %d"
+ (file-name-nondirectory (buffer-file-name))
+ priority))
+ (message "Compiling templates %s priority %d... done!"
+ (file-name-nondirectory (buffer-file-name))
+ priority))
+
+ ;; Save it up!
+ (srecode-compile-template-table table mode priority application vars)
+ )
+)
+
+(defun srecode-compile-one-template-tag (tag STATE)
+ "Compile a template tag TAG into an srecode template class.
+STATE is the current compile state as an object `srecode-compile-state'."
+ (require 'srecode/dictionary)
+ (let* ((context (oref STATE context))
+ (codeout (srecode-compile-split-code
+ tag (semantic-tag-get-attribute tag :code)
+ STATE))
+ (code (cdr codeout))
+ (args (semantic-tag-function-arguments tag))
+ (binding (semantic-tag-get-attribute tag :binding))
+ (rawdicts (semantic-tag-get-attribute tag :dictionaries))
+ (sdicts (srecode-create-section-dictionary rawdicts STATE))
+ (addargs nil)
+ )
+; (message "Compiled %s to %d codes with %d args and %d prompts."
+; (semantic-tag-name tag)
+; (length code)
+; (length args)
+; (length prompts))
+ (while args
+ (setq addargs (cons (intern (car args)) addargs))
+ (when (eq (car addargs) :blank)
+ ;; If we have a wrap, then put wrap inserters on both
+ ;; ends of the code.
+ (setq code (append
+ (list (srecode-compile-inserter "BLANK"
+ "\r"
+ STATE
+ :secondname nil
+ :where 'begin))
+ code
+ (list (srecode-compile-inserter "BLANK"
+ "\r"
+ STATE
+ :secondname nil
+ :where 'end))
+ )))
+ (setq args (cdr args)))
+ (srecode-template (semantic-tag-name tag)
+ :context context
+ :args (nreverse addargs)
+ :dictionary sdicts
+ :binding binding
+ :code code)
+ ))
+
+(defun srecode-compile-do-hard-newline-p (comp)
+ "Examine COMP to decide if the upcoming newline should be hard.
+It is hard if the previous inserter is a newline object."
+ (while (and comp (stringp (car comp)))
+ (setq comp (cdr comp)))
+ (or (not comp)
+ (require 'srecode/insert)
+ (srecode-template-inserter-newline-child-p (car comp))))
+
+(defun srecode-compile-split-code (tag str STATE
+ &optional end-name)
+ "Split the code for TAG into something templatable.
+STR is the string of code from TAG to split.
+STATE is the current compile state.
+ESCAPE_START and ESCAPE_END are regexps that indicate the beginning
+escape character, and end escape character pattern for expandable
+macro names.
+Optional argument END-NAME specifies the name of a token upon which
+parsing should stop.
+If END-NAME is specified, and the input string"
+ (let* ((what str)
+ (end-token nil)
+ (comp nil)
+ (regex (concat "\n\\|" (regexp-quote (oref STATE escape_start))))
+ (regexend (regexp-quote (oref STATE escape_end)))
+ )
+ (while (and what (not end-token))
+ (cond
+ ((string-match regex what)
+ (let* ((prefix (substring what 0 (match-beginning 0)))
+ (match (substring what
+ (match-beginning 0)
+ (match-end 0)))
+ (namestart (match-end 0))
+ (junk (string-match regexend what namestart))
+ end tail name)
+ ;; Add string to compiled output
+ (when (> (length prefix) 0)
+ (setq comp (cons prefix comp)))
+ (if (string= match "\n")
+ ;; Do newline thingy.
+ (let ((new-inserter
+ (srecode-compile-inserter
+ "INDENT"
+ "\n"
+ STATE
+ :secondname nil
+ ;; This newline is "hard" meaning ALWAYS do it
+ ;; if the previous entry is also a newline.
+ ;; Without it, user entered blank lines will be
+ ;; ignored.
+ :hard (srecode-compile-do-hard-newline-p comp)
+ )))
+ ;; Trim WHAT back.
+ (setq what (substring what namestart))
+ (when (> (length what) 0)
+ ;; make the new inserter, but only if we aren't last.
+ (setq comp (cons new-inserter comp))
+ ))
+ ;; Regular inserter thingy.
+ (setq end (if junk
+ (match-beginning 0)
+ (error "Could not find end escape for %s"
+ (semantic-tag-name tag)))
+ tail (match-end 0))
+ (cond ((not end)
+ (error "No matching escape end for %s"
+ (semantic-tag-name tag)))
+ ((<= end namestart)
+ (error "Stray end escape for %s"
+ (semantic-tag-name tag)))
+ )
+ ;; Add string to compiled output
+ (setq name (substring what namestart end)
+ key nil)
+ ;; Trim WHAT back.
+ (setq what (substring what tail))
+ ;; Get the inserter
+ (let ((new-inserter
+ (srecode-compile-parse-inserter name STATE))
+ )
+ ;; If this is an end inserter, then assign into
+ ;; the end-token.
+ (if (srecode-match-end new-inserter end-name)
+ (setq end-token new-inserter))
+ ;; Add the inserter to our compilation stream.
+ (setq comp (cons new-inserter comp))
+ ;; Allow the inserter an opportunity to modify
+ ;; the input stream.
+ (setq what (srecode-parse-input new-inserter tag what
+ STATE))
+ )
+ )))
+ (t
+ (if end-name
+ (error "Unmatched section end %s" end-name))
+ (setq comp (cons what comp)
+ what nil))))
+ (cons what (nreverse comp))))
+
+(defun srecode-compile-parse-inserter (txt STATE)
+ "Parse the inserter TXT with the current STATE.
+Return an inserter object."
+ (let ((key (aref txt 0))
+ )
+ (if (and (or (< key ?A) (> key ?Z))
+ (or (< key ?a) (> key ?z)) )
+ (setq name (substring txt 1))
+ (setq name txt
+ key nil))
+ (let* ((junk (string-match ":" name))
+ (namepart (if junk
+ (substring name 0 (match-beginning 0))
+ name))
+ (secondname (if junk
+ (substring name (match-end 0))
+ nil))
+ (new-inserter (srecode-compile-inserter
+ namepart key STATE
+ :secondname secondname
+ )))
+ ;; Return the new inserter
+ new-inserter)))
+
+(defun srecode-compile-inserter (name key STATE &rest props)
+ "Create an srecode inserter object for some macro NAME.
+KEY indicates a single character key representing a type
+of inserter to create.
+STATE is the current compile state.
+PROPS are additional properties that might need to be passed
+to the inserter constructor."
+ ;;(message "Compile: %s %S" name props)
+ (if (not key)
+ (apply 'srecode-template-inserter-variable name props)
+ (let ((classes (class-children srecode-template-inserter))
+ (new nil))
+ ;; Loop over the various subclasses and
+ ;; create the correct inserter.
+ (while (and (not new) classes)
+ (setq classes (append classes (class-children (car classes))))
+ ;; Do we have a match?
+ (when (and (not (class-abstract-p (car classes)))
+ (equal (oref (car classes) key) key))
+ ;; Create the new class, and apply state.
+ (setq new (apply (car classes) name props))
+ (srecode-inserter-apply-state new STATE)
+ )
+ (setq classes (cdr classes)))
+ (if (not new) (error "SRECODE: Unknown macro code %S" key))
+ new)))
+
+(defun srecode-compile-template-table (templates mode priority application vars)
+ "Compile a list of TEMPLATES into an semantic recode table.
+The table being compiled is for MODE, or the string \"default\".
+PRIORITY is a numerical value that indicates this tables location
+in an ordered search.
+APPLICATION is the name of the application these templates belong to.
+A list of defined variables VARS provides a variable table."
+ (let ((namehash (make-hash-table :test 'equal
+ :size (length templates)))
+ (contexthash (make-hash-table :test 'equal :size 10))
+ (lp templates)
+ )
+
+ (while lp
+
+ (let* ((objname (oref (car lp) :object-name))
+ (context (oref (car lp) :context))
+ (globalname (concat context ":" objname))
+ )
+
+ ;; Place this template object into the global name hash.
+ (puthash globalname (car lp) namehash)
+
+ ;; Place this template into the specific context name hash.
+ (let ((hs (gethash context contexthash)))
+ ;; Make a new context if none was available.
+ (when (not hs)
+ (setq hs (make-hash-table :test 'equal :size 20))
+ (puthash context hs contexthash))
+ ;; Put into that contenxt's hash.
+ (puthash objname (car lp) hs)
+ )
+
+ (setq lp (cdr lp))))
+
+ (let* ((table (srecode-mode-table-new mode (buffer-file-name)
+ :templates (nreverse templates)
+ :namehash namehash
+ :contexthash contexthash
+ :variables vars
+ :major-mode mode
+ :priority priority
+ :application application))
+ (tmpl (oref table templates)))
+ ;; Loop over all the templates, and xref.
+ (while tmpl
+ (oset (car tmpl) :table table)
+ (setq tmpl (cdr tmpl))))
+ ))
+
+
+
+;;; DEBUG
+;;
+;; Dump out information about the current srecoder compiled templates.
+;;
+
+(defmethod srecode-dump ((tmp srecode-template))
+ "Dump the contents of the SRecode template tmp."
+ (princ "== Template \"")
+ (princ (object-name-string tmp))
+ (princ "\" in context ")
+ (princ (oref tmp context))
+ (princ "\n")
+ (when (oref tmp args)
+ (princ " Arguments: ")
+ (prin1 (oref tmp args))
+ (princ "\n"))
+ (when (oref tmp dictionary)
+ (princ " Section Dictionaries:\n")
+ (srecode-dump (oref tmp dictionary) 4)
+ ;(princ "\n")
+ )
+ (when (and (slot-boundp tmp 'binding) (oref tmp binding))
+ (princ " Binding: ")
+ (prin1 (oref tmp binding))
+ (princ "\n"))
+ (princ " Compiled Codes:\n")
+ (srecode-dump-code-list (oref tmp code) " ")
+ (princ "\n\n")
+ )
+
+(defun srecode-dump-code-list (code indent)
+ "Dump the CODE from a template code list to standard output.
+Argument INDENT specifies the indentation level for the list."
+ (let ((i 1))
+ (while code
+ (princ indent)
+ (prin1 i)
+ (princ ") ")
+ (cond ((stringp (car code))
+ (prin1 (car code)))
+ ((srecode-template-inserter-child-p (car code))
+ (srecode-dump (car code) indent))
+ (t
+ (princ "Unknown Code: ")
+ (prin1 (car code))))
+ (setq code (cdr code)
+ i (1+ i))
+ (when code
+ (princ "\n"))))
+ )
+
+(defmethod srecode-dump ((ins srecode-template-inserter) indent)
+ "Dump the state of the SRecode template inserter INS."
+ (princ "INS: \"")
+ (princ (object-name-string ins))
+ (when (oref ins :secondname)
+ (princ "\" : \"")
+ (princ (oref ins :secondname)))
+ (princ "\" type \"")
+ (let* ((oc (symbol-name (object-class ins)))
+ (junk (string-match "srecode-template-inserter-" oc))
+ (on (if junk
+ (substring oc (match-end 0))
+ oc)))
+ (princ on))
+ (princ "\"")
+ )
+
+(provide 'srecode/compile)
+
+;; Local variables:
+;; generated-autoload-file: "loaddefs.el"
+;; generated-autoload-feature: srecode/loaddefs
+;; generated-autoload-load-name: "srecode/compile"
+;; End:
+
+;;; srecode/compile.el ends here