| 1 | ;;; backquote.lisp |
|---|
| 2 | ;;; |
|---|
| 3 | ;;; Copyright (C) 2004-2005 Peter Graves |
|---|
| 4 | ;;; $Id: backquote.lisp 15569 2022-03-19 12:50:18Z mevenson $ |
|---|
| 5 | ;;; |
|---|
| 6 | ;;; This program is free software; you can redistribute it and/or |
|---|
| 7 | ;;; modify it under the terms of the GNU General Public License |
|---|
| 8 | ;;; as published by the Free Software Foundation; either version 2 |
|---|
| 9 | ;;; of the License, or (at your option) any later version. |
|---|
| 10 | ;;; |
|---|
| 11 | ;;; This program is distributed in the hope that it will be useful, |
|---|
| 12 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | ;;; GNU General Public License for more details. |
|---|
| 15 | ;;; |
|---|
| 16 | ;;; You should have received a copy of the GNU General Public License |
|---|
| 17 | ;;; along with this program; if not, write to the Free Software |
|---|
| 18 | ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 19 | ;;; |
|---|
| 20 | ;;; As a special exception, the copyright holders of this library give you |
|---|
| 21 | ;;; permission to link this library with independent modules to produce an |
|---|
| 22 | ;;; executable, regardless of the license terms of these independent |
|---|
| 23 | ;;; modules, and to copy and distribute the resulting executable under |
|---|
| 24 | ;;; terms of your choice, provided that you also meet, for each linked |
|---|
| 25 | ;;; independent module, the terms and conditions of the license of that |
|---|
| 26 | ;;; module. An independent module is a module which is not derived from |
|---|
| 27 | ;;; or based on this library. If you modify this library, you may extend |
|---|
| 28 | ;;; this exception to your version of the library, but you are not |
|---|
| 29 | ;;; obligated to do so. If you do not wish to do so, delete this |
|---|
| 30 | ;;; exception statement from your version. |
|---|
| 31 | |
|---|
| 32 | ;;; Adapted from SBCL. |
|---|
| 33 | |
|---|
| 34 | ;;;; the backquote reader macro |
|---|
| 35 | |
|---|
| 36 | ;;;; This software is part of the SBCL system. See the README file for |
|---|
| 37 | ;;;; more information. |
|---|
| 38 | ;;;; |
|---|
| 39 | ;;;; This software is derived from the CMU CL system, which was |
|---|
| 40 | ;;;; written at Carnegie Mellon University and released into the |
|---|
| 41 | ;;;; public domain. The software is in the public domain and is |
|---|
| 42 | ;;;; provided with absolutely no warranty. See the COPYING and CREDITS |
|---|
| 43 | ;;;; files for more information. |
|---|
| 44 | |
|---|
| 45 | (in-package #:system) |
|---|
| 46 | |
|---|
| 47 | ;;; The flags passed back by BACKQUOTIFY can be interpreted as follows: |
|---|
| 48 | ;;; |
|---|
| 49 | ;;; |`,|: [a] => a |
|---|
| 50 | ;;; NIL: [a] => a ;the NIL flag is used only when a is NIL |
|---|
| 51 | ;;; T: [a] => a ;the T flag is used when a is self-evaluating |
|---|
| 52 | ;;; QUOTE: [a] => (QUOTE a) |
|---|
| 53 | ;;; APPEND: [a] => (APPEND . a) |
|---|
| 54 | ;;; NCONC: [a] => (NCONC . a) |
|---|
| 55 | ;;; LIST: [a] => (LIST . a) |
|---|
| 56 | ;;; LIST*: [a] => (LIST* . a) |
|---|
| 57 | ;;; |
|---|
| 58 | ;;; The flags are combined according to the following set of rules: |
|---|
| 59 | ;;; ([a] means that a should be converted according to the previous table) |
|---|
| 60 | ;;; |
|---|
| 61 | ;;; \ car || otherwise | QUOTE or | |`,@| | |`,.| |
|---|
| 62 | ;;;cdr \ || | T or NIL | | |
|---|
| 63 | ;;;================================================================================ |
|---|
| 64 | ;;; |`,| || LIST* ([a] [d]) | LIST* ([a] [d]) | APPEND (a [d]) | NCONC (a [d]) |
|---|
| 65 | ;;; NIL || LIST ([a]) | QUOTE (a) | <hair> a | <hair> a |
|---|
| 66 | ;;;QUOTE or T|| LIST* ([a] [d]) | QUOTE (a . d) | APPEND (a [d]) | NCONC (a [d]) |
|---|
| 67 | ;;; APPEND || LIST* ([a] [d]) | LIST* ([a] [d]) | APPEND (a . d) | NCONC (a [d]) |
|---|
| 68 | ;;; NCONC || LIST* ([a] [d]) | LIST* ([a] [d]) | APPEND (a [d]) | NCONC (a . d) |
|---|
| 69 | ;;; LIST || LIST ([a] . d) | LIST ([a] . d) | APPEND (a [d]) | NCONC (a [d]) |
|---|
| 70 | ;;; LIST* || LIST* ([a] . d) | LIST* ([a] . d) | APPEND (a [d]) | NCONC (a [d]) |
|---|
| 71 | ;;; |
|---|
| 72 | ;;;<hair> involves starting over again pretending you had read ".,a)" instead |
|---|
| 73 | ;;; of ",@a)" |
|---|
| 74 | |
|---|
| 75 | ;; (%defvar '*backquote-count* 0) ; defined in Java, q.v. Lisp.java:2754 |
|---|
| 76 | (%defvar '*bq-comma-flag* '(|,|)) |
|---|
| 77 | (%defvar '*bq-at-flag* '(|,@|)) |
|---|
| 78 | (%defvar '*bq-dot-flag* '(|,.|)) |
|---|
| 79 | ;; (%defvar '*bq-vector-flag* '(|bqv|)) ; defined in Java, q.v. Lisp.java:2757 |
|---|
| 80 | |
|---|
| 81 | ;;; the actual character macro |
|---|
| 82 | (defun backquote-macro (stream ignore) |
|---|
| 83 | (declare (ignore ignore)) |
|---|
| 84 | (let ((*backquote-count* (1+ *backquote-count*))) |
|---|
| 85 | (multiple-value-bind (flag thing) |
|---|
| 86 | (backquotify stream (read stream t nil t)) |
|---|
| 87 | (when (eq flag *bq-at-flag*) |
|---|
| 88 | (%reader-error stream ",@ after backquote in ~S" thing)) |
|---|
| 89 | (when (eq flag *bq-dot-flag*) |
|---|
| 90 | (%reader-error stream ",. after backquote in ~S" thing)) |
|---|
| 91 | (backquotify-1 flag thing)))) |
|---|
| 92 | |
|---|
| 93 | (defun comma-macro (stream ignore) |
|---|
| 94 | (declare (ignore ignore)) |
|---|
| 95 | (unless (> *backquote-count* 0) |
|---|
| 96 | (when *read-suppress* |
|---|
| 97 | (return-from comma-macro nil)) |
|---|
| 98 | (%reader-error stream "Comma not inside a backquote.")) |
|---|
| 99 | (let ((c (read-char stream)) |
|---|
| 100 | (*backquote-count* (1- *backquote-count*))) |
|---|
| 101 | (cond ((char= c #\@) |
|---|
| 102 | (cons *bq-at-flag* (read stream t nil t))) |
|---|
| 103 | ((char= c #\.) |
|---|
| 104 | (cons *bq-dot-flag* (read stream t nil t))) |
|---|
| 105 | (t (unread-char c stream) |
|---|
| 106 | (cons *bq-comma-flag* (read stream t nil t)))))) |
|---|
| 107 | |
|---|
| 108 | ;;; |
|---|
| 109 | (defun expandable-backq-expression-p (object) |
|---|
| 110 | (and (consp object) |
|---|
| 111 | (let ((flag (%car object))) |
|---|
| 112 | (or (eq flag *bq-at-flag*) |
|---|
| 113 | (eq flag *bq-dot-flag*))))) |
|---|
| 114 | |
|---|
| 115 | ;;; This does the expansion from table 2. |
|---|
| 116 | (defun backquotify (stream code) |
|---|
| 117 | (cond ((atom code) |
|---|
| 118 | (cond ((null code) (values nil nil)) |
|---|
| 119 | ((or (consp code) |
|---|
| 120 | (symbolp code)) |
|---|
| 121 | ;; Keywords are self-evaluating. Install after packages. |
|---|
| 122 | (values 'quote code)) |
|---|
| 123 | (t (values t code)))) |
|---|
| 124 | ((or (eq (car code) *bq-at-flag*) |
|---|
| 125 | (eq (car code) *bq-dot-flag*)) |
|---|
| 126 | (values (car code) (cdr code))) |
|---|
| 127 | ((eq (car code) *bq-comma-flag*) |
|---|
| 128 | (comma (cdr code))) |
|---|
| 129 | ((eq (car code) *bq-vector-flag*) |
|---|
| 130 | (multiple-value-bind (dflag d) (backquotify stream (cdr code)) |
|---|
| 131 | (values 'vector (backquotify-1 dflag d)))) |
|---|
| 132 | (t (multiple-value-bind (aflag a) (backquotify stream (car code)) |
|---|
| 133 | (multiple-value-bind (dflag d) (backquotify stream (cdr code)) |
|---|
| 134 | (when (eq dflag *bq-at-flag*) |
|---|
| 135 | ;; Get the errors later. |
|---|
| 136 | (%reader-error stream ",@ after dot in ~S" code)) |
|---|
| 137 | (when (eq dflag *bq-dot-flag*) |
|---|
| 138 | (%reader-error stream ",. after dot in ~S" code)) |
|---|
| 139 | (cond |
|---|
| 140 | ((eq aflag *bq-at-flag*) |
|---|
| 141 | (if (null dflag) |
|---|
| 142 | (if (expandable-backq-expression-p a) |
|---|
| 143 | (values 'append (list a)) |
|---|
| 144 | (comma a)) |
|---|
| 145 | (values 'append |
|---|
| 146 | (cond ((eq dflag 'append) |
|---|
| 147 | (cons a d )) |
|---|
| 148 | (t (list a (backquotify-1 dflag d))))))) |
|---|
| 149 | ((eq aflag *bq-dot-flag*) |
|---|
| 150 | (if (null dflag) |
|---|
| 151 | (if (expandable-backq-expression-p a) |
|---|
| 152 | (values 'nconc (list a)) |
|---|
| 153 | (comma a)) |
|---|
| 154 | (values 'nconc |
|---|
| 155 | (cond ((eq dflag 'nconc) |
|---|
| 156 | (cons a d)) |
|---|
| 157 | (t (list a (backquotify-1 dflag d))))))) |
|---|
| 158 | ((null dflag) |
|---|
| 159 | (if (memq aflag '(quote t nil)) |
|---|
| 160 | (values 'quote (list a)) |
|---|
| 161 | (values 'list (list (backquotify-1 aflag a))))) |
|---|
| 162 | ((memq dflag '(quote t)) |
|---|
| 163 | (if (memq aflag '(quote t nil)) |
|---|
| 164 | (values 'quote (cons a d )) |
|---|
| 165 | (values 'list* (list (backquotify-1 aflag a) |
|---|
| 166 | (backquotify-1 dflag d))))) |
|---|
| 167 | (t (setq a (backquotify-1 aflag a)) |
|---|
| 168 | (if (memq dflag '(list list*)) |
|---|
| 169 | (values dflag (cons a d)) |
|---|
| 170 | (values 'list* |
|---|
| 171 | (list a (backquotify-1 dflag d))))))))))) |
|---|
| 172 | |
|---|
| 173 | ;;; This handles the <hair> cases. |
|---|
| 174 | (defun comma (code) |
|---|
| 175 | (cond ((atom code) |
|---|
| 176 | (cond ((null code) |
|---|
| 177 | (values nil nil)) |
|---|
| 178 | ((or (numberp code) (eq code t)) |
|---|
| 179 | (values t code)) |
|---|
| 180 | (t (values *bq-comma-flag* code)))) |
|---|
| 181 | ((and (eq (car code) 'quote) |
|---|
| 182 | (not (expandable-backq-expression-p (cadr code)))) |
|---|
| 183 | (values (car code) (cadr code))) |
|---|
| 184 | ((memq (car code) '(append list list* nconc)) |
|---|
| 185 | (values (car code) (cdr code))) |
|---|
| 186 | ((eq (car code) 'cons) |
|---|
| 187 | (values 'list* (cdr code))) |
|---|
| 188 | (t (values *bq-comma-flag* code)))) |
|---|
| 189 | |
|---|
| 190 | ;;; This handles table 1. |
|---|
| 191 | (defun backquotify-1 (flag thing) |
|---|
| 192 | (cond ((or (eq flag *bq-comma-flag*) |
|---|
| 193 | (memq flag '(t nil))) |
|---|
| 194 | thing) |
|---|
| 195 | ((eq flag 'quote) |
|---|
| 196 | (list 'quote thing)) |
|---|
| 197 | ((eq flag 'list*) |
|---|
| 198 | (cond ((and (null (cddr thing)) |
|---|
| 199 | (not (expandable-backq-expression-p (cadr thing)))) |
|---|
| 200 | (cons 'backq-cons thing)) |
|---|
| 201 | ((expandable-backq-expression-p (car (last thing))) |
|---|
| 202 | (list 'backq-append |
|---|
| 203 | (cons 'backq-list (butlast thing)) |
|---|
| 204 | ;; Can it be optimized further? -- APD, 2001-12-21 |
|---|
| 205 | (car (last thing)))) |
|---|
| 206 | (t |
|---|
| 207 | (cons 'backq-list* thing)))) |
|---|
| 208 | ((eq flag 'vector) |
|---|
| 209 | (list 'backq-vector thing)) |
|---|
| 210 | (t (cons (ecase flag |
|---|
| 211 | ((list) 'backq-list) |
|---|
| 212 | ((append) 'backq-append) |
|---|
| 213 | ((nconc) 'backq-nconc)) |
|---|
| 214 | thing)))) |
|---|
| 215 | |
|---|
| 216 | ;;;; magic BACKQ- versions of builtin functions |
|---|
| 217 | |
|---|
| 218 | ;;; Define synonyms for the lisp functions we use, so that by using |
|---|
| 219 | ;;; them, the backquoted material will be recognizable to the |
|---|
| 220 | ;;; pretty-printer. |
|---|
| 221 | (defun backq-list (&rest args) (apply #'list args)) |
|---|
| 222 | (defun backq-list* (&rest args) (apply #'list* args)) |
|---|
| 223 | (defun backq-append (&rest args) (apply #'append args)) |
|---|
| 224 | (defun backq-nconc (&rest args) (apply #'nconc args)) |
|---|
| 225 | (defun backq-cons (&rest args) (apply #'cons args)) |
|---|
| 226 | |
|---|
| 227 | (defun backq-vector (list) |
|---|
| 228 | (declare (list list)) |
|---|
| 229 | (coerce list 'simple-vector)) |
|---|
| 230 | |
|---|
| 231 | ;;; The pretty-printer needs to know about our special tokens |
|---|
| 232 | (%defvar '*backq-tokens* |
|---|
| 233 | '(backq-comma backq-comma-at backq-comma-dot backq-list |
|---|
| 234 | backq-list* backq-append backq-nconc backq-cons backq-vector)) |
|---|
| 235 | |
|---|
| 236 | (defun %reader-error (stream control &rest args) |
|---|
| 237 | (error 'reader-error |
|---|
| 238 | :stream stream |
|---|
| 239 | :format-control control |
|---|
| 240 | :format-arguments args)) |
|---|