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 int main(int argc
, char* argv
[])
24 String prompt
= "user> ";
26 malEnvPtr
replEnv(new malEnv
);
28 installFunctions(replEnv
);
29 installMacros(replEnv
);
30 makeArgv(replEnv
, argc
- 2, argv
+ 2);
32 String filename
= escape(argv
[1]);
33 safeRep(STRF("(load-file %s)", filename
.c_str()), replEnv
);
36 while (s_readLine
.get(prompt
, input
)) {
37 safeRep(input
, replEnv
);
42 static void safeRep(const String
& input
, malEnvPtr env
)
46 out
= rep(input
, env
);
48 catch (malEmptyInputException
&) {
54 std::cout
<< out
<< "\n";
57 static void makeArgv(malEnvPtr env
, int argc
, char* argv
[])
59 malValueVec
* args
= new malValueVec();
60 for (int i
= 0; i
< argc
; i
++) {
61 args
->push_back(mal::string(argv
[i
]));
63 env
->set("*ARGV*", mal::list(args
));
66 String
rep(const String
& input
, malEnvPtr env
)
68 return PRINT(EVAL(READ(input
), env
));
71 malValuePtr
READ(const String
& input
)
73 return readStr(input
);
76 malValuePtr
EVAL(malValuePtr ast
, malEnvPtr env
)
79 const malList
* list
= DYNAMIC_CAST(malList
, ast
);
80 if (!list
|| (list
->count() == 0)) {
81 return ast
->eval(env
);
84 ast
= macroExpand(ast
, env
);
85 list
= DYNAMIC_CAST(malList
, ast
);
86 if (!list
|| (list
->count() == 0)) {
87 return ast
->eval(env
);
90 // From here on down we are evaluating a non-empty list.
91 // First handle the special forms.
92 if (const malSymbol
* symbol
= DYNAMIC_CAST(malSymbol
, list
->item(0))) {
93 String special
= symbol
->value();
94 int argCount
= list
->count() - 1;
96 if (special
== "def!") {
97 checkArgsIs("def!", 2, argCount
);
98 const malSymbol
* id
= VALUE_CAST(malSymbol
, list
->item(1));
99 return env
->set(id
->value(), EVAL(list
->item(2), env
));
102 if (special
== "defmacro!") {
103 checkArgsIs("defmacro!", 2, argCount
);
105 const malSymbol
* id
= VALUE_CAST(malSymbol
, list
->item(1));
106 malValuePtr body
= EVAL(list
->item(2), env
);
107 const malLambda
* lambda
= VALUE_CAST(malLambda
, body
);
108 return env
->set(id
->value(), mal::macro(*lambda
));
111 if (special
== "do") {
112 checkArgsAtLeast("do", 1, argCount
);
114 for (int i
= 1; i
< argCount
; i
++) {
115 EVAL(list
->item(i
), env
);
117 ast
= list
->item(argCount
);
121 if (special
== "fn*") {
122 checkArgsIs("fn*", 2, argCount
);
124 const malSequence
* bindings
=
125 VALUE_CAST(malSequence
, list
->item(1));
127 for (int i
= 0; i
< bindings
->count(); i
++) {
128 const malSymbol
* sym
=
129 VALUE_CAST(malSymbol
, bindings
->item(i
));
130 params
.push_back(sym
->value());
133 return mal::lambda(params
, list
->item(2), env
);
136 if (special
== "if") {
137 checkArgsBetween("if", 2, 3, argCount
);
139 bool isTrue
= EVAL(list
->item(1), env
)->isTrue();
140 if (!isTrue
&& (argCount
== 2)) {
141 return mal::nilValue();
143 ast
= list
->item(isTrue
? 2 : 3);
147 if (special
== "let*") {
148 checkArgsIs("let*", 2, argCount
);
149 const malSequence
* bindings
=
150 VALUE_CAST(malSequence
, list
->item(1));
151 int count
= checkArgsEven("let*", bindings
->count());
152 malEnvPtr
inner(new malEnv(env
));
153 for (int i
= 0; i
< count
; i
+= 2) {
154 const malSymbol
* var
=
155 VALUE_CAST(malSymbol
, bindings
->item(i
));
156 inner
->set(var
->value(), EVAL(bindings
->item(i
+1), inner
));
163 if (special
== "macroexpand") {
164 checkArgsIs("macroexpand", 1, argCount
);
165 return macroExpand(list
->item(1), env
);
168 if (special
== "quasiquote") {
169 checkArgsIs("quasiquote", 1, argCount
);
170 ast
= quasiquote(list
->item(1));
174 if (special
== "quote") {
175 checkArgsIs("quote", 1, argCount
);
176 return list
->item(1);
180 // Now we're left with the case of a regular list to be evaluated.
181 std::unique_ptr
<malValueVec
> items(list
->evalItems(env
));
182 malValuePtr op
= items
->at(0);
183 if (const malLambda
* lambda
= DYNAMIC_CAST(malLambda
, op
)) {
184 ast
= lambda
->getBody();
185 env
= lambda
->makeEnv(items
->begin()+1, items
->end());
189 return APPLY(op
, items
->begin()+1, items
->end(), env
);
194 String
PRINT(malValuePtr ast
)
196 return ast
->print(true);
199 malValuePtr
APPLY(malValuePtr op
, malValueIter argsBegin
, malValueIter argsEnd
,
202 const malApplicable
* handler
= DYNAMIC_CAST(malApplicable
, op
);
203 ASSERT(handler
!= NULL
, "\"%s\" is not applicable", op
->print(true).c_str());
205 return handler
->apply(argsBegin
, argsEnd
, env
);
208 static bool isSymbol(malValuePtr obj
, const String
& text
)
210 const malSymbol
* sym
= DYNAMIC_CAST(malSymbol
, obj
);
211 return sym
&& (sym
->value() == text
);
214 static const malSequence
* isPair(malValuePtr obj
)
216 const malSequence
* list
= DYNAMIC_CAST(malSequence
, obj
);
217 return list
&& !list
->isEmpty() ? list
: NULL
;
220 static malValuePtr
quasiquote(malValuePtr obj
)
222 const malSequence
* seq
= isPair(obj
);
224 return mal::list(mal::symbol("quote"), obj
);
227 if (isSymbol(seq
->item(0), "unquote")) {
228 // (qq (uq form)) -> form
229 checkArgsIs("unquote", 1, seq
->count() - 1);
233 const malSequence
* innerSeq
= isPair(seq
->item(0));
234 if (innerSeq
&& isSymbol(innerSeq
->item(0), "splice-unquote")) {
235 checkArgsIs("splice-unquote", 1, innerSeq
->count() - 1);
236 // (qq (sq '(a b c))) -> a b c
238 mal::symbol("concat"),
240 quasiquote(seq
->rest())
244 // (qq (a b c)) -> (list (qq a) (qq b) (qq c))
245 // (qq xs ) -> (cons (qq (car xs)) (qq (cdr xs)))
248 quasiquote(seq
->first()),
249 quasiquote(seq
->rest())
254 static const malLambda
* isMacroApplication(malValuePtr obj
, malEnvPtr env
)
256 if (const malSequence
* seq
= isPair(obj
)) {
257 if (malSymbol
* sym
= DYNAMIC_CAST(malSymbol
, seq
->first())) {
258 if (malEnvPtr symEnv
= env
->find(sym
->value())) {
259 malValuePtr value
= sym
->eval(symEnv
);
260 if (malLambda
* lambda
= DYNAMIC_CAST(malLambda
, value
)) {
261 return lambda
->isMacro() ? lambda
: NULL
;
269 static malValuePtr
macroExpand(malValuePtr obj
, malEnvPtr env
)
271 while (const malLambda
* macro
= isMacroApplication(obj
, env
)) {
272 const malSequence
* seq
= STATIC_CAST(malSequence
, obj
);
273 obj
= macro
->apply(seq
->begin() + 1, seq
->end(), env
);
278 static const char* macroTable
[] = {
279 "(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)))))))",
280 "(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))))))))",
283 static void installMacros(malEnvPtr env
)
285 for (auto ¯o
: macroTable
) {
290 static const char* malFunctionTable
[] = {
291 "(def! list (fn* (& items) items))",
292 "(def! not (fn* (cond) (if cond false true)))",
293 "(def! >= (fn* (a b) (<= b a)))",
294 "(def! < (fn* (a b) (not (<= b a))))",
295 "(def! > (fn* (a b) (not (<= a b))))",
296 "(def! load-file (fn* (filename) \
297 (eval (read-string (str \"(do \" (slurp filename) \")\")))))",
300 static void installFunctions(malEnvPtr env
) {
301 for (auto &function
: malFunctionTable
) {
306 // Added to keep the linker happy at step A
307 malValuePtr
readline(const String
& prompt
)
310 if (s_readLine
.get(prompt
, input
)) {
311 return mal::string(input
);
313 return mal::nilValue();