diff lisp/cedet/srecode/dictionary.el @ 104498:41dc39934483

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.
author Chong Yidong <cyd@stupidchicken.com>
date Sun, 20 Sep 2009 21:06:41 +0000
parents
children 2f2bc20297c6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/cedet/srecode/dictionary.el	Sun Sep 20 21:06:41 2009 +0000
@@ -0,0 +1,565 @@
+;;; srecode-dictionary.el --- Dictionary code for the semantic recoder.
+
+;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <eric@siege-engine.com>
+
+;; 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:
+;;
+;; Dictionaries contain lists of names and their assocaited values.
+;; These dictionaries are used to fill in macros from recoder templates.
+
+;;; Code:
+
+;;; CLASSES
+
+(require 'eieio)
+(require 'srecode)
+(require 'srecode/table)
+(eval-when-compile (require 'semantic))
+
+(declare-function srecode-compile-parse-inserter "srecode/compile")
+(declare-function srecode-dump-code-list "srecode/compile")
+(declare-function srecode-load-tables-for-mode "srecode/find")
+(declare-function srecode-insert-code-stream "srecode/insert")
+(declare-function data-debug-new-buffer "data-debug")
+(declare-function data-debug-insert-object-slots "eieio-datadebug")
+(declare-function srecode-field "srecode/fields")
+
+(defclass srecode-dictionary ()
+  ((namehash :initarg :namehash
+	     :documentation
+	     "Hash table containing the names of all the templates.")
+   (buffer :initarg :buffer
+	   :documentation
+	   "The buffer this dictionary was initialized with.")
+   (parent :initarg :parent
+	   :type (or null srecode-dictionary)
+	   :documentation
+	   "The parent dictionary.
+Symbols not appearing in this dictionary will be checked against the
+parent dictionary.")
+   (origin :initarg :origin
+	   :type string
+	   :documentation
+	   "A string representing the origin of this dictionary.
+Useful only while debugging.")
+   )
+  "Dictionary of symbols and what they mean.
+Dictionaries are used to look up named symbols from
+templates to decide what to do with those symbols.")
+
+(defclass srecode-dictionary-compound-value ()
+  ()
+  "A compound dictionary value.
+Values stored in a dictionary must be a STRING,
+a dictionary for showing sections, or an instance of a subclass
+of this class.
+
+Compound dictionary values derive from this class, and must
+provide a sequence of method implementations to convert into
+a string."
+  :abstract t)
+
+(defclass srecode-dictionary-compound-variable
+  (srecode-dictionary-compound-value)
+  ((value :initarg :value
+	  :documentation
+	  "The value of this template variable.
+Variables in template files are usually a single string
+which can be inserted into a dictionary directly.
+
+Some variables may be more complex and involve dictionary
+lookups, strings, concatenation, or the like.
+
+The format of VALUE is determined by current template
+formatting rules.")
+   (compiled :initarg :compiled
+	     :type list
+	     :documentation
+	     "The compiled version of VALUE.")
+   )
+  "A compound dictionary value for template file variables.
+You can declare a variable in a template like this:
+
+set NAME \"str\" macro \"OTHERNAME\"
+
+with appending various parts together in a list.")
+
+(defmethod initialize-instance ((this srecode-dictionary-compound-variable)
+				&optional fields)
+  "Initialize the compound variable THIS.
+Makes sure that :value is compiled."
+  (let ((newfields nil)
+	(state nil))
+    (while fields
+      ;; Strip out :state
+      (if (eq (car fields) :state)
+	  (setq state (car (cdr fields)))
+	(setq newfields (cons (car (cdr fields))
+			      (cons (car fields) newfields))))
+      (setq fields (cdr (cdr fields))))
+
+    (when (not state)
+      (error "Cannot create compound variable without :state"))
+
+    (call-next-method this (nreverse newfields))
+    (when (not (slot-boundp this 'compiled))
+      (let ((val (oref this :value))
+	    (comp nil))
+	(while val
+	  (let ((nval (car val))
+		)
+	    (cond ((stringp nval)
+		   (setq comp (cons nval comp)))
+		  ((and (listp nval)
+			(equal (car nval) 'macro))
+		   (require 'srecode/compile)
+		   (setq comp (cons
+			       (srecode-compile-parse-inserter
+				(cdr nval)
+				state)
+			       comp)))
+		  (t
+		   (error "Don't know how to handle variable value %S" nval)))
+	    )
+	  (setq val (cdr val)))
+	(oset this :compiled (nreverse comp))))))
+
+;;; DICTIONARY METHODS
+;;
+
+(defun srecode-create-dictionary (&optional buffer-or-parent)
+  "Create a dictionary for BUFFER.
+If BUFFER-OR-PARENT is not specified, assume a buffer, and
+use the current buffer.
+If BUFFER-OR-PARENT is another dictionary, then remember the
+parent within the new dictionary, and assume that BUFFER
+is the same as belongs to the parent dictionary.
+The dictionary is initialized with variables setup for that
+buffer's table.
+If BUFFER-OR-PARENT is t, then this dictionary should not be
+assocated with a buffer or parent."
+  (save-excursion
+    (let ((parent nil)
+	  (buffer nil)
+	  (origin nil)
+	  (initfrombuff nil))
+      (cond ((bufferp buffer-or-parent)
+	     (set-buffer buffer-or-parent)
+	     (setq buffer buffer-or-parent
+		   origin (buffer-name buffer-or-parent)
+		   initfrombuff t))
+	    ((srecode-dictionary-child-p buffer-or-parent)
+	     (setq parent buffer-or-parent
+		   buffer (oref buffer-or-parent buffer)
+		   origin (concat (object-name buffer-or-parent) " in "
+				  (if buffer (buffer-name buffer)
+				    "no buffer")))
+	     (when buffer
+	       (set-buffer buffer)))
+	    ((eq buffer-or-parent t)
+	     (setq buffer nil
+		   origin "Unspecified Origin"))
+	    (t
+	     (setq buffer (current-buffer)
+		   origin (concat "Unspecified.  Assume "
+				  (buffer-name buffer))
+		   initfrombuff t)
+	     )
+	    )
+      (let ((dict (srecode-dictionary
+		   major-mode
+		   :buffer buffer
+		   :parent parent
+		   :namehash  (make-hash-table :test 'equal
+					       :size 20)
+		   :origin origin)))
+	;; Only set up the default variables if we are being built
+	;; directroy for a particular buffer.
+	(when initfrombuff
+	  ;; Variables from the table we are inserting from.
+	  ;; @todo - get a better tree of tables.
+	  (let ((mt (srecode-get-mode-table major-mode))
+		(def (srecode-get-mode-table 'default)))
+	    ;; Each table has multiple template tables.
+	    ;; Do DEF first so that MT can override any values.
+	    (srecode-dictionary-add-template-table dict def)
+	    (srecode-dictionary-add-template-table dict mt)
+	    ))
+	dict))))
+
+(defmethod srecode-dictionary-add-template-table ((dict srecode-dictionary)
+						  tpl)
+  "Insert into DICT the variables found in table TPL.
+TPL is an object representing a compiled template file."
+  (when tpl
+    (let ((tabs (oref tpl :tables)))
+      (while tabs
+	(let ((vars (oref (car tabs) variables)))
+	  (while vars
+	    (srecode-dictionary-set-value
+	     dict (car (car vars)) (cdr (car vars)))
+	    (setq vars (cdr vars))))
+	(setq tabs (cdr tabs))))))
+
+
+(defmethod srecode-dictionary-set-value ((dict srecode-dictionary)
+					 name value)
+  "In dictionary DICT, set NAME to have VALUE."
+  ;; Validate inputs
+  (if (not (stringp name))
+      (signal 'wrong-type-argument (list name 'stringp)))
+  ;; Add the value.
+  (with-slots (namehash) dict
+    (puthash name value namehash))
+  )
+
+(defmethod srecode-dictionary-add-section-dictionary ((dict srecode-dictionary)
+						      name &optional show-only)
+  "In dictionary DICT, add a section dictionary for section macro NAME.
+Return the new dictionary.
+
+You can add several dictionaries to the same section macro.
+For each dictionary added to a macro, the block of codes in the
+template will be repeated.
+
+If optional argument SHOW-ONLY is non-nil, then don't add a new dictionarly
+if there is already one in place.  Also, don't add FIRST/LAST entries.
+These entries are not needed when we are just showing a section.
+
+Each dictionary added will automatically get values for positional macros
+which will enable SECTIONS to be enabled.
+
+ * FIRST - The first entry in the table.
+ * NOTFIRST - Not the first entry in the table.
+ * LAST - The last entry in the table
+ * NOTLAST - Not the last entry in the table.
+
+Adding a new dictionary will alter these values in previously
+inserted dictionaries."
+  ;; Validate inputs
+  (if (not (stringp name))
+      (signal 'wrong-type-argument (list name 'stringp)))
+  (let ((new (srecode-create-dictionary dict))
+	(ov (srecode-dictionary-lookup-name dict name)))
+
+    (when (not show-only)
+      ;; Setup the FIRST/NOTFIRST and LAST/NOTLAST entries.
+      (if (null ov)
+	  (progn
+	    (srecode-dictionary-show-section new "FIRST")
+	    (srecode-dictionary-show-section new "LAST"))
+	;; Not the very first one.  Lets clean up CAR.
+	(let ((tail (car (last ov))))
+	  (srecode-dictionary-hide-section tail "LAST")
+	  (srecode-dictionary-show-section tail "NOTLAST")
+	  )
+	(srecode-dictionary-show-section new "NOTFIRST")
+	(srecode-dictionary-show-section new "LAST"))
+      )
+
+    (when (or (not show-only) (null ov))
+      (srecode-dictionary-set-value dict name (append ov (list new))))
+    ;; Return the new sub-dictionary.
+    new))
+
+(defmethod srecode-dictionary-show-section ((dict srecode-dictionary) name)
+  "In dictionary DICT, indicate that the section NAME should be exposed."
+  ;; Validate inputs
+  (if (not (stringp name))
+      (signal 'wrong-type-argument (list name 'stringp)))
+  ;; Showing a section is just like making a section dictionary, but
+  ;; with no dictionary values to add.
+  (srecode-dictionary-add-section-dictionary dict name t)
+  nil)
+
+(defmethod srecode-dictionary-hide-section ((dict srecode-dictionary) name)
+  "In dictionary DICT, indicate that the section NAME should be hidden."
+  ;; We need to find the has value, and then delete it.
+  ;; Validate inputs
+  (if (not (stringp name))
+      (signal 'wrong-type-argument (list name 'stringp)))
+  ;; Add the value.
+  (with-slots (namehash) dict
+    (remhash name namehash))
+  nil)
+
+(defmethod srecode-dictionary-merge ((dict srecode-dictionary) otherdict)
+  "Merge into DICT the dictionary entries from OTHERDICT."
+  (when otherdict
+    (maphash
+     (lambda (key entry)
+       ;; Only merge in the new values if there was no old value.
+       ;; This protects applications from being whacked, and basically
+       ;; makes these new section dictionary entries act like
+       ;; "defaults" instead of overrides.
+       (when (not (srecode-dictionary-lookup-name dict key))
+	 (cond ((and (listp entry) (srecode-dictionary-p (car entry)))
+		;; A list of section dictionaries.
+		;; We need to merge them in.
+		(while entry
+		  (let ((new-sub-dict
+			 (srecode-dictionary-add-section-dictionary
+			  dict key)))
+		    (srecode-dictionary-merge new-sub-dict (car entry)))
+		  (setq entry (cdr entry)))
+		  )
+
+	       (t
+		(srecode-dictionary-set-value dict key entry)))
+	       ))
+     (oref otherdict namehash))))
+
+(defmethod srecode-dictionary-lookup-name ((dict srecode-dictionary)
+					   name)
+  "Return information about the current DICT's value for NAME."
+  (if (not (slot-boundp dict 'namehash))
+      nil
+    ;; Get the value of this name from the dictionary
+    (or (with-slots (namehash) dict
+	  (gethash name namehash))
+	(and (not (member name '("FIRST" "LAST" "NOTFIRST" "NOTLAST")))
+	     (oref dict parent)
+	     (srecode-dictionary-lookup-name (oref dict parent) name))
+	)))
+
+(defmethod srecode-root-dictionary ((dict srecode-dictionary))
+  "For dictionary DICT, return the root dictionary.
+The root dictionary is usually for a current or active insertion."
+  (let ((ans dict))
+    (while (oref ans parent)
+      (setq ans (oref ans parent)))
+    ans))
+
+;;; COMPOUND VALUE METHODS
+;;
+;; Compound values must provide at least the toStriong method
+;; for use in converting the compound value into sometehing insertable.
+
+(defmethod srecode-compound-toString ((cp srecode-dictionary-compound-value)
+				      function
+				      dictionary)
+  "Convert the compound dictionary value CP to a string.
+If FUNCTION is non-nil, then FUNCTION is somehow applied to an aspect
+of the compound value.  The FUNCTION could be a fraction
+of some function symbol with a logical prefix excluded.
+
+If you subclass `srecode-dictionary-compound-value' then this
+method could return nil, but if it does that, it must insert
+the value itself using `princ', or by detecting if the current
+standard out is a buffer, and using `insert'."
+  (object-name cp))
+
+(defmethod srecode-dump ((cp srecode-dictionary-compound-value)
+			 &optional indent)
+  "Display information about this compound value."
+  (princ (object-name cp))
+  )
+
+(defmethod srecode-compound-toString ((cp srecode-dictionary-compound-variable)
+				      function
+				      dictionary)
+  "Convert the compound dictionary variable value CP into a string.
+FUNCTION and DICTIONARY are as for the baseclass."
+  (require 'srecode/insert)
+  (srecode-insert-code-stream (oref cp compiled) dictionary))
+
+
+(defmethod srecode-dump ((cp srecode-dictionary-compound-variable)
+			 &optional indent)
+  "Display information about this compound value."
+  (require 'srecode/compile)
+  (princ "# Compound Variable #\n")
+  (let ((indent (+ 4 (or indent 0)))
+	(cmp (oref cp compiled))
+	)
+    (srecode-dump-code-list cmp (make-string indent ? ))
+    ))
+
+;;; FIELD EDITING COMPOUND VALUE
+;;
+;; This is an interface to using field-editing objects
+;; instead of asking questions.  This provides the basics
+;; behind this compound value.
+
+(defclass srecode-field-value (srecode-dictionary-compound-value)
+  ((firstinserter :initarg :firstinserter
+		  :documentation
+		  "The inserter object for the first occurance of this field.")
+   (defaultvalue :initarg :defaultvalue
+     :documentation
+     "The default value for this inserter.")
+   )
+  "When inserting values with editable field mode, a dictionary value.
+Compound values allow a field to be stored in the dictionary for when
+it is referenced a second time.  This compound value can then be
+inserted with a new editable field.")
+
+(defmethod srecode-compound-toString((cp srecode-field-value)
+				     function
+				     dictionary)
+  "Convert this field into an insertable string."
+  (require 'srecode/fields)
+  ;; If we are not in a buffer, then this is not supported.
+  (when (not (bufferp standard-output))
+    (error "FIELDS invoked while inserting template to non-buffer."))
+
+  (if function
+      (error "@todo: Cannot mix field insertion with functions.")
+
+    ;; No function.  Perform a plain field insertion.
+    ;; We know we are in a buffer, so we can perform the insertion.
+    (let* ((dv (oref cp defaultvalue))
+	   (sti (oref cp firstinserter))
+	   (start (point))
+	   (name (oref sti :object-name)))
+
+      (if (or (not dv) (string= dv ""))
+	  (insert name)
+	(insert dv))
+
+      (srecode-field name :name name
+		     :start start
+		     :end (point)
+		     :prompt (oref sti prompt)
+		     :read-fcn (oref sti read-fcn)
+		     )
+      ))
+  ;; Returning nil is a signal that we have done the insertion ourselves.
+  nil)
+
+
+;;; Higher level dictionary functions
+;;
+(defun srecode-create-section-dictionary (sectiondicts STATE)
+  "Create a dictionary with section entries for a template.
+The format for SECTIONDICTS is what is emitted from the template parsers.
+STATE is the current compiler state."
+  (when sectiondicts
+    (let ((new (srecode-create-dictionary t)))
+      ;; Loop over each section.  The section is a macro w/in the
+      ;; template.
+      (while sectiondicts
+	(let* ((sect (car (car sectiondicts)))
+	       (entries (cdr (car sectiondicts)))
+	       (subdict (srecode-dictionary-add-section-dictionary new sect))
+	       )
+	  ;; Loop over each entry.  This is one variable in the
+	  ;; section dictionary.
+	  (while entries
+	    (let ((tname (semantic-tag-name (car entries)))
+		  (val (semantic-tag-variable-default (car entries))))
+	      (if (eq val t)
+		  (srecode-dictionary-show-section subdict tname)
+		(cond
+		 ((and (stringp (car val))
+		       (= (length val) 1))
+		  (setq val (car val)))
+		 (t
+		  (setq val (srecode-dictionary-compound-variable
+			     tname :value val :state STATE))))
+		(srecode-dictionary-set-value
+		 subdict tname val))
+	      (setq entries (cdr entries))))
+	  )
+	(setq sectiondicts (cdr sectiondicts)))
+      new)))
+
+;;; DUMP DICTIONARY
+;;
+;; Make a dictionary, and dump it's contents.
+
+(defun srecode-adebug-dictionary ()
+  "Run data-debug on this mode's dictionary."
+  (interactive)
+  (require 'eieio-datadebug)
+  (require 'semantic)
+  (require 'srecode/find)
+  (let* ((modesym major-mode)
+	 (start (current-time))
+	 (junk (or (progn (srecode-load-tables-for-mode modesym)
+			  (srecode-get-mode-table modesym))
+		   (error "No table found for mode %S" modesym)))
+	 (dict (srecode-create-dictionary (current-buffer)))
+	 (end (current-time))
+	 )
+    (message "Creating a dictionary took %.2f seconds."
+	     (semantic-elapsed-time start end))
+    (data-debug-new-buffer "*SRECODE ADEBUG*")
+    (data-debug-insert-object-slots dict "*")))
+
+(defun srecode-dictionary-dump ()
+  "Dump a typical fabricated dictionary."
+  (interactive)
+  (require 'srecode/find)
+  (let ((modesym major-mode))
+    ;; This load allows the dictionary access to inherited
+    ;; and stacked dictionary entries.
+    (srecode-load-tables-for-mode modesym)
+    (let ((tmp (srecode-get-mode-table modesym))
+	  )
+      (if (not tmp)
+	  (error "No table found for mode %S" modesym))
+      ;; Now make the dictionary.
+      (let ((dict (srecode-create-dictionary (current-buffer))))
+	(with-output-to-temp-buffer "*SRECODE DUMP*"
+	  (princ "DICTIONARY FOR ")
+	  (princ major-mode)
+	  (princ "\n--------------------------------------------\n")
+	  (srecode-dump dict))
+	))))
+
+(defmethod srecode-dump ((dict srecode-dictionary) &optional indent)
+  "Dump a dictionary."
+  (if (not indent) (setq indent 0))
+  (maphash (lambda (key entry)
+	     (princ (make-string indent ? ))
+	     (princ " ")
+	     (princ key)
+	     (princ " ")
+	     (cond ((and (listp entry)
+			 (srecode-dictionary-p (car entry)))
+		    (let ((newindent (if indent
+					 (+ indent 4)
+				       4)))
+		      (while entry
+			(princ " --> SUBDICTIONARY ")
+			(princ (object-name dict))
+			(princ "\n")
+			(srecode-dump (car entry) newindent)
+			(setq entry (cdr entry))
+			))
+		    (princ "\n")
+		    )
+		   ((srecode-dictionary-compound-value-child-p entry)
+		    (srecode-dump entry indent)
+		    (princ "\n")
+		    )
+		   (t
+		    (prin1 entry)
+		    ;(princ "\n")
+		    ))
+	     (terpri)
+	     )
+	   (oref dict namehash))
+  )
+
+(provide 'srecode/dictionary)
+
+;;; srecode/dictionary.el ends here