-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathwalk.lisp
120 lines (112 loc) · 3.71 KB
/
walk.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
(in-package #:cloture)
(in-readtable clojure-shortcut)
(defconst special-forms
'#_(
&
catch
def
def-
do
finally
fn
if
let
letfn
loop
monitor-enter
monitor-exit
new
quote
recur
reify
set!
throw
try
var
|.|))
(defun special-form? (form)
(and (consp form)
(symbolp (car form))
(member (car form) special-forms)))
(defun splice-splicing-conditional (obj)
(flet ((splice? (elt)
(and (listp elt)
(eql (car elt) %splicing-conditional))))
(if (listp obj)
(if (member-if #'splice? obj)
(mappend (lambda (elt)
(if (splice? elt)
(cdr elt)
(list elt)))
obj)
obj)
obj)))
(defun declojurize (tree)
"Replace literal objects (outside quasiquotes) with constructors.
Code is declojurized (for compilation by Lisp) when it is returned
from Clojure macros."
(map-tree (named-lambda rec (tree)
(match tree
((type seq)
`([]
,@(mapcar (op (map-tree #'rec _))
(convert 'list tree))))
((type set)
`(|#{}|
,@(mapcar (op (map-tree #'rec _))
(convert 'list tree))))
((type map)
`(|{}|
,@(mapcar (op (map-tree #'rec _))
(map->list tree))))
(otherwise tree)))
tree))
(defun clojurize (tree)
"Replace calls to constructors with literal objects.
Also convert the symbols for true, false, and nil to unit types.
Code is \"clojurized\" before being passed to Clojure macros, and when
returned by Clojure's quote."
(map-tree (named-lambda rec (tree)
(let ((tree (splice-splicing-conditional tree)))
(match tree
((list* '[] elts)
(let ((elts (mapcar (op (map-tree #'rec _)) elts)))
(convert 'seq elts)))
((list* '|#{}| elts)
(let ((elts (mapcar (op (map-tree #'rec _)) elts)))
(convert 'set elts)))
((list* '{} elts)
(let ((elts (mapcar (op (map-tree #'rec _)) elts)))
(list->map elts)))
('|clojure.core|:|true| |clojure.core|:|true|)
('|clojure.core|:|false| |clojure.core|:|false|)
('|clojure.core|:|nil| |clojure.core|:|nil|)
((list* _ (and _ (not (type list))))
(error "Improper list in Clojure tree."))
(otherwise tree))))
tree))
(defun clojure-macroexpand-1 (form &optional env)
"Like `macroexpand-1', but bottoms out if it hits a Clojure special form or a call that is not in Clojure."
(if (special-form? form)
(values form nil)
(multiple-value-bind (exp exp?)
(macroexpand-1 form env)
(if (not exp?) form
(match exp
((list* (and sym (type symbol)) _)
(if (clojure-symbol? sym)
(values exp t)
(values form nil)))
((and sym (type symbol))
(if (clojure-symbol? sym)
(values exp t)
(values form nil)))
(otherwise (values exp exp?)))))))
(defun clojure-macroexpand (form &optional env)
(nlet lp ((form form)
(expanded? nil))
(multiple-value-bind (exp exp?)
(clojure-macroexpand-1 form env)
(if exp?
(lp exp t)
(values form expanded?)))))