changeset 181:a587d037d637

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Thu, 31 Jan 1991 21:18:19 +0000
parents 289ee39b1637
children 35ea4993d08c
files lisp/emacs-lisp/backquote.el
diffstat 1 files changed, 354 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emacs-lisp/backquote.el	Thu Jan 31 21:18:19 1991 +0000
@@ -0,0 +1,354 @@
+;; Copyright (C) 1985 Free Software Foundation, Inc.
+;; Written by Dick King (king@kestrel).
+
+;; 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 1, 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; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;; This is a rudimentry backquote package written by D. King,
+ ;;; king@kestrel, on 8/31/85.  (` x) is a macro
+ ;;; that expands to a form that produces x.  (` (a b ..)) is
+ ;;; a macro that expands into a form that produces a list of what a b
+ ;;; etc. would have produced.  Any element can be of the form
+ ;;; (, <form>) in which case the resulting form evaluates
+ ;;; <form> before putting it into place, or (,@ <form>), in which
+ ;;; case the evaluation of <form> is arranged for and each element
+ ;;; of the result (which must be a (possibly null) list) is inserted.
+;;; As an example, the immediately following macro push (v l) could
+ ;;; have been written 
+;;;    (defmacro push (v l)
+;;;         (` (setq (, l) (cons (,@ (list v l))))))
+ ;;; although
+;;;    (defmacro push (v l)
+;;;         (` (setq (, l) (cons (, v) (, l)))))
+ ;;; is far more natural.  The magic atoms ,
+ ;;; and ,@ are user-settable and list-valued.  We recommend that
+ ;;; things never be removed from this list lest you break something
+ ;;; someone else wrote in the dim past that comes to be recompiled in
+ ;;; the distant future.
+
+;;; LIMITATIONS: tail consing is not handled correctly.  Do not say
+ ;;; (` (a . (, b))) - say (` (a (,@ b)))
+ ;;; which works even if b is not list-valued.
+;;; No attempt is made to handle vectors.  (` [a (, b) c]) doesn't work.
+;;; Sorry, you must say things like
+ ;;; (` (a (,@ 'b))) to get (a . b) and 
+ ;;; (` ((, ',) c)) to get (, c) - [(` (a , b)) will work but is a bad habit]
+;;; I haven't taught it the joys of nconc.
+;;; (` atom) dies.  (` (, atom)) or anything else is okay.
+
+;;; BEWARE BEWARE BEWARE
+ ;;; inclusion of (,atom) rather than (, atom) or (,@atom) rather than
+ ;;; (,@ atom) will result in errors that will show up very late.
+ ;;; This is so crunchy that I am considering including a check for
+ ;;; this or changing the syntax to ... ,(<form>).  RMS: opinion?
+
+
+(provide 'backquote)
+
+;;; a raft of general-purpose macros follows.  See the nearest
+ ;;; Commonlisp manual.
+(defmacro bq-push (v l)
+  "Pushes evaluated first form onto second unevaluated object
+a list-value atom"
+  (list 'setq l (list 'cons v l)))
+
+(defmacro bq-caar (l)
+  (list 'car (list 'car l)))
+
+(defmacro bq-cadr (l)
+  (list 'car (list 'cdr l)))
+
+(defmacro bq-cdar (l)
+  (list 'cdr (list 'car l)))
+
+
+;;; These two advertised variables control what characters are used to
+ ;;; unquote things.  I have included , and ,@ as the unquote and
+ ;;; splice operators, respectively, to give users of MIT CADR machine
+ ;;; derivitive machines a warm, cosy feeling.
+
+(defconst backquote-unquote '(,)
+  "*A list of all objects that stimulate unquoting in `.  Memq test.")
+
+
+(defconst backquote-splice '(,@)
+  "*A list of all objects that stimulate splicing in `.  Memq test.")
+
+
+;;; This is the interface 
+(defmacro ` (form)
+  "(` FORM)  is a macro that expands to code to construct FORM.
+Note that this is very slow in interpreted code, but fast if you compile.
+FORM is one or more nested lists, which are `almost quoted':
+They are copied recursively, with non-lists used unchanged in the copy.
+ (` a b) == (list 'a 'b)  constructs a new list with two elements, `a' and `b'.
+ (` a (b c)) == (list 'a (list 'b 'c))  constructs two nested new lists.
+
+However, certain special lists are not copied.  They specify substitution.
+Lists that look like (, EXP) are evaluated and the result is substituted.
+ (` a (, (+ x 5))) == (list 'a (+ x 5))
+
+Elements of the form (,@ EXP) are evaluated and then all the elements
+of the result are substituted.  This result must be a list; it may
+be `nil'.
+
+As an example, a simple macro `push' could be written:
+   (defmacro push (v l)
+        (` (setq (, l) (cons (,@ (list v l))))))
+or as
+   (defmacro push (v l)
+        (` (setq (, l) (cons (, v) (, l)))))
+
+LIMITATIONS: \"dotted lists\" are not allowed in FORM.
+The ultimate cdr of each list scanned by ` must be `nil'.
+\(This does not apply to constants inside expressions to be substituted.)
+
+Substitution elements are not allowed as the cdr
+of a cons cell.  For example, (` (A . (, B))) does not work.
+Instead, write (` (A (,@ B))).
+
+You cannot construct vectors, only lists.  Vectors are treated as
+constants.
+
+BEWARE BEWARE BEWARE
+Inclusion of (,ATOM) rather than (, ATOM)
+or of (,@ATOM) rather than (,@ ATOM)
+will result in errors that will show up very late."
+  (bq-make-maker form))
+
+;;; We develop the method for building the desired list from
+ ;;; the end towards the beginning.  The contract is that there be a
+ ;;; variable called state and a list called tailmaker, and that the form
+ ;;; (cons state tailmaker) deliver the goods.  Exception - if the
+ ;;; state is quote the tailmaker is the form itself.
+;;; This function takes a form and returns what I will call a maker in
+ ;;; what follows.  Evaluating the maker would produce the form,
+ ;;; properly evaluated according to , and ,@ rules.
+;;; I work backwards - it seemed a lot easier.  The reason for this is
+ ;;; if I'm in some sort of a routine building a maker and I switch
+ ;;; gears, it seemed to me easier to jump into some other state and
+ ;;; glue what I've already done to the end, than to to prepare that
+ ;;; something and go back to put things together.
+(defun bq-make-maker (form)
+  "Given one argument, a `mostly quoted' object, produces a maker.
+See backquote.el for details"
+  (let ((tailmaker (quote nil)) (qc 0) (ec 0) (state nil))
+    (mapcar 'bq-iterative-list-builder (reverse form))
+    (and state
+	 (cond ((eq state 'quote)
+		(list state tailmaker))
+	       ((= (length tailmaker) 1)
+		(funcall (bq-cadr (assq state bq-singles)) tailmaker))
+	       (t (cons state tailmaker))))))
+
+;;; There are exceptions - we wouldn't want to call append of one
+ ;;; argument, for example.
+(defconst bq-singles '((quote bq-quotecar)
+		       (append car)
+		       (list bq-make-list)
+		       (cons bq-id)))
+
+(defun bq-id (x) x)
+
+(defun bq-quotecar (x) (list 'quote (car x)))
+
+(defun bq-make-list (x) (cons 'list x))
+
+;;; fr debugging use only
+;(defun funcalll (a b) (funcall a b))
+;(defun funcalll (a b) (debug nil 'enter state tailmaker a b)
+;  (let ((ans (funcall a b))) (debug  nil 'leave state tailmaker)
+;       ans))
+
+;;; Given a state/tailmaker pair that already knows how to make a
+ ;;; partial tail of the desired form, this function knows how to add
+ ;;; yet another element to the burgening list.  There are four cases;
+ ;;; the next item is an atom (which will certainly be quoted); a 
+ ;;; (, xxx), which will be evaluated and put into the list at the top
+ ;;; level; a (,@ xxx), which will be evaluated and spliced in, or
+ ;;; some other list, in which case we first compute the form's maker,
+ ;;; and then we either launch into the quoted case if the maker's
+ ;;; top level function is quote, or into the comma case if it isn't.
+;;; The fourth case reduces to one of the other three, so here we have
+ ;;; a choice of three ways to build tailmaker, and cit turns out we
+ ;;; use five possible values of state (although someday I'll add
+ ;;; nconcto the possible values of state).
+;;; This maintains the invariant that (cons state tailmaker) is the
+ ;;; maker for the elements of the tail we've eaten so far.
+(defun bq-iterative-list-builder (form)
+  "Called by `bq-make-maker'.  Adds a new item form to tailmaker, 
+changing state if need be, so tailmaker and state constitute a recipe
+for making the list so far."
+  (cond ((atom form)
+	 (funcall (bq-cadr (assq state bq-quotefns)) form))
+	((memq (car form) backquote-unquote)
+	 (funcall (bq-cadr (assq state bq-evalfns)) (bq-cadr form)))
+	((memq (car form) backquote-splice)
+	 (funcall (bq-cadr (assq state bq-splicefns)) (bq-cadr form)))
+	(t
+	 (let ((newform (bq-make-maker form)))
+	   (if (and (listp newform) (eq (car newform) 'quote))
+	       (funcall (bq-cadr (assq state bq-quotefns)) (bq-cadr newform))
+	     (funcall (bq-cadr (assq state bq-evalfns)) newform))))
+	))
+
+;;; We do a 2-d branch on the form of splicing and the old state.
+ ;;; Here's fifteen functions' names...
+(defconst bq-splicefns '((nil bq-splicenil)
+			 (append bq-spliceappend)
+			 (list bq-splicelist)
+			 (quote bq-splicequote)
+			 (cons bq-splicecons)))
+
+(defconst bq-evalfns '((nil bq-evalnil)
+		       (append bq-evalappend)
+		       (list bq-evallist)
+		       (quote bq-evalquote)
+		       (cons bq-evalcons)))
+
+(defconst bq-quotefns '((nil bq-quotenil)
+			(append bq-quoteappend)
+			(list bq-quotelist)
+			(quote bq-quotequote)
+			(cons bq-quotecons)))
+
+;;; The name of each function is
+ ;;; (concat 'bq- <type-of-element-addition> <old-state>)
+;;; I'll comment the non-obvious ones before the definitions...
+ ;;; In what follows, uppercase letters and form will always be
+ ;;; metavariables that don't need commas in backquotes, and I will
+ ;;; assume the existence of something like matches that takes a
+ ;;; backquote-like form and a value, binds metavariables and returns
+ ;;; t if the pattern match is successful, returns nil otherwise.  I
+ ;;; will write such a goodie someday.
+
+;;;   (setq tailmaker
+ ;;;      (if (matches ((quote X) Y) tailmaker)
+ ;;;          (` ((quote (form X)) Y))
+ ;;;        (` ((list form (quote X)) Y))))
+ ;;;  (setq state 'append)
+(defun bq-quotecons (form)
+  (if (and (listp (car tailmaker))
+	   (eq (bq-caar tailmaker) 'quote))
+      (setq tailmaker
+	    (list (list 'quote (list form (bq-cadr (car tailmaker))))
+		  (bq-cadr tailmaker))) 
+    (setq tailmaker
+	  (list (list 'list
+		      (list 'quote form)
+		      (car tailmaker))
+		(bq-cadr tailmaker))))
+  (setq state 'append))
+
+(defun bq-quotequote (form)
+  (bq-push form tailmaker))
+
+;;; Could be improved to convert (list 'a 'b 'c .. 'w x) 
+ ;;;                          to (append '(a b c .. w) x)
+ ;;; when there are enough elements
+(defun bq-quotelist (form)
+  (bq-push (list 'quote form) tailmaker))
+
+;;; (setq tailmaker
+ ;;;  (if (matches ((quote X) (,@ Y)))
+ ;;;      (` ((quote (, (cons form X))) (,@ Y)))))
+(defun bq-quoteappend (form)
+  (cond ((and (listp tailmaker)
+	   (listp (car tailmaker))
+	   (eq (bq-caar tailmaker) 'quote))
+	 (rplaca (bq-cdar tailmaker)
+		 (cons form (car (bq-cdar tailmaker)))))
+	(t (bq-push (list 'quote (list form)) tailmaker))))
+
+(defun bq-quotenil (form)
+  (setq tailmaker (list form))
+  (setq state 'quote))
+
+;;; (if (matches (X Y) tailmaker)  ; it must
+ ;;;    (` ((list form X) Y)))
+(defun bq-evalcons (form)
+  (setq tailmaker
+	(list (list 'list form (car tailmaker))
+	      (bq-cadr tailmaker)))
+  (setq state 'append))
+
+;;;  (if (matches (X Y Z (,@ W)))
+ ;;;     (progn (setq state 'append)
+ ;;;            (` ((list form) (quote (X Y Z (,@ W))))))
+ ;;;     (progn (setq state 'list)
+ ;;;            (list form 'X 'Y .. )))  ;  quote each one there is
+(defun bq-evalquote (form)
+  (cond ((< (length tailmaker) 3)
+	 (setq tailmaker
+	       (cons form
+		     (mapcar (function (lambda (x)
+					 (list 'quote x)))
+			     tailmaker)))
+	 (setq state 'list))
+	(t
+	 (setq tailmaker
+	       (list (list 'list form)
+		     (list 'quote tailmaker)))
+	 (setq state 'append))))
+
+(defun bq-evallist (form)
+  (bq-push form tailmaker))
+
+;;;  (cond ((matches ((list (,@ X)) (,@ Y)))
+ ;;;        (` ((list form  (,@ X)) (,@ Y))))
+ ;;;       ((matches (X))
+ ;;;        (` (form (,@ X))) (setq state 'cons))
+ ;;;       ((matches ((,@ X)))
+ ;;;        (` (form (,@ X)))))
+(defun bq-evalappend (form)
+  (cond ((and (listp tailmaker)
+	   (listp (car tailmaker))
+	   (eq (bq-caar tailmaker) 'list))
+	 (rplacd (car tailmaker)
+		 (cons form (bq-cdar tailmaker))))
+	((= (length tailmaker) 1)
+	 (setq tailmaker (cons form tailmaker)
+	       state 'cons))
+	(t (bq-push (list 'list form) tailmaker))))
+
+(defun bq-evalnil (form)
+  (setq tailmaker (list form)
+	state 'list))
+
+;;; (if (matches (X Y))  ; it must
+ ;;;    (progn (setq state 'append)
+ ;;;           (` (form (cons X Y)))))   ; couldn't think of anything clever
+(defun bq-splicecons (form)
+  (setq tailmaker
+	(list form
+	      (list 'cons (car tailmaker) (bq-cadr tailmaker)))
+	state 'append))
+
+(defun bq-splicequote (form)
+  (setq tailmaker (list form (list 'quote tailmaker))
+	state 'append))
+
+(defun bq-splicelist (form)
+  (setq tailmaker (list form (cons 'list tailmaker))
+	state 'append))
+
+(defun bq-spliceappend (form)
+  (bq-push form tailmaker))
+
+(defun bq-splicenil (form)
+  (setq state 'append
+	tailmaker (list form)))