Fix bugs introduced in reader hash-map/{} change
[jackhill/mal.git] / cpp / step4_if_fn_do.cpp
CommitLineData
ad0fc149
ST
1#include "MAL.h"
2
3#include "Environment.h"
4#include "ReadLine.h"
5#include "Types.h"
6
7#include <iostream>
8#include <memory>
9
10malValuePtr READ(const String& input);
11String PRINT(malValuePtr ast);
12static void installFunctions(malEnvPtr env);
13
14static ReadLine s_readLine("~/.mal-history");
15
16int main(int argc, char* argv[])
17{
18 String prompt = "user> ";
19 String input;
20 malEnvPtr replEnv(new malEnv);
21 installCore(replEnv);
22 installFunctions(replEnv);
23 while (s_readLine.get(prompt, input)) {
24 String out;
25 try {
26 out = rep(input, replEnv);
27 }
28 catch (malEmptyInputException&) {
29 continue; // no output
30 }
31 catch (String& s) {
32 out = s;
33 };
34 std::cout << out << "\n";
35 }
36 return 0;
37}
38
39String rep(const String& input, malEnvPtr env)
40{
41 return PRINT(EVAL(READ(input), env));
42}
43
44malValuePtr READ(const String& input)
45{
46 return readStr(input);
47}
48
49malValuePtr EVAL(malValuePtr ast, malEnvPtr env)
50{
51 const malList* list = DYNAMIC_CAST(malList, ast);
52 if (!list || (list->count() == 0)) {
53 return ast->eval(env);
54 }
55
56 // From here on down we are evaluating a non-empty list.
57 // First handle the special forms.
58 if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {
59 String special = symbol->value();
60 int argCount = list->count() - 1;
61
62 if (special == "def!") {
63 checkArgsIs("def!", 2, argCount);
64 const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));
65 return env->set(id->value(), EVAL(list->item(2), env));
66 }
67
68 if (special == "do") {
69 checkArgsAtLeast("do", 1, argCount);
70
71 for (int i = 1; i < argCount; i++) {
72 EVAL(list->item(i), env);
73 }
74 return EVAL(list->item(argCount), env);
75 }
76
77 if (special == "fn*") {
78 checkArgsIs("fn*", 2, argCount);
79
80 const malSequence* bindings =
81 VALUE_CAST(malSequence, list->item(1));
82 StringVec params;
83 for (int i = 0; i < bindings->count(); i++) {
84 const malSymbol* sym =
85 VALUE_CAST(malSymbol, bindings->item(i));
86 params.push_back(sym->value());
87 }
88
89 return mal::lambda(params, list->item(2), env);
90 }
91
92 if (special == "if") {
93 checkArgsBetween("if", 2, 3, argCount);
94
95 bool isTrue = EVAL(list->item(1), env)->isTrue();
96 if (!isTrue && (argCount == 2)) {
97 return mal::nilValue();
98 }
99 return EVAL(list->item(isTrue ? 2 : 3), env);
100 }
101
102 if (special == "let*") {
103 checkArgsIs("let*", 2, argCount);
104 const malSequence* bindings =
105 VALUE_CAST(malSequence, list->item(1));
106 int count = checkArgsEven("let*", bindings->count());
107 malEnvPtr inner(new malEnv(env));
108 for (int i = 0; i < count; i += 2) {
109 const malSymbol* var =
110 VALUE_CAST(malSymbol, bindings->item(i));
111 inner->set(var->value(), EVAL(bindings->item(i+1), inner));
112 }
113 return EVAL(list->item(2), inner);
114 }
115 }
116
117 // Now we're left with the case of a regular list to be evaluated.
118 std::unique_ptr<malValueVec> items(list->evalItems(env));
119 malValuePtr op = items->at(0);
120 if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {
121 return EVAL(lambda->getBody(),
122 lambda->makeEnv(items->begin()+1, items->end()));
123 }
124 else {
125 return APPLY(op, items->begin()+1, items->end(), env);
126 }
127}
128
129String PRINT(malValuePtr ast)
130{
131 return ast->print(true);
132}
133
134malValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd,
135 malEnvPtr env)
136{
137 const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);
138 ASSERT(handler != NULL, "\"%s\" is not applicable", op->print(true).c_str());
139
140 return handler->apply(argsBegin, argsEnd, env);
141}
142
143static const char* malFunctionTable[] = {
144 "(def! list (fn* (& items) items))",
145 "(def! not (fn* (cond) (if cond false true)))",
146 "(def! >= (fn* (a b) (<= b a)))",
147 "(def! < (fn* (a b) (not (<= b a))))",
148 "(def! > (fn* (a b) (not (<= a b))))",
149};
150
151static void installFunctions(malEnvPtr env) {
f01c2683
ST
152 for (auto &function : malFunctionTable) {
153 rep(function, env);
ad0fc149
ST
154 }
155}
cb252845
ST
156
157// Added to keep the linker happy at step A
158malValuePtr readline(const String& prompt)
159{
160 String input;
161 if (s_readLine.get(prompt, input)) {
162 return mal::string(input);
163 }
164 return mal::nilValue();
165}
166