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