Plaster
New
List
Login
common-lisp
default
anonymous
2023.10.13 15:44:03
;;;; Compile-time LET (defun ensure-list (thing) (if (listp thing) thing (list thing))) (defmacro tmlet ((name lambda-list &body body) &body mlet-body &environment env) (let ((transfer (gensym "MLET-TRANSFER"))) `(macrolet ((,transfer (&rest args &environment env) (funcall ,(macro-function name env) `(,',name ,@args) env)) (,name ,lambda-list (let ((,name ',transfer)) ,@body))) ,@mlet-body))) (defmacro ct (var) `(error "~A doesn't have compile time value" ',var)) (defmacro ctlet (bindings &body body) (let ((%vals (loop for (var value) in (mapcar #'ensure-list bindings) collect (list var (gensym "%VAL") value)))) `(macrolet (,@(loop for (var %val value) in %vals collect `(,%val () ,value))) (%ctlet (,@(loop for (var %val value) in %vals collect `(,var ,%val))) ,@body)))) (defmacro %ctlet (bindings &body body &environment env) `(tmlet (ct (var) (case var ,@(loop for (var %val) in bindings collect `(,var ',(funcall (macro-function %val env) `(,%val) env))) (t `(,ct ,var)))) ,@body)) #| Usage example: Consider the following: (let ((x 10)) (macrolet ((foo () x)) (foo))) This will cause a compiler error: ; The variable X is unbound. ; It is a local variable not available at compile-time. You can use CTLET instead! To access compile-time variable use a getter CT. (Note that compile-time variables are not setfable, but you can modify their value instead.) (ctlet ((x 10)) (macrolet ((foo () (ct x))) (foo))) ; => 10 Another example. Consider two functions: (defun foo () (ctlet ((x (print :once!))) (list (ct x) (ct x)))) (defun bar () (macrolet ((x () (print :once?))) (list (x) (x)))) The first one will print `:ONCE!` only once per macroexpansion of CTLET form, but the second one will print `:ONCE?` at least twice - once per macroexpansion of (x). #|
Raw
Annotate
Repaste
Edit