Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/guicho271828/recursive-macroexpansion
Provides another `macroexpand`
https://github.com/guicho271828/recursive-macroexpansion
Last synced: 29 days ago
JSON representation
Provides another `macroexpand`
- Host: GitHub
- URL: https://github.com/guicho271828/recursive-macroexpansion
- Owner: guicho271828
- Created: 2014-02-05T04:31:39.000Z (almost 11 years ago)
- Default Branch: master
- Last Pushed: 2015-03-07T04:18:47.000Z (almost 10 years ago)
- Last Synced: 2024-10-15T14:10:59.490Z (3 months ago)
- Language: Common Lisp
- Size: 188 KB
- Stars: 8
- Watchers: 4
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.org
Awesome Lists containing this project
README
* Recursive-Macroexpansion
Yet another macro-expansion system
which provides an easier compile-time error handling.Recursive-Macroexpansion has the completely different expansion algorithm than
Common Lisp's macro expansion, which is based on =macroexpand-1= and =macroexpand=.# *BIG NOTE*: This is my *FIRST* library written in controversial =CL21=. Thanks to
# [[https://github.com/fukamachi][Fukamachi]] !
#
# *BIG NOTE2*: This should be clearly noted for the people not familier with
# CL21. This library is written in CL21 but /using this library does not require
# doing so/. If you use RMACROEXPAND, it is just a normal common lisp
# function. Symbols are exported just as in Common Lisp.
#
# While dependency to CL21 exists, once installed, CL21 does not interfere
# you, nor does it break your environment (such as overwriting the readtable).
# CL21 introduces many custom readmacros, but one
# notable point in CL21 is its *package local readtable* (implemented with
# =named-readtable=). AFAIK it is safer than the previous readtable manipulation
# method. In CL21, a new =in-package= is introduced so that it automatically loads the
# =named-readtable= with the same name as that of the package.* API
Four functions/macros are exported.
| | analogous to |
|---------------------------------+---------------------------------------------------------|
| defrmacro | defmacro |
| rmacroexpand | macroexpand , macroexpand-dammit , macroexpand-all etc. |
| recursive-macro-function | macro-function |
| (setf recursive-macro-function) | (setf macro-function) |* Examples
Ordinary macro expands one layer only. Further expansions are implicitly done by
the other call to macroexpand-1, which users cannot control.
Therefore, we are not able to trap the conditions signalled
in the expansion of the subforms.For example, the combination below is a rather stupid example:
#+BEGIN_SRC lisp
(defun stupid-error-handler (c)
(format *standard-output* "ignored a stupid error")
(continue c))(defmacro with-let (var-form value-form &body body)
(handler-bind ((error #'stupid-error-handler))
`(let ((,var-form ,value-form))
,@body)))(defmacro limited-progn (&body body)
;; stupid assertion
(unless (< (length body) 3)
(cerror "Ignore!" "the body of limited-progn should be of length less than 3 !"))
`(progn ,@body))(is (tree-equal
'(let ((x 1)) (print x))
(macroexpand '(with-let x 1 (print x)))));; expansion works normally if nothing is signalled
(finishes
(with-let x 1 (limited-progn 1 2)));; the error is not trapped, so it is visible from the outside
(signals error
(with-let x 1 (limited-progn 1 2 3 4)))
#+END_SRCOn the other hand, with =defrmacro=, we can control the further expansion. What's
special here is =rmacroexpand=.#+BEGIN_SRC lisp
(defrmacro with-let (var-form value-form &body body)
(handler-bind ((error #'stupid-error-handler))
(rmacroexpand
`(let ((,var-form ,value-form))
,@body))))(is (tree-equal
'(let ((x 1)) (print x))
(rmacroexpand '(with-let x 1 (print x)))));; expansion works normally
(finishes
(rmacroexpand
'(with-let x 1 (limited-progn 1 2))));; the error is handled in compile time and ignored.
(finishes
(rmacroexpand
'(with-let x 1 (limited-progn 1 2 3 4))))
)
#+END_SRC** Considering the surrounding environment
*Note* that the example above IGNORES the surrounding environment
in the expansion of the subforms,
so the subform expansion does not consider the declared information.#+BEGIN_SRC lisp
(defmacro env-checker (&environment env)
`(quote ,(multiple-value-list (variable-information 'x env))))(test recursive-macroexpansion-without-environment
(is (tree-equal
'(LET ((X 1))
(DECLARE (TYPE INTEGER X))
(LET ((Y X))
'(NIL NIL NIL)))
(rmacroexpand
'(let ((x 1))
(declare (type integer x))
(with-let y x
(env-checker)))))))
#+END_SRCIn order to pass the outer environment to the subform expander,
call =rmacroexpand= with optional second argument =env=.#+BEGIN_SRC lisp
(defrmacro with-let-env (&environment env var-form value-form &body body)
(handler-bind ((error #'stupid-error-handler))
(rmacroexpand
`(let ((,var-form ,value-form))
,@body)
env)))(test recursive-macroexpansion-without-environment
(is (tree-equal
'(LET ((X 1))
(DECLARE (TYPE INTEGER X))
(LET ((Y X))
'(:LEXICAL T ((TYPE . INTEGER)))))
(rmacroexpand
'(let ((x 1))
(declare (type integer x))
(with-let-env y x
(env-checker)))))))
#+END_SRC** Dependencies
This library is at least tested on implementation listed below:
+ SBCL 1.1.14 on X86-64 Linux 3.2.0-58-generic (author's environment)
Also, it depends on the following libraries:
+ CL21 :: Common Lisp in the 21st Century. by Fukamachi
** Author
+ Masataro Asai
* Copyright
Copyright (c) 2014 Masataro Asai