forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
step8_macros.hy
executable file
·178 lines (148 loc) · 5.44 KB
/
step8_macros.hy
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env hy
(import [hy.models [HyString :as Str HySymbol :as Sym]])
(import sys traceback)
(import [reader [read-str Blank]])
(import [printer [pr-str]])
(import [env [env-new env-get env-set env-find]])
(import core)
;; read
(defn READ [str]
(read-str str))
;; eval
(defn pair? [x]
(and (core.sequential? x) (> (len x) 0)))
(defn QUASIQUOTE [ast]
(if
(not (pair? ast))
(tuple [(Sym "quote") ast])
(= (Sym "unquote") (first ast))
(nth ast 1)
(and (pair? (first ast))
(= (Sym "splice-unquote") (first (first ast))))
(tuple [(Sym "concat") (nth (first ast) 1) (QUASIQUOTE (tuple (rest ast)))])
True
(tuple [(Sym "cons") (QUASIQUOTE (first ast)) (QUASIQUOTE (tuple (rest ast)))])))
(defn macro? [ast env]
(when (and (coll? ast)
(symbol? (first ast))
(env-find env (first ast)))
(setv mac (env-get env (first ast)))
(and (hasattr mac "macro")
mac.macro)))
(defn macroexpand [ast env]
(while (macro? ast env)
(setv mac (env-get env (first ast))
ast (apply mac (tuple (rest ast)))))
ast)
(defn eval-ast [ast env]
;;(print "eval-ast:" ast (type ast))
(if
(symbol? ast) (env-get env ast)
(instance? dict ast) (dict (map (fn [k]
[(EVAL k env) (EVAL (get ast k) env)])
ast))
(instance? tuple ast) (tuple (map (fn [x] (EVAL x env)) ast))
(instance? list ast) (list (map (fn [x] (EVAL x env)) ast))
True ast))
(defn EVAL [ast env]
;;(print "EVAL:" ast (type ast) (instance? tuple ast))
(setv res None)
(while True
(setv res
(if (not (instance? tuple ast))
(eval-ast ast env)
;; apply list
(do
(setv ast (macroexpand ast env))
(if (not (instance? tuple ast))
(eval-ast ast env)
(do
(setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])
(if
(none? a0)
ast
(= (Sym "def!") a0)
(env-set env a1 (EVAL a2 env))
(= (Sym "let*") a0)
(do
(setv env (env-new env))
(for [[b e] (partition a1 2)]
(env-set env b (EVAL e env)))
(setv ast a2)
(continue)) ;; TCO
(= (Sym "quote") a0)
a1
(= (Sym "quasiquote") a0)
(do (setv ast (QUASIQUOTE a1)) (continue)) ;; TCO
(= (Sym "defmacro!") a0)
(do (setv func (EVAL a2 env)
func.macro True)
(env-set env a1 func))
(= (Sym "macroexpand") a0)
(macroexpand a1 env)
(= (Sym "do") a0)
(do (eval-ast (list (butlast (rest ast))) env)
(setv ast (last ast))
(continue)) ;; TCO
(= (Sym "if") a0)
(do
(setv cond (EVAL a1 env))
(if (or (none? cond) (and (instance? bool cond)
(= cond False)))
(if (> (len ast) 2)
(do (setv ast (nth ast 3)) (continue)) ;; TCO
None)
(do (setv ast a2) (continue)))) ;; TCO
(= (Sym "fn*") a0)
(do
(setv func (fn [&rest args]
(EVAL a2 (env-new env a1 (or args []))))
func.ast a2
func.env env
func.params a1)
func)
;; apply
(do
(setv el (eval-ast ast env)
f (first el)
args (list (rest el)))
(if (hasattr f "ast")
(do (setv ast f.ast
env (env-new f.env f.params args))
(continue)) ;; TCO
(apply f args)))))))))
(break))
res)
;; print
(defn PRINT [exp]
(pr-str exp True))
;; repl
(def repl-env (env-new))
(defn REP [str]
(PRINT (EVAL (READ str) repl-env)))
;; core.hy: defined using Hy
(for [k core.ns]
(env-set repl-env (Sym k) (get core.ns k)))
(env-set repl-env (Sym "eval") (fn [ast] (EVAL ast repl-env)))
(env-set repl-env (Sym "*ARGV*") (, ))
;; core.mal: defined using the language itself
(REP "(def! not (fn* [a] (if a false true)))")
(REP "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
(REP "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
(REP "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
(defmain [&rest args]
(if (>= (len args) 2)
(do
(env-set repl-env (Sym "*ARGV*") (tuple (map Str (rest (rest args)))))
(REP (+ "(load-file \"" (get args 1) "\")")))
(do
(while True
(try
(do (setv line (raw_input "user> "))
(if (= "" line) (continue))
(print (REP line)))
(except [EOFError] (break))
(except [Blank])
(except []
(print (.join "" (apply traceback.format_exception
(.exc_info sys))))))))))