gensym: hide the counter in an environment, define inc in stepA.
[jackhill/mal.git] / dart / step6_file.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(List<String> argv) {
12 // TODO(het): use replEnv#set once generalized tearoffs are implemented
13 ns.forEach((sym, fun) => replEnv.set(sym, fun));
14
15 replEnv.set(new MalSymbol('eval'),
16 new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));
17
18 replEnv.set(new MalSymbol('*ARGV*'),
19 new MalList(argv.map((s) => new MalString(s)).toList()));
20
21 rep('(def! not (fn* (a) (if a false true)))');
22 rep("(def! load-file "
23 "(fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
24}
25
26MalType READ(String x) => reader.read_str(x);
27
28MalType eval_ast(MalType ast, Env env) {
29 if (ast is MalSymbol) {
30 var result = env.get(ast);
31 if (result == null) {
32 throw new NotFoundException(ast.value);
33 }
34 return result;
35 } else if (ast is MalList) {
36 return new MalList(ast.elements.map((x) => EVAL(x, env)).toList());
37 } else if (ast is MalVector) {
38 return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());
39 } else if (ast is MalHashMap) {
40 var newMap = new Map<MalType, MalType>.from(ast.value);
41 for (var key in newMap.keys) {
42 newMap[key] = EVAL(newMap[key], env);
43 }
44 return new MalHashMap(newMap);
45 } else {
46 return ast;
47 }
48}
49
50MalType EVAL(MalType ast, Env env) {
51 while (true) {
52 if (ast is! MalList) {
53 return eval_ast(ast, env);
54 } else {
55 if ((ast as MalList).elements.isEmpty) {
56 return ast;
57 } else {
58 var list = ast as MalList;
59 if (list.elements.first is MalSymbol) {
60 var symbol = list.elements.first as MalSymbol;
61 var args = list.elements.sublist(1);
62 if (symbol.value == "def!") {
63 MalSymbol key = args.first;
64 MalType value = EVAL(args[1], env);
65 env.set(key, value);
66 return value;
67 } else if (symbol.value == "let*") {
68 // TODO(het): If elements.length is not even, give helpful error
69 Iterable<List<MalType>> pairs(List<MalType> elements) sync* {
70 for (var i = 0; i < elements.length; i += 2) {
71 yield [elements[i], elements[i + 1]];
72 }
73 }
74
75 var newEnv = new Env(env);
76 MalIterable bindings = args.first;
77 for (var pair in pairs(bindings.elements)) {
78 MalSymbol key = pair[0];
79 MalType value = EVAL(pair[1], newEnv);
80 newEnv.set(key, value);
81 }
82 ast = args[1];
83 env = newEnv;
84 continue;
85 } else if (symbol.value == "do") {
86 eval_ast(new MalList(args.sublist(0, args.length - 1)), env);
87 ast = args.last;
88 continue;
89 } else if (symbol.value == "if") {
90 var condition = EVAL(args[0], env);
91 if (condition is MalNil ||
92 condition is MalBool && condition.value == false) {
93 // False side of branch
94 if (args.length < 3) {
95 return new MalNil();
96 }
97 ast = args[2];
98 continue;
99 } else {
100 // True side of branch
101 ast = args[1];
102 continue;
103 }
104 } else if (symbol.value == "fn*") {
105 var params = (args[0] as MalIterable)
106 .elements
107 .map((e) => e as MalSymbol)
108 .toList();
109 return new MalClosure(
110 params,
111 args[1],
112 env,
113 (List<MalType> funcArgs) =>
114 EVAL(args[1], new Env(env, params, funcArgs)));
115 }
116 }
117 var newAst = eval_ast(ast, env) as MalList;
118 var f = newAst.elements.first;
119 var args = newAst.elements.sublist(1);
120 if (f is MalBuiltin) {
121 return f.call(args);
122 } else if (f is MalClosure) {
123 ast = f.ast;
124 env = new Env(f.env, f.params, args);
125 continue;
126 } else {
127 throw 'bad!';
128 }
129 }
130 }
131 }
132}
133
134String PRINT(MalType x) => printer.pr_str(x);
135
136String rep(String x) {
dd7a4f55 137 return PRINT(EVAL(READ(x), replEnv));
3934e3f8
HT
138}
139
140const prompt = 'user> ';
141main(List<String> args) {
142 setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));
143 if (args.isNotEmpty) {
144 rep("(load-file \"${args.first}\")");
145 return;
146 }
147 while (true) {
148 stdout.write(prompt);
149 var input = stdin.readLineSync();
150 if (input == null) return;
151 var output;
152 try {
153 output = rep(input);
dd7a4f55
JM
154 } on reader.ParseException catch (e) {
155 stdout.writeln("Error: '${e.message}'");
156 continue;
157 } on NotFoundException catch (e) {
158 stdout.writeln("Error: '${e.value}' not found");
159 continue;
160 } on MalException catch (e) {
161 stdout.writeln("Error: ${printer.pr_str(e.value)}");
162 continue;
3934e3f8
HT
163 } on reader.NoInputException {
164 continue;
165 }
166 stdout.writeln(output);
167 }
168}