-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.lisp
144 lines (138 loc) · 4.3 KB
/
main.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(in-package #:evaluated-flet)
(defun %function-name-core (name)
(etypecase name
((cons (eql setf)
(cons symbol null))
(second name))
(symbol name)))
(defun %gensymed-function-name (name)
(gensym (string (%function-name-core name))))
;; PARSE-BODY from Alexandria.
(defun parse-body (body &key documentation whole)
"Parses BODY into (values remaining-forms declarations doc-string).
Documentation strings are recognized only if DOCUMENTATION is true.
Syntax errors in body are signalled and WHOLE is used in the signal
arguments when given."
(let ((doc nil)
(decls nil)
(current nil))
(tagbody
:declarations
(setf current (car body))
(when (and documentation (stringp current) (cdr body))
(if doc
(error "Too many documentation strings in ~S." (or whole body))
(setf doc (pop body)))
(go :declarations))
(when (and (listp current) (eql (first current) 'declare))
(push (pop body) decls)
(go :declarations)))
(values body (nreverse decls) doc)))
(defun extract-function-declarations (declarations &key name)
(let ((core (and name
(%function-name-core name)))
target-declarations
other-declarations)
(labels ((correct-name-p (name)
(or (not core)
(eq name core)))
(split (function list key)
(let (a b)
(dolist (element list
(values (nreverse a)
(nreverse b)))
(if (funcall function (funcall key element))
(push element a)
(push element b)))))
(bof (wrapper function list &key (key #'identity))
(multiple-value-bind (targets others)
(split function list key)
(when targets
(push (funcall wrapper targets) target-declarations))
(when others
(push (funcall wrapper others) other-declarations)))))
(dolist (declaration declarations
(values (nreverse target-declarations)
(nreverse other-declarations)))
(case (first declaration)
(ftype
(bof (lambda (names)
`(ftype ,(second declaration) ,@names))
#'correct-name-p
(cddr declaration)
:key #'%function-name-core))
((inline notinline)
(bof (lambda (names)
`(,(first declaration) ,@names))
#'correct-name-p
(cdr declaration)
:key #'%function-name-core))
((ignore ignorable dynamic-extent)
(bof (lambda (names)
`(,(first declaration) ,@names))
(lambda (name)
(and (typep name '(cons (eql function) (cons t null)))
(correct-name-p
(%function-name-core (second name)))))
(cdr declaration)))
(t (push declaration other-declarations)))))))
(defun eflet*-undefined (name)
(lambda (&rest args)
(error "Tried to call undefined ~S function ~S with args ~S"
'eflet* name args)))
(defmacro eflet* (definitions &body body)
(multiple-value-bind (body declarations) (parse-body body)
(setf declarations
(mapcan (lambda (declaration)
(copy-seq (cdr declaration)))
declarations))
(let ((defcount (length definitions)))
(if definitions
(flet ((extract-declarations (function-name)
(multiple-value-bind (targets others)
(extract-function-declarations
declarations :name function-name)
(setf declarations others)
targets))
(decls (declarations)
(when declarations
`((declare ,@declarations)))))
(first
(reduce
(lambda (definition body)
(destructuring-bind (name form) definition
(let* ((extracted-declarations
(extract-declarations name))
(remaining-declarations
(when (zerop (decf defcount))
declarations)))
(list
(if (typep form '(cons (eql lambda)))
`(flet ((,name ,@(cdr form)))
,@(decls (append
remaining-declarations
extracted-declarations))
,@body)
(let ((function-var
(%gensymed-function-name name)))
`(let ((,function-var
(or ,form
(eflet*-undefined ',name))))
,@(decls
(append
`((type (or function
symbol
null
(cons (eql setf)
(cons symbol null)))
,function-var))
remaining-declarations))
(flet ((,name (&rest args)
(apply ,function-var args)))
,@(decls extracted-declarations)
,@body))))))))
definitions
:from-end t
:initial-value body)))
`(locally ,@declarations
,@body)))))