This repository has been archived by the owner on Oct 31, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInterpreter.java
536 lines (476 loc) · 21.3 KB
/
Interpreter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
import java.util.Stack;
import java.util.Iterator; // remove
import java.util.HashMap;
import java.util.Map;
/* *** This file is given as part of the programming assignment. *** */
public class Interpreter {
// a scanner...
private Scan scanner;
// contains variables; current implementation is just built-in functions
private Map<String,Expression> variables;
// tok is global to all these parsing methods;
// scan just calls the scanner's scan method and saves the result in tok.
private Token tok; // the current token
private void scan() {
tok = scanner.scan();
}
// note on the first sets:
// we cheat -- there is only a single token in the set,
// so we just compare tkrep with the first token.
// level at which parsing;
// used to handle the problem of scan-ahead, given interactive system.
private int level;
// for error handling
// to make this a bit friendlier in interactive environment;
// handle parse errors by jumping back to main loop.
class ParsingExpressionException extends Exception {
public ParsingExpressionException(String msg) {
// super(msg); // call constructor in superclass (i.e., base class);
// it outputs message and a bit more.
}
}
public Interpreter(String list[]) {
scanner = new Scan(list);
variables = new HashMap<String,Expression>();
init_builtins();
while (true) {
System.out.print("> ");
System.out.flush();
// always reset since previous might have failed
level = 0;
scan();
if (is(TK.EOF)) break;
try {
// read and parse expression.
Expression expression = expr();
// in later parts:
// print out expression
System.out.println(expression.toString());
// evaluate expression
// print out value of evaluated expression
System.out.println(expression.evaluate(variables));
// note that an error in evaluating (at any level) will
// return List.nil and evaluation will continue.
}
catch (ParsingExpressionException e) {
System.out.println( "trying to recover from parse error");
gobble();
}
//System.out.println("");
}
}
private Expression expr() throws ParsingExpressionException {
if (is(TK.LPAREN))
return list();
else if (is(TK.ID) || is(TK.NUM))
return atom();
else if (is(TK.QUOTE)) {
/*
added handling of quote by simply adding a new recursive function
which simply creates a list (quote expr) where expr is gotten
from an expr() call. almost nothing changes.
*/
return quote();
}
else {
parse_error("bad start of expression");
/*NOTREACHED*/
}
return null; // is there a better way to make Java not complain?
}
private List list() throws ParsingExpressionException {
level++;
mustbe(TK.LPAREN);
// we use a list that we will initialize when we first come
// across something (which we either will, or will find a
// close paren and return List.nil)
// i wonder if there a better way to do this
// at first, i was trying to just make functionally nil lists
// which i would call append on and would set the first value
// if it were null, but that seemed even worse than this
// the idea of building a vector/list and iterating through it
// was also quite unappealing
List l = null;
if (is(TK.RPAREN))
l = List.nil;
else {
// first gets set to false at the end of the loop, once
// it's been gone through once already
boolean first = true;
while (!is(TK.RPAREN)) {
if (is(TK.LPAREN))
if (first)
l = new List(list());
else
l.append(new List(list()));
else if (is(TK.NUM) || is(TK.ID))
if (first)
l = new List(atom());
else
l.append(atom());
else if (is(TK.QUOTE))
if (first)
l = new List(quote());
else
l.append(new List(quote()));
else
parse_error("oops -- bad atom " + tok.kind);
first = false;
}
}
level--;
mustbe(TK.RPAREN);
return l;
}
private Atom atom() throws ParsingExpressionException {
Token this_tok = tok;
if (is(TK.ID)) {
mustbe(TK.ID);
return new Identifier(this_tok.string);
}
else if (is(TK.NUM)) {
mustbe(TK.NUM);
return new Number(Integer.parseInt(this_tok.string));
}
else {
parse_error("oops -- bad atom");
/*NOTREACHED*/
}
return null; // is there a better way to make Java not complain?
}
private List quote() throws ParsingExpressionException {
scan(); // throw away the quote
return new List(new Identifier("quote"), new List(expr()));
}
// is current token what we want?
private boolean is(TK tk) {
return tk == tok.kind;
}
// ensure current token is tk and skip over it.
void mustbe(TK tk) throws ParsingExpressionException {
if( !is(tk) ) {
System.err.println( "mustbe: want " + tk + ", got " + tok );
parse_error( "missing token (mustbe)" );
}
// read ahead to next token only if not at top level.
// this enables returning to main loop after parse entire expression;
// otherwise would need to wait for user to type first
// part of next expression before evaluating current expression,
// which wouldn't be so good in interactive environment.
// (so main loop always calls scan before calling expr)
if (level > 0) scan();
}
void parse_error(String msg) throws ParsingExpressionException {
System.err.println( "can't parse: " + msg );
throw new ParsingExpressionException("problem parsing");
}
// used in recovering from errors.
// gobble up all tokens up until something that could start an expression.
// obviously, not entirely effective...
// another possibility would be to gobble up to matching ) or ]
// but that's not 100% effective either.
void gobble() {
while( level > 0 &&
!is(TK.LPAREN) &&
!is(TK.ID) &&
!is(TK.NUM) &&
!is(TK.EOF) ) {
scan();
}
}
private void init_builtins() {
// eventually reduce redudancy here
variables.put("nil", List.nil);
variables.put("show", new Function("show", 0) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
System.out.println(" show special 0 builtin");
System.out.println(" cons non-special 2 builtin");
System.out.println(" car non-special 1 builtin");
System.out.println(" cdr non-special 1 builtin");
System.out.println(" quote special 1 builtin");
System.out.println(" list non-special -1 builtin");
System.out.println(" append non-special -1 builtin");
System.out.println(" length non-special 1 builtin");
System.out.println(" + non-special 2 builtin");
System.out.println(" - non-special 2 builtin");
System.out.println(" * non-special 2 builtin");
System.out.println(" / non-special 2 builtin");
System.out.println(" = non-special 2 builtin");
System.out.println(" /= non-special 2 builtin");
System.out.println(" < non-special 2 builtin");
System.out.println(" > non-special 2 builtin");
System.out.println(" <= non-special 2 builtin");
System.out.println(" >= non-special 2 builtin");
System.out.println(" null non-special 1 builtin");
System.out.println(" atom non-special 1 builtin");
System.out.println(" listp non-special 1 builtin");
System.out.println(" integerp non-special 1 builtin");
System.out.println(" cond special -1 builtin");
return List.nil;
}
});
variables.put("quote", new Function("quote", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
return list.getCar();
}
});
variables.put("list", new Function("list", -1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null)
return List.nil;
return list.evaluate_elements(variables);
}
});
variables.put("null", new Function("null", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
if (list.getCar() == List.nil)
return Number.t;
else
return List.nil;
}
});
variables.put("atom", new Function("atom", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
if (list.getCar() == List.nil)
return Number.t;
if (list.length() == 1)
if (list.getCar().evaluate(variables) instanceof Atom)
return Number.t;
return List.nil;
}
});
variables.put("listp", new Function("listp", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
if (list.length() == 1)
if(list.getCar().evaluate(variables) instanceof List)
return Number.t;
return List.nil;
}
});
variables.put("integerp", new Function("integerp", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
if (list.length() == 1) {
Expression test = list.getCar().evaluate(variables);
if (test instanceof Number)
return Number.t;
}
return List.nil;
}
});
variables.put("cons", new Function("cons", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
Expression cdr = list.getCdr().getCar().evaluate(variables);
if (cdr instanceof List) {
List consed = new List(list.getCar().evaluate(variables));
if (cdr != List.nil)
consed.append((List) cdr);
return consed;
}
System.out.println("cons's 2nd argument is non-list");
return List.nil;
}
});
variables.put("car", new Function("car", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (list == List.nil)
return List.nil;
if (!checkArity(list, variables))
return List.nil;
Expression car = list.getCar().evaluate(variables);
if (car instanceof List)
return ((List) car).getCar();
return List.nil;
}
});
variables.put("cdr", new Function("cdr", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (list == List.nil)
return List.nil;
if (!checkArity(list, variables))
return List.nil;
Expression car = list.getCar().evaluate(variables);
if (car instanceof List)
return ((List) car).getCdr();
return List.nil;
}
});
variables.put("length", new Function("length", 1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables))
return List.nil;
Expression car = list.getCar().evaluate(variables);
if (!(car instanceof List)) {
System.out.println("length given a non-list or an impure list (dotted pair at end of list)");
return List.nil;
}
return new Number(((List) car).length());
}
});
variables.put("append", new Function("append", -1) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return List.chain_append(list);
}
catch (List.AppendGivenNonListException e) {
return List.nil;
}
}
});
variables.put("+", new Function("+", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).add((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("-", new Function("-", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).subtract((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("*", new Function("*", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).mult((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("/", new Function("/", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).div((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("=", new Function("=", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).equal((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("/=", new Function("/=", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).notEqual((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("<", new Function("<", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).lessThan((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put(">", new Function(">", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).greaterThan((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("<=", new Function("<=", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).lessThanOrEqual((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put(">=", new Function(">=", 2) {
public Expression call(List list, Map<String,Expression> variables) {
if (!checkArity(list, variables) || list == null || list == List.nil)
return List.nil;
list = list.evaluate_elements(variables);
try {
return ((Number) list.getCar()).greaterThanOrEqual((Number) list.getCdr().getCar());
} catch (ClassCastException e) {
System.out.println("builtin arithmetic rel op given non-number");
return List.nil;
}
}
});
variables.put("cond", new Function("cond", -1) {
public Expression call(List list, Map<String,Expression> variables) {
if (list == List.nil || list == null)
return List.nil;
if (list.getCar() == List.nil)
return List.nil;
return Function.chain_cond(list, variables);
}
});
}
}