|
| 1 | +;;;; Metatronic macros |
| 2 | +;;; |
| 3 | + |
| 4 | +#+org.tfeb.tools.require-module |
| 5 | +(org.tfeb.tools.require-module:needs |
| 6 | + (:org.tfeb.hax.utilities :compile t)) |
| 7 | + |
| 8 | +(defpackage :org.tfeb.hax.metatronic |
| 9 | + (:use :cl :org.tfeb.hax.utilities) |
| 10 | + (:export |
| 11 | + #:defmacro/m |
| 12 | + #:macrolet/m |
| 13 | + #:metatronize)) |
| 14 | + |
| 15 | +(in-package :org.tfeb.hax.metatronic) |
| 16 | + |
| 17 | +(provide :org.tfeb.hax.metatronic) |
| 18 | + |
| 19 | +(defun metatronize (form &key |
| 20 | + (rewrites '()) (shares '()) |
| 21 | + (rewriter nil)) |
| 22 | + ;; This has hard-wired knowledge of what a metatronic variable looks |
| 23 | + ;; like unless REWRITER is given |
| 24 | + "Return a metatronic version of FORM, the table of variables, a list |
| 25 | +of anonymous variables and the sharing table. |
| 26 | +
|
| 27 | +Arguments are FORM with keyword arguments |
| 28 | +- REWRITES is a table of variables returned from a previous call |
| 29 | +- SHARES is a sharing table returned from a previous call |
| 30 | +- REWRITER, if given, should be a designator for a function of one |
| 31 | + argument, a symbol, which should either return the symbol or a |
| 32 | + metatronized symbol and an indication of whether it should be stored |
| 33 | + in the rewrite table. |
| 34 | +
|
| 35 | +This only looks at list structure. Sharing and circularity of list |
| 36 | +structure (only) is correctly copied." |
| 37 | + (let ((rtab rewrites) ;I feel bad assigning to arguments |
| 38 | + (stab shares) |
| 39 | + (anons '())) |
| 40 | + (labels ((rewrite (this) |
| 41 | + (typecase this |
| 42 | + (symbol |
| 43 | + (let ((r (assoc this rtab))) |
| 44 | + (if r |
| 45 | + (cdr r) |
| 46 | + (if rewriter |
| 47 | + (multiple-value-bind (new storep) |
| 48 | + (funcall rewriter this) |
| 49 | + (if (eq new this) |
| 50 | + this |
| 51 | + (progn |
| 52 | + (if storep |
| 53 | + (setf rtab (acons this new rtab)) |
| 54 | + (push new anons)) |
| 55 | + new))) |
| 56 | + (let* ((n (symbol-name this)) |
| 57 | + (l (length n))) |
| 58 | + (if (and (>= l 2) |
| 59 | + (char= (char n 0) #\<) |
| 60 | + (char= (char n (1- l)) #\>)) |
| 61 | + (let ((s (make-symbol n))) |
| 62 | + (if (/= l 2) |
| 63 | + (setf rtab (acons this s rtab)) |
| 64 | + (push s anons)) |
| 65 | + s) |
| 66 | + this)))))) |
| 67 | + (cons |
| 68 | + (let ((seen (assoc this stab))) |
| 69 | + (if seen |
| 70 | + (cdr seen) |
| 71 | + (let ((new (cons nil nil))) |
| 72 | + (setf stab (acons this new stab) |
| 73 | + (car new) (rewrite (car this)) |
| 74 | + (cdr new) (rewrite (cdr this))) |
| 75 | + new)))) |
| 76 | + (t |
| 77 | + ;; Not going to handle arrays etc because it is a |
| 78 | + ;; lot of work for almost or actually no benefit. |
| 79 | + this)))) |
| 80 | + (values (rewrite form) rtab anons stab)))) |
| 81 | + |
| 82 | +(defun m2 (form metatrons anonymous) |
| 83 | + ;; Second quantization: this just exists so macroexpansions are |
| 84 | + ;; smaller |
| 85 | + (values (metatronize form |
| 86 | + :rewriter (lambda (s) |
| 87 | + (cond |
| 88 | + ((member s metatrons) |
| 89 | + (values (make-symbol (symbol-name s)) |
| 90 | + t)) |
| 91 | + ((member s anonymous) |
| 92 | + (values (make-symbol (symbol-name s)) |
| 93 | + nil)) |
| 94 | + (t |
| 95 | + (values s nil))))))) |
| 96 | + |
| 97 | +(defmacro defmacro/m (name (&rest args) &body doc/decls/forms) |
| 98 | + "Define a metatronic macro |
| 99 | +
|
| 100 | +This is exactly like DEFMACRO but metatronic symbols are gensymized, |
| 101 | +when they occur directly in list structure. |
| 102 | +
|
| 103 | +Note that metatronic symbols are *not* gensymized in arrays, |
| 104 | +structures or what have you as it's just too hard. Use |
| 105 | +LOAD-TIME-VALUE to construct a literal at load time if you really need |
| 106 | +this." |
| 107 | + (multiple-value-bind (doc decls forms) (parse-docstring-body doc/decls/forms) |
| 108 | + (multiple-value-bind (metatronized-forms rtab anons stab) (metatronize forms) |
| 109 | + (declare (ignore stab)) |
| 110 | + `(defmacro ,name ,args |
| 111 | + ,@(if doc (list doc) '()) |
| 112 | + ,@decls |
| 113 | + (m2 (progn ,@metatronized-forms) |
| 114 | + ',(mapcar #'cdr rtab) ',anons))))) |
| 115 | + |
| 116 | +(defmacro macrolet/m (clauses &body forms) |
| 117 | + "MACROLET, metatronically" |
| 118 | + `(macrolet |
| 119 | + ,(mapcar (lambda (clause) |
| 120 | + (destructuring-bind (name (&rest args) &body doc/decls/forms) clause |
| 121 | + (multiple-value-bind (doc decls forms) (parse-docstring-body doc/decls/forms) |
| 122 | + (multiple-value-bind (metatronized-forms rtab anons stab) |
| 123 | + (metatronize forms) |
| 124 | + (declare (ignore stab)) |
| 125 | + `(,name ,args |
| 126 | + ,@(if doc (list doc) '()) |
| 127 | + ,@decls |
| 128 | + (m2 (progn ,@metatronized-forms) |
| 129 | + ',(mapcar #'cdr rtab) |
| 130 | + ',anons)))))) |
| 131 | + clauses) |
| 132 | + ,@forms)) |
| 133 | + |
| 134 | +#+(and LispWorks LW-Editor) |
| 135 | +(editor:setup-indent "macrolet/m" 1 nil nil 'flet) |
| 136 | + |
| 137 | +#|| |
| 138 | +(defmacro/m do-file ((lv file) &body forms) |
| 139 | + `(let ((<file> ,file)) |
| 140 | + (with-open-file (<in> <file>) |
| 141 | + (doing ((,lv (read-line <in> nil <in>))) |
| 142 | + ((eq ,lv <in>) <file>) |
| 143 | + ,@forms)))) |
| 144 | +||# |
0 commit comments