add Dart implementation
[jackhill/mal.git] / dart / step4_if_fn_do.dart
CommitLineData
3934e3f8
HT
1import 'dart:io';
2
3import 'core.dart';
4import 'env.dart';
5import 'printer.dart' as printer;
6import 'reader.dart' as reader;
7import 'types.dart';
8
9final Env replEnv = new Env();
10
11void setupEnv() {
12 ns.forEach((sym, fun) => replEnv.set(sym, fun));
13
14 rep('(def! not (fn* (a) (if a false true)))');
15}
16
17MalType READ(String x) => reader.read_str(x);
18
19MalType eval_ast(MalType ast, Env env) {
20 if (ast is MalSymbol) {
21 var result = env.get(ast);
22 if (result == null) {
23 throw new NotFoundException(ast.value);
24 }
25 return result;
26 } else if (ast is MalList) {
27 return new MalList(ast.elements.map((x) => EVAL(x, env)).toList());
28 } else if (ast is MalVector) {
29 return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());
30 } else if (ast is MalHashMap) {
31 var newMap = new Map<MalSymbol, MalType>.from(ast.value);
32 for (var key in newMap.keys) {
33 newMap[key] = EVAL(newMap[key], env);
34 }
35 return new MalHashMap(newMap);
36 } else {
37 return ast;
38 }
39}
40
41MalType EVAL(MalType ast, Env env) {
42 if (ast is! MalList) {
43 return eval_ast(ast, env);
44 } else {
45 if ((ast as MalList).elements.isEmpty) {
46 return ast;
47 } else {
48 var list = ast as MalList;
49 if (list.elements.first is MalSymbol) {
50 var symbol = list.elements.first as MalSymbol;
51 var args = list.elements.sublist(1);
52 if (symbol.value == "def!") {
53 MalSymbol key = args.first;
54 MalType value = EVAL(args[1], env);
55 env.set(key, value);
56 return value;
57 } else if (symbol.value == "let*") {
58 // TODO(het): If elements.length is not even, give helpful error
59 Iterable<List<MalType>> pairs(List<MalType> elements) sync* {
60 for (var i = 0; i < elements.length; i += 2) {
61 yield [elements[i], elements[i + 1]];
62 }
63 }
64
65 var newEnv = new Env(env);
66 MalIterable bindings = args.first;
67 for (var pair in pairs(bindings.elements)) {
68 MalSymbol key = pair[0];
69 MalType value = EVAL(pair[1], newEnv);
70 newEnv.set(key, value);
71 }
72 return EVAL(args[1], newEnv);
73 } else if (symbol.value == "do") {
74 return args.map((e) => EVAL(e, env)).toList().last;
75 } else if (symbol.value == "if") {
76 var condition = EVAL(args[0], env);
77 if (condition is MalNil ||
78 condition is MalBool && condition.value == false) {
79 // False side of branch
80 if (args.length < 3) {
81 return new MalNil();
82 }
83 return EVAL(args[2], env);
84 } else {
85 // True side of branch
86 return EVAL(args[1], env);
87 }
88 } else if (symbol.value == "fn*") {
89 var params = (args[0] as MalIterable)
90 .elements
91 .map((e) => e as MalSymbol)
92 .toList();
93 return new MalClosure(
94 params,
95 args[1],
96 env,
97 (List<MalType> funcArgs) =>
98 EVAL(args[1], new Env(env, params, funcArgs)));
99 }
100 }
101 var newAst = eval_ast(ast, env) as MalList;
102 var f = newAst.elements.first;
103 if (f is MalCallable) {
104 return f.call(newAst.elements.sublist(1));
105 } else {
106 throw 'bad!';
107 }
108 }
109 }
110}
111
112String PRINT(MalType x) => printer.pr_str(x);
113
114String rep(String x) {
115 var parsed;
116 try {
117 parsed = READ(x);
118 } on reader.ParseException catch (e) {
119 return e.message;
120 }
121
122 var evaledAst;
123 try {
124 evaledAst = EVAL(parsed, replEnv);
125 } on NotFoundException catch (e) {
126 return "'${e.value}' not found";
127 }
128 return PRINT(evaledAst);
129}
130
131const prompt = 'user> ';
132main() {
133 setupEnv();
134 while (true) {
135 stdout.write(prompt);
136 var input = stdin.readLineSync();
137 if (input == null) return;
138 var output;
139 try {
140 output = rep(input);
141 } on reader.NoInputException {
142 continue;
143 }
144 stdout.writeln(output);
145 }
146}