|
|
1.1 ! root 1: ;; Copyright (C) 1985 Free Software Foundation, Inc. ! 2: ;; Written by Dick King (king@kestrel). ! 3: ! 4: ;; This file is part of GNU Emacs. ! 5: ! 6: ;; GNU Emacs is distributed in the hope that it will be useful, ! 7: ;; but WITHOUT ANY WARRANTY. No author or distributor ! 8: ;; accepts responsibility to anyone for the consequences of using it ! 9: ;; or for whether it serves any particular purpose or works at all, ! 10: ;; unless he says so in writing. Refer to the GNU Emacs General Public ! 11: ;; License for full details. ! 12: ! 13: ;; Everyone is granted permission to copy, modify and redistribute ! 14: ;; GNU Emacs, but only under the conditions described in the ! 15: ;; GNU Emacs General Public License. A copy of this license is ! 16: ;; supposed to have been given to you along with GNU Emacs so you ! 17: ;; can know your rights and responsibilities. It should be in a ! 18: ;; file named COPYING. Among other things, the copyright notice ! 19: ;; and this notice must be preserved on all copies. ! 20: ! 21: ! 22: ;;; This is a rudimentry backquote package written by D. King, ! 23: ;;; king@kestrel, on 8/31/85. (` x) is a macro ! 24: ;;; that expands to a form that produces x. (` (a b ..)) is ! 25: ;;; a macro that expands into a form that produces a list of what a b ! 26: ;;; etc. would have produced. Any element can be of the form ! 27: ;;; (, <form>) in which case the resulting form evaluates ! 28: ;;; <form> before putting it into place, or (,@ <form>), in which ! 29: ;;; case the evaluation of <form> is arranged for and each element ! 30: ;;; of the result (which must be a (possibly null) list) is inserted. ! 31: ;;; As an example, the immediately following macro push (v l) could ! 32: ;;; have been written ! 33: ;;; (defmacro push (v l) ! 34: ;;; (` (setq (, l) (cons (,@ (list v l)))))) ! 35: ;;; although ! 36: ;;; (defmacro push (v l) ! 37: ;;; (` (setq (, l) (cons (, v) (, l))))) ! 38: ;;; is far more natural. The magic atoms , ! 39: ;;; and ,@ are user-settable and list-valued. We recommend that ! 40: ;;; things never be removed from this list lest you break something ! 41: ;;; someone else wrote in the dim past that comes to be recompiled in ! 42: ;;; the distant future. ! 43: ! 44: ;;; LIMITATIONS: tail consing is not handled correctly. Do not say ! 45: ;;; (` (a . (, b))) - say (` (a (,@ b))) ! 46: ;;; which works even if b is not list-valued. ! 47: ;;; No attempt is made to handle vectors. (` [a (, b) c]) doesn't work. ! 48: ;;; Sorry, you must say things like ! 49: ;;; (` (a (,@ 'b))) to get (a . b) and ! 50: ;;; (` ((, ',) c)) to get (, c) - [(` (a , b)) will work but is a bad habit] ! 51: ;;; I haven't taught it the joys of nconc. ! 52: ;;; (` atom) dies. (` (, atom)) or anything else is okay. ! 53: ! 54: ;;; BEWARE BEWARE BEWARE ! 55: ;;; inclusion of (,atom) rather than (, atom) or (,@atom) rather than ! 56: ;;; (,@ atom) will result in errors that will show up very late. ! 57: ;;; This is so crunchy that I am considering including a check for ! 58: ;;; this or changing the syntax to ... ,(<form>). RMS: opinion? ! 59: ! 60: ! 61: (provide 'backquote) ! 62: ! 63: ;;; a raft of general-purpose macros follows. See the nearest ! 64: ;;; Commonlisp manual. ! 65: (defmacro bq-push (v l) ! 66: "Pushes evaluated first form onto second unevaluated object ! 67: a list-value atom" ! 68: (list 'setq l (list 'cons v l))) ! 69: ! 70: (defmacro bq-caar (l) ! 71: (list 'car (list 'car l))) ! 72: ! 73: (defmacro bq-cadr (l) ! 74: (list 'car (list 'cdr l))) ! 75: ! 76: (defmacro bq-cdar (l) ! 77: (list 'cdr (list 'car l))) ! 78: ! 79: ! 80: ;;; These two advertised variables control what characters are used to ! 81: ;;; unquote things. I have included , and ,@ as the unquote and ! 82: ;;; splice operators, respectively, to give users of MIT CADR machine ! 83: ;;; derivitive machines a warm, cosy feeling. ! 84: ! 85: (defconst backquote-unquote '(,) ! 86: "*A list of all objects that stimulate unquoting in `. Memq test.") ! 87: ! 88: ! 89: (defconst backquote-splice '(,@) ! 90: "*A list of all objects that stimulate splicing in `. Memq test.") ! 91: ! 92: ! 93: ;;; This is the interface ! 94: (defmacro ` (form) ! 95: "(` FORM) Expands to a form that will generate FORM. ! 96: FORM is `almost quoted' -- see backquote.el for a description." ! 97: (bq-make-maker form)) ! 98: ! 99: ;;; We develop the method for building the desired list from ! 100: ;;; the end towards the beginning. The contract is that there be a ! 101: ;;; variable called state and a list called tailmaker, and that the form ! 102: ;;; (cons state tailmaker) deliver the goods. Exception - if the ! 103: ;;; state is quote the tailmaker is the form itself. ! 104: ;;; This function takes a form and returns what I will call a maker in ! 105: ;;; what follows. Evaluating the maker would produce the form, ! 106: ;;; properly evaluated according to , and ,@ rules. ! 107: ;;; I work backwards - it seemed a lot easier. The reason for this is ! 108: ;;; if I'm in some sort of a routine building a maker and I switch ! 109: ;;; gears, it seemed to me easier to jump into some other state and ! 110: ;;; glue what I've already done to the end, than to to prepare that ! 111: ;;; something and go back to put things together. ! 112: (defun bq-make-maker (form) ! 113: "Given one argument, a `mostly quoted' object, produces a maker. ! 114: See backquote.el for details" ! 115: (let ((tailmaker (quote nil)) (qc 0) (ec 0) (state nil)) ! 116: (mapcar 'bq-iterative-list-builder (reverse form)) ! 117: (and state ! 118: (cond ((eq state 'quote) ! 119: (list state tailmaker)) ! 120: ((= (length tailmaker) 1) ! 121: (funcall (bq-cadr (assq state bq-singles)) tailmaker)) ! 122: (t (cons state tailmaker)))))) ! 123: ! 124: ;;; There are exceptions - we wouldn't want to call append of one ! 125: ;;; argument, for example. ! 126: (defconst bq-singles '((quote bq-quotecar) ! 127: (append car) ! 128: (list bq-make-list) ! 129: (cons bq-id))) ! 130: ! 131: (defun bq-id (x) x) ! 132: ! 133: (defun bq-quotecar (x) (list 'quote (car x))) ! 134: ! 135: (defun bq-make-list (x) (cons 'list x)) ! 136: ! 137: ;;; fr debugging use only ! 138: ;(defun funcalll (a b) (funcall a b)) ! 139: ;(defun funcalll (a b) (debug nil 'enter state tailmaker a b) ! 140: ; (let ((ans (funcall a b))) (debug nil 'leave state tailmaker) ! 141: ; ans)) ! 142: ! 143: ;;; Given a state/tailmaker pair that already knows how to make a ! 144: ;;; partial tail of the desired form, this function knows how to add ! 145: ;;; yet another element to the burgening list. There are four cases; ! 146: ;;; the next item is an atom (which will certainly be quoted); a ! 147: ;;; (, xxx), which will be evaluated and put into the list at the top ! 148: ;;; level; a (,@ xxx), which will be evaluated and spliced in, or ! 149: ;;; some other list, in which case we first compute the form's maker, ! 150: ;;; and then we either launch into the quoted case if the maker's ! 151: ;;; top level function is quote, or into the comma case if it isn't. ! 152: ;;; The fourth case reduces to one of the other three, so here we have ! 153: ;;; a choice of three ways to build tailmaker, and cit turns out we ! 154: ;;; use five possible values of state (although someday I'll add ! 155: ;;; nconcto the possible values of state). ! 156: ;;; This maintains the invariant that (cons state tailmaker) is the ! 157: ;;; maker for the elements of the tail we've eaten so far. ! 158: (defun bq-iterative-list-builder (form) ! 159: "Called by bq-make-maker. Adds a new item form to tailmaker, ! 160: changing state if need be, so tailmaker and state constitute a recipie ! 161: for making the list so far." ! 162: (cond ((atom form) ! 163: (funcall (bq-cadr (assq state bq-quotefns)) form)) ! 164: ((memq (car form) backquote-unquote) ! 165: (funcall (bq-cadr (assq state bq-evalfns)) (bq-cadr form))) ! 166: ((memq (car form) backquote-splice) ! 167: (funcall (bq-cadr (assq state bq-splicefns)) (bq-cadr form))) ! 168: (t ! 169: (let ((newform (bq-make-maker form))) ! 170: (if (and (listp newform) (eq (car newform) 'quote)) ! 171: (funcall (bq-cadr (assq state bq-quotefns)) (bq-cadr newform)) ! 172: (funcall (bq-cadr (assq state bq-evalfns)) newform)))) ! 173: )) ! 174: ! 175: ;;; We do a 2-d branch on the form of splicing and the old state. ! 176: ;;; Here's fifteen functions' names... ! 177: (defconst bq-splicefns '((nil bq-splicenil) ! 178: (append bq-spliceappend) ! 179: (list bq-splicelist) ! 180: (quote bq-splicequote) ! 181: (cons bq-splicecons))) ! 182: ! 183: (defconst bq-evalfns '((nil bq-evalnil) ! 184: (append bq-evalappend) ! 185: (list bq-evallist) ! 186: (quote bq-evalquote) ! 187: (cons bq-evalcons))) ! 188: ! 189: (defconst bq-quotefns '((nil bq-quotenil) ! 190: (append bq-quoteappend) ! 191: (list bq-quotelist) ! 192: (quote bq-quotequote) ! 193: (cons bq-quotecons))) ! 194: ! 195: ;;; The name of each function is ! 196: ;;; (concat 'bq- <type-of-element-addition> <old-state>) ! 197: ;;; I'll comment the non-obvious ones before the definitions... ! 198: ;;; In what follows, uppercase letters and form will always be ! 199: ;;; metavariables that don't need commas in backquotes, and I will ! 200: ;;; assume the existence of something like matches that takes a ! 201: ;;; backquote-like form and a value, binds metavariables and returns ! 202: ;;; t if the pattern match is successful, returns nil otherwise. I ! 203: ;;; will write such a goodie someday. ! 204: ! 205: ;;; (setq tailmaker ! 206: ;;; (if (matches ((quote X) Y) tailmaker) ! 207: ;;; (` ((quote (form X)) Y)) ! 208: ;;; (` ((list form (quote X)) Y)))) ! 209: ;;; (setq state 'append) ! 210: (defun bq-quotecons (form) ! 211: (if (and (listp (car tailmaker)) ! 212: (eq (bq-caar tailmaker) 'quote)) ! 213: (setq tailmaker ! 214: (list (list 'quote (list form (bq-cadr (car tailmaker)))) ! 215: (bq-cadr tailmaker))) ! 216: (setq tailmaker ! 217: (list (list 'list ! 218: (list 'quote form) ! 219: (car tailmaker)) ! 220: (bq-cadr tailmaker)))) ! 221: (setq state 'append)) ! 222: ! 223: (defun bq-quotequote (form) ! 224: (bq-push form tailmaker)) ! 225: ! 226: ;;; Could be improved to convert (list 'a 'b 'c .. 'w x) ! 227: ;;; to (append '(a b c .. w) x) ! 228: ;;; when there are enough elements ! 229: (defun bq-quotelist (form) ! 230: (bq-push (list 'quote form) tailmaker)) ! 231: ! 232: ;;; (setq tailmaker ! 233: ;;; (if (matches ((quote X) (,@ Y))) ! 234: ;;; (` ((quote (, (cons form X))) (,@ Y))))) ! 235: (defun bq-quoteappend (form) ! 236: (cond ((and (listp tailmaker) ! 237: (listp (car tailmaker)) ! 238: (eq (bq-caar tailmaker) 'quote)) ! 239: (rplaca (bq-cdar tailmaker) ! 240: (cons form (car (bq-cdar tailmaker))))) ! 241: (t (bq-push (list 'quote (list form)) tailmaker)))) ! 242: ! 243: (defun bq-quotenil (form) ! 244: (setq tailmaker (list form)) ! 245: (setq state 'quote)) ! 246: ! 247: ;;; (if (matches (X Y) tailmaker) ; it must ! 248: ;;; (` ((list form X) Y))) ! 249: (defun bq-evalcons (form) ! 250: (setq tailmaker ! 251: (list (list 'list form (car tailmaker)) ! 252: (bq-cadr tailmaker))) ! 253: (setq state 'append)) ! 254: ! 255: ;;; (if (matches (X Y Z (,@ W))) ! 256: ;;; (progn (setq state 'append) ! 257: ;;; (` ((list form) (quote (X Y Z (,@ W)))))) ! 258: ;;; (progn (setq state 'list) ! 259: ;;; (list form 'X 'Y .. ))) ; quote each one there is ! 260: (defun bq-evalquote (form) ! 261: (cond ((< (length tailmaker) 3) ! 262: (setq tailmaker ! 263: (cons form ! 264: (mapcar (function (lambda (x) ! 265: (list 'quote x))) ! 266: tailmaker))) ! 267: (setq state 'list)) ! 268: (t ! 269: (setq tailmaker ! 270: (list (list 'list form) ! 271: (list 'quote tailmaker))) ! 272: (setq state 'append)))) ! 273: ! 274: (defun bq-evallist (form) ! 275: (bq-push form tailmaker)) ! 276: ! 277: ;;; (cond ((matches ((list (,@ X)) (,@ Y))) ! 278: ;;; (` ((list form (,@ X)) (,@ Y)))) ! 279: ;;; ((matches (X)) ! 280: ;;; (` (form (,@ X))) (setq state 'cons)) ! 281: ;;; ((matches ((,@ X))) ! 282: ;;; (` (form (,@ X))))) ! 283: (defun bq-evalappend (form) ! 284: (cond ((and (listp tailmaker) ! 285: (listp (car tailmaker)) ! 286: (eq (bq-caar tailmaker) 'list)) ! 287: (rplacd (car tailmaker) ! 288: (cons form (bq-cdar tailmaker)))) ! 289: ((= (length tailmaker) 1) ! 290: (setq tailmaker (cons form tailmaker)) ! 291: (setq state 'cons)) ! 292: (t (bq-push (list 'list form) tailmaker)))) ! 293: ! 294: (defun bq-evalnil (form) ! 295: (setq tailmaker (list form)) ! 296: (setq state 'list)) ! 297: ! 298: ;;; (if (matches (X Y)) ; it must ! 299: ;;; (progn (setq state 'append) ! 300: ;;; (` (form (cons X Y))))) ; couldn't think of anything clever ! 301: (defun bq-splicecons (form) ! 302: (setq tailmaker ! 303: (list form ! 304: (list 'cons (car tailmaker) (bq-cadr tailmaker)))) ! 305: (setq state 'append)) ! 306: ! 307: (defun bq-splicequote (form) ! 308: (setq tailmaker (list form (list 'quote (list tailmaker)))) ! 309: (setq state 'append)) ! 310: ! 311: (defun bq-splicelist (form) ! 312: (setq tailmaker (list form (cons 'list tailmaker))) ! 313: (setq state 'append)) ! 314: ! 315: (defun bq-spliceappend (form) ! 316: (bq-push form tailmaker)) ! 317: ! 318: (defun bq-splicenil (form) ! 319: (setq state 'append) ! 320: (setq tailmaker (list form))) ! 321: ! 322: ! 323:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.