3 #include "Environment.h"
10 malValuePtr
READ(const String
& input
);
11 String
PRINT(malValuePtr ast
);
12 static void installFunctions(malEnvPtr env
);
14 static void makeArgv(malEnvPtr env
, int argc
, char* argv
[]);
15 static void safeRep(const String
& input
, malEnvPtr env
);
16 static malValuePtr
quasiquote(malValuePtr obj
);
17 static malValuePtr
macroExpand(malValuePtr obj
, malEnvPtr env
);
18 static void installMacros(malEnvPtr env
);
20 static ReadLine
s_readLine("~/.mal-history");
22 static malEnvPtr
replEnv(new malEnv
);
24 int main(int argc
, char* argv
[])
26 String prompt
= "user> ";
29 installFunctions(replEnv
);
30 installMacros(replEnv
);
31 makeArgv(replEnv
, argc
- 2, argv
+ 2);
33 String filename
= escape(argv
[1]);
34 safeRep(STRF("(load-file %s)", filename
.c_str()), replEnv
);
37 while (s_readLine
.get(prompt
, input
)) {
38 safeRep(input
, replEnv
);
43 static void safeRep(const String
& input
, malEnvPtr env
)
47 out
= rep(input
, env
);
49 catch (malEmptyInputException
&) {
55 std::cout
<< out
<< "\n";
58 static void makeArgv(malEnvPtr env
, int argc
, char* argv
[])
60 malValueVec
* args
= new malValueVec();
61 for (int i
= 0; i
< argc
; i
++) {
62 args
->push_back(mal::string(argv
[i
]));
64 env
->set("*ARGV*", mal::list(args
));
67 String
rep(const String
& input
, malEnvPtr env
)
69 return PRINT(EVAL(READ(input
), env
));
72 malValuePtr
READ(const String
& input
)
74 return readStr(input
);
77 malValuePtr
EVAL(malValuePtr ast
, malEnvPtr env
)
83 const malList
* list
= DYNAMIC_CAST(malList
, ast
);
84 if (!list
|| (list
->count() == 0)) {
85 return ast
->eval(env
);
88 ast
= macroExpand(ast
, env
);
89 list
= DYNAMIC_CAST(malList
, ast
);
90 if (!list
|| (list
->count() == 0)) {
91 return ast
->eval(env
);
94 // From here on down we are evaluating a non-empty list.
95 // First handle the special forms.
96 if (const malSymbol
* symbol
= DYNAMIC_CAST(malSymbol
, list
->item(0))) {
97 String special
= symbol
->value();
98 int argCount
= list
->count() - 1;
100 if (special
== "def!") {
101 checkArgsIs("def!", 2, argCount
);
102 const malSymbol
* id
= VALUE_CAST(malSymbol
, list
->item(1));
103 return env
->set(id
->value(), EVAL(list
->item(2), env
));
106 if (special
== "defmacro!") {
107 checkArgsIs("defmacro!", 2, argCount
);
109 const malSymbol
* id
= VALUE_CAST(malSymbol
, list
->item(1));
110 malValuePtr body
= EVAL(list
->item(2), env
);
111 const malLambda
* lambda
= VALUE_CAST(malLambda
, body
);
112 return env
->set(id
->value(), mal::macro(*lambda
));
115 if (special
== "do") {
116 checkArgsAtLeast("do", 1, argCount
);
118 for (int i
= 1; i
< argCount
; i
++) {
119 EVAL(list
->item(i
), env
);
121 ast
= list
->item(argCount
);
125 if (special
== "fn*") {
126 checkArgsIs("fn*", 2, argCount
);
128 const malSequence
* bindings
=
129 VALUE_CAST(malSequence
, list
->item(1));
131 for (int i
= 0; i
< bindings
->count(); i
++) {
132 const malSymbol
* sym
=
133 VALUE_CAST(malSymbol
, bindings
->item(i
));
134 params
.push_back(sym
->value());
137 return mal::lambda(params
, list
->item(2), env
);
140 if (special
== "if") {
141 checkArgsBetween("if", 2, 3, argCount
);
143 bool isTrue
= EVAL(list
->item(1), env
)->isTrue();
144 if (!isTrue
&& (argCount
== 2)) {
145 return mal::nilValue();
147 ast
= list
->item(isTrue
? 2 : 3);
151 if (special
== "let*") {
152 checkArgsIs("let*", 2, argCount
);
153 const malSequence
* bindings
=
154 VALUE_CAST(malSequence
, list
->item(1));
155 int count
= checkArgsEven("let*", bindings
->count());
156 malEnvPtr
inner(new malEnv(env
));
157 for (int i
= 0; i
< count
; i
+= 2) {
158 const malSymbol
* var
=
159 VALUE_CAST(malSymbol
, bindings
->item(i
));
160 inner
->set(var
->value(), EVAL(bindings
->item(i
+1), inner
));
167 if (special
== "macroexpand") {
168 checkArgsIs("macroexpand", 1, argCount
);
169 return macroExpand(list
->item(1), env
);
172 if (special
== "quasiquote") {
173 checkArgsIs("quasiquote", 1, argCount
);
174 ast
= quasiquote(list
->item(1));
178 if (special
== "quote") {
179 checkArgsIs("quote", 1, argCount
);
180 return list
->item(1);
184 // Now we're left with the case of a regular list to be evaluated.
185 std::unique_ptr
<malValueVec
> items(list
->evalItems(env
));
186 malValuePtr op
= items
->at(0);
187 if (const malLambda
* lambda
= DYNAMIC_CAST(malLambda
, op
)) {
188 ast
= lambda
->getBody();
189 env
= lambda
->makeEnv(items
->begin()+1, items
->end());
193 return APPLY(op
, items
->begin()+1, items
->end());
198 String
PRINT(malValuePtr ast
)
200 return ast
->print(true);
203 malValuePtr
APPLY(malValuePtr op
, malValueIter argsBegin
, malValueIter argsEnd
)
205 const malApplicable
* handler
= DYNAMIC_CAST(malApplicable
, op
);
206 MAL_CHECK(handler
!= NULL
,
207 "\"%s\" is not applicable", op
->print(true).c_str());
209 return handler
->apply(argsBegin
, argsEnd
);
212 static bool isSymbol(malValuePtr obj
, const String
& text
)
214 const malSymbol
* sym
= DYNAMIC_CAST(malSymbol
, obj
);
215 return sym
&& (sym
->value() == text
);
218 static const malSequence
* isPair(malValuePtr obj
)
220 const malSequence
* list
= DYNAMIC_CAST(malSequence
, obj
);
221 return list
&& !list
->isEmpty() ? list
: NULL
;
224 static malValuePtr
quasiquote(malValuePtr obj
)
226 const malSequence
* seq
= isPair(obj
);
228 return mal::list(mal::symbol("quote"), obj
);
231 if (isSymbol(seq
->item(0), "unquote")) {
232 // (qq (uq form)) -> form
233 checkArgsIs("unquote", 1, seq
->count() - 1);
237 const malSequence
* innerSeq
= isPair(seq
->item(0));
238 if (innerSeq
&& isSymbol(innerSeq
->item(0), "splice-unquote")) {
239 checkArgsIs("splice-unquote", 1, innerSeq
->count() - 1);
240 // (qq (sq '(a b c))) -> a b c
242 mal::symbol("concat"),
244 quasiquote(seq
->rest())
248 // (qq (a b c)) -> (list (qq a) (qq b) (qq c))
249 // (qq xs ) -> (cons (qq (car xs)) (qq (cdr xs)))
252 quasiquote(seq
->first()),
253 quasiquote(seq
->rest())
258 static const malLambda
* isMacroApplication(malValuePtr obj
, malEnvPtr env
)
260 if (const malSequence
* seq
= isPair(obj
)) {
261 if (malSymbol
* sym
= DYNAMIC_CAST(malSymbol
, seq
->first())) {
262 if (malEnvPtr symEnv
= env
->find(sym
->value())) {
263 malValuePtr value
= sym
->eval(symEnv
);
264 if (malLambda
* lambda
= DYNAMIC_CAST(malLambda
, value
)) {
265 return lambda
->isMacro() ? lambda
: NULL
;
273 static malValuePtr
macroExpand(malValuePtr obj
, malEnvPtr env
)
275 while (const malLambda
* macro
= isMacroApplication(obj
, env
)) {
276 const malSequence
* seq
= STATIC_CAST(malSequence
, obj
);
277 obj
= macro
->apply(seq
->begin() + 1, seq
->end());
282 static const char* macroTable
[] = {
283 "(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)))))))",
284 "(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))))))))",
287 static void installMacros(malEnvPtr env
)
289 for (auto ¯o
: macroTable
) {
294 static const char* malFunctionTable
[] = {
295 "(def! list (fn* (& items) items))",
296 "(def! not (fn* (cond) (if cond false true)))",
297 "(def! >= (fn* (a b) (<= b a)))",
298 "(def! < (fn* (a b) (not (<= b a))))",
299 "(def! > (fn* (a b) (not (<= a b))))",
300 "(def! load-file (fn* (filename) \
301 (eval (read-string (str \"(do \" (slurp filename) \")\")))))",
304 static void installFunctions(malEnvPtr env
) {
305 for (auto &function
: malFunctionTable
) {
310 // Added to keep the linker happy at step A
311 malValuePtr
readline(const String
& prompt
)
314 if (s_readLine
.get(prompt
, input
)) {
315 return mal::string(input
);
317 return mal::nilValue();