python: remove extraneous macroexpand call.
[jackhill/mal.git] / dart / step3_env.dart
CommitLineData
3934e3f8
HT
1import 'dart:io';
2
3import 'env.dart';
4import 'printer.dart' as printer;
5import 'reader.dart' as reader;
6import 'types.dart';
7
8final Env replEnv = new Env();
9
10void 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
33MalType READ(String x) => reader.read_str(x);
34
35MalType 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
57MalType 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
99String PRINT(MalType x) => printer.pr_str(x);
100
101String 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
118const prompt = 'user> ';
119main() {
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}