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