Merge pull request #379 from bjh21/bjh21-unterminated-string-fixes
[jackhill/mal.git] / dart / types.dart
1 import 'dart:collection';
2 import 'env.dart';
3
4 abstract class MalType {
5 bool get isMacro => false;
6 MalType meta;
7
8 MalType clone();
9 }
10
11 abstract class MalIterable extends MalType
12 with ListMixin<MalType>
13 implements List<MalType> {
14 final List<MalType> elements;
15
16 MalIterable(this.elements);
17
18 MalType operator [](int index) => elements[index];
19 void operator []=(int index, MalType value) {
20 elements[index] = value;
21 }
22
23 int get length => elements.length;
24 void set length(int newLength) {
25 elements.length = newLength;
26 }
27
28 bool operator ==(other) {
29 if (other is! MalIterable) return false;
30
31 // apparently (= (list) nil) should be false...
32 if (other is MalNil) return false;
33
34 if (elements.length != other.elements.length) return false;
35 for (var i = 0; i < elements.length; i++) {
36 if (elements[i] != other.elements[i]) return false;
37 }
38 return true;
39 }
40
41 @override
42 MalIterable clone();
43 }
44
45 class MalList extends MalIterable {
46 MalList(List<MalType> elements) : super(elements);
47
48 @override
49 MalList clone() {
50 return new MalList(elements.toList());
51 }
52 }
53
54 class MalVector extends MalIterable {
55 MalVector(List<MalType> elements) : super(elements);
56
57 @override
58 MalVector clone() {
59 return new MalVector(elements.toList());
60 }
61 }
62
63 class MalHashMap extends MalType {
64 final Map<MalType, MalType> value;
65
66 MalHashMap(this.value);
67
68 MalHashMap.fromSequence(List<MalType> elements)
69 : value = _mapFromSequence(elements);
70
71 static Map<MalType, MalType> _mapFromSequence(List<MalType> elements) {
72 var result = <MalType, MalType>{};
73
74 var readingKey = true;
75 MalType pendingKey;
76 for (var malType in elements) {
77 if (readingKey) {
78 if (malType is MalString || malType is MalKeyword) {
79 pendingKey = malType;
80 } else {
81 throw new ArgumentError('hash-map keys must be strings or keywords');
82 }
83 } else {
84 result[pendingKey] = malType;
85 }
86 readingKey = !readingKey;
87 }
88
89 return result;
90 }
91
92 bool operator ==(other) {
93 if (other is! MalHashMap) return false;
94 var otherMap = (other as MalHashMap).value;
95 if (otherMap.length != value.length) return false;
96 for (var key in value.keys) {
97 if (!otherMap.containsKey(key)) return false;
98 if (value[key] != otherMap[key]) return false;
99 }
100 return true;
101 }
102
103 @override
104 MalHashMap clone() {
105 return new MalHashMap(new Map.from(value));
106 }
107 }
108
109 class MalInt extends MalType {
110 final int value;
111
112 MalInt(this.value);
113
114 bool operator ==(other) {
115 if (other is! MalInt) return false;
116 return other.value == value;
117 }
118
119 @override
120 MalInt clone() {
121 return new MalInt(value);
122 }
123 }
124
125 class MalSymbol extends MalType {
126 final String value;
127
128 MalSymbol(this.value);
129
130 int get hashCode => value.hashCode;
131
132 bool operator ==(other) {
133 if (other is! MalSymbol) return false;
134 return value == other.value;
135 }
136
137 @override
138 MalSymbol clone() {
139 return new MalSymbol(value);
140 }
141 }
142
143 class MalKeyword extends MalType {
144 final String value;
145
146 MalKeyword(this.value);
147
148 int get hashCode => value.hashCode;
149
150 bool operator ==(other) {
151 if (other is! MalKeyword) return false;
152 return value == other.value;
153 }
154
155 @override
156 MalKeyword clone() {
157 return new MalKeyword(value);
158 }
159 }
160
161 class MalString extends MalType {
162 final String value;
163
164 MalString(this.value);
165
166 int get hashCode => value.hashCode;
167
168 bool operator ==(other) {
169 if (other is! MalString) return false;
170 return other.value == value;
171 }
172
173 @override
174 MalString clone() {
175 return new MalString(value);
176 }
177 }
178
179 class MalBool extends MalType {
180 final bool value;
181
182 MalBool(this.value);
183
184 bool operator ==(other) {
185 if (other is! MalBool) return false;
186 return other.value == value;
187 }
188
189 @override
190 MalBool clone() {
191 return new MalBool(value);
192 }
193 }
194
195 class MalNil extends MalIterable {
196 MalNil() : super(const <MalType>[]);
197
198 bool operator ==(other) => other is MalNil;
199
200 @override
201 MalNil clone() {
202 return new MalNil();
203 }
204 }
205
206 class MalAtom extends MalType {
207 MalType value;
208
209 MalAtom(this.value);
210
211 @override
212 MalAtom clone() {
213 return new MalAtom(value);
214 }
215 }
216
217 abstract class MalCallable extends MalType {
218 MalType call(List<MalType> args);
219
220 bool get isMacro => false;
221 }
222
223 typedef MalType BuiltinFunc(List<MalType> args);
224
225 class MalBuiltin extends MalCallable {
226 final BuiltinFunc func;
227
228 MalBuiltin(this.func);
229
230 MalType call(List<MalType> args) {
231 return func(args);
232 }
233
234 @override
235 MalBuiltin clone() {
236 return new MalBuiltin(func);
237 }
238 }
239
240 typedef MalType EvalFun(MalType ast, Env env);
241
242 class MalClosure extends MalCallable {
243 final List<MalSymbol> params;
244 final MalType ast;
245 final Env env;
246 final Function func;
247
248 @override
249 bool isMacro = false;
250
251 MalClosure(this.params, this.ast, this.env, this.func);
252
253 MalType call(List<MalType> args) {
254 return func(args);
255 }
256
257 @override
258 MalClosure clone() {
259 var closure =
260 new MalClosure(this.params.toList(), this.ast, this.env, this.func);
261 closure.isMacro = this.isMacro;
262 return closure;
263 }
264 }
265
266 class MalException implements Exception {
267 final MalType value;
268
269 MalException(this.value);
270 }