Allow it to compile under RPython (segfaults, though)
Chris Pressey
10 years ago
3 | 3 | # ... implemented in a Questionable Manner |
4 | 4 | |
5 | 5 | from optparse import OptionParser |
6 | import re | |
7 | 6 | import sys |
8 | 7 | |
9 | 8 | |
10 | 9 | class AST(object): |
11 | def __init__(self, type, children=None, value=None): | |
10 | def __init__(self, type, children=[], value=None, index=0, formals=[]): | |
12 | 11 | self.type = type |
13 | 12 | self.value = value |
14 | if children is not None: | |
15 | self.children = children | |
16 | else: | |
17 | self.children = [] | |
13 | self.index = index | |
14 | self.formals = formals | |
15 | self.children = [] | |
16 | for child in children: | |
17 | self.add_child(child) | |
18 | 18 | |
19 | 19 | def add_child(self, item): |
20 | assert isinstance(item, AST) | |
20 | 21 | self.children.append(item) |
21 | 22 | |
22 | 23 | def __repr__(self): |
34 | 35 | self.type = None |
35 | 36 | self.scan() |
36 | 37 | |
37 | def scan_pattern(self, pattern, type, token_group=1, rest_group=2): | |
38 | pattern = r'^(' + pattern + r')(.*?)$' | |
39 | match = re.match(pattern, self.text, re.DOTALL) | |
40 | if not match: | |
41 | return False | |
42 | else: | |
38 | def scan_single_char(self, chars, type): | |
39 | text = self.text | |
40 | token = '' | |
41 | ||
42 | if len(text) == 0: | |
43 | return False | |
44 | for char in chars: | |
45 | if text.startswith(char): | |
46 | token += char | |
47 | text = text[1:] | |
48 | break | |
49 | if token: | |
50 | self.text = text | |
51 | self.token = token | |
43 | 52 | self.type = type |
44 | self.token = match.group(token_group) | |
45 | self.text = match.group(rest_group) | |
46 | #print >>sys.stderr, "(%r/%s->%r)" % (self.token, self.type, self.text) | |
47 | 53 | return True |
54 | else: | |
55 | return False | |
56 | ||
57 | def scan_multi_char(self, chars, type): | |
58 | text = self.text | |
59 | token = '' | |
60 | while True: | |
61 | if len(text) == 0: | |
62 | return False | |
63 | found = False | |
64 | for char in chars: | |
65 | if text.startswith(char): | |
66 | token += char | |
67 | text = text[1:] | |
68 | found = True | |
69 | break | |
70 | if not found: | |
71 | if token: | |
72 | self.text = text | |
73 | self.token = token | |
74 | self.type = type | |
75 | return True | |
76 | else: | |
77 | return False | |
78 | ||
79 | def scan_atom(self): | |
80 | text = self.text | |
81 | token = '' | |
82 | if len(text) == 0: | |
83 | return False | |
84 | if text.startswith(':'): | |
85 | token += ':' | |
86 | text = text[1:] | |
87 | else: | |
88 | return False | |
89 | while len(text) != 0 and text[0].isalpha(): | |
90 | token += text[0] | |
91 | text = text[1:] | |
92 | self.text = text | |
93 | self.token = token | |
94 | self.type = 'atom' | |
95 | return True | |
96 | ||
97 | def scan_smallifier(self): | |
98 | text = self.text | |
99 | token = '' | |
100 | if len(text) == 0: | |
101 | return False | |
102 | if text.startswith('<'): | |
103 | token += '<' | |
104 | text = text[1:] | |
105 | else: | |
106 | return False | |
107 | while len(text) != 0 and text[0].isalpha(): | |
108 | token += text[0] | |
109 | text = text[1:] | |
110 | self.text = text | |
111 | self.token = token | |
112 | self.type = 'smallifier' | |
113 | return True | |
114 | ||
115 | def scan_identifier(self): | |
116 | text = self.text | |
117 | token = '' | |
118 | if len(text) == 0: | |
119 | return False | |
120 | while len(text) != 0 and (text[0].isalpha() or text[0] == '?'): | |
121 | token += text[0] | |
122 | text = text[1:] | |
123 | if not token: | |
124 | return False | |
125 | self.text = text | |
126 | self.token = token | |
127 | self.type = 'identifier' | |
128 | return True | |
48 | 129 | |
49 | 130 | def scan(self): |
50 | self.scan_pattern(r'[ \t\r\n]*', 'whitespace') | |
131 | self.scan_multi_char(' \t\r\n', 'whitespace') | |
51 | 132 | if not self.text: |
52 | 133 | self.token = None |
53 | 134 | self.type = 'EOF' |
54 | 135 | return |
55 | if self.scan_pattern(r'\(|\)|\,|\#', 'goose egg'): | |
136 | if self.scan_single_char('(),#', 'goose egg'): | |
56 | 137 | return |
57 | if self.scan_pattern(r':[a-zA-Z]+', 'atom'): | |
138 | if self.scan_atom(): | |
58 | 139 | return |
59 | if self.scan_pattern(r'[a-zA-Z]+\??', 'identifier'): | |
140 | if self.scan_identifier(): | |
60 | 141 | return |
61 | if self.scan_pattern(r'\<[a-z]+', 'smallifier'): | |
142 | if self.scan_smallifier(): | |
62 | 143 | return |
63 | if self.scan_pattern(r'.', 'unknown character'): | |
64 | return | |
65 | else: | |
66 | raise ValueError, "this should never happen, self.text=(%s)" % self.text | |
144 | self.token = self.text[0] | |
145 | self.text = self.text[1:] | |
146 | self.type = 'unknown character' | |
67 | 147 | |
68 | 148 | def expect(self, token): |
69 | 149 | if self.token == token: |
99 | 179 | def __init__(self, text): |
100 | 180 | self.scanner = Scanner(text) |
101 | 181 | self.defining = None |
102 | self.self_arity = None | |
103 | 182 | self.defined = {} |
104 | 183 | self.formals = None |
105 | 184 | |
137 | 216 | self.self_arity = len(args) + 1 |
138 | 217 | expr = self.expr() |
139 | 218 | self.defining = None |
140 | self.self_arity = None | |
141 | 219 | self.formals = None |
142 | 220 | self.defined[name] = len(args) + 1 |
143 | return AST('FunDef', [expr] + args, value=name) | |
221 | return AST('FunDef', [expr], formals=args, value=name) | |
144 | 222 | |
145 | 223 | def expr(self): |
146 | 224 | if self.scanner.consume("cons"): |
201 | 279 | elif self.scanner.consume("#"): |
202 | 280 | if self.defining is None: |
203 | 281 | raise SyntaxError('Use of "#" outside of a function body') |
204 | return AST('ArgRef', value=0) | |
282 | return AST('ArgRef', index=0) | |
205 | 283 | elif self.scanner.on_type("atom"): |
206 | 284 | atom = self.scanner.token |
207 | 285 | self.scanner.scan() |
227 | 305 | 'outside of a function body' % ident) |
228 | 306 | if ident not in self.formals: |
229 | 307 | raise SyntaxError('Undefined argument "%s"' % ident) |
230 | return AST('ArgRef', value=self.formals[ident]) | |
308 | return AST('ArgRef', index=self.formals[ident]) | |
231 | 309 | else: |
232 | 310 | return self.smaller() |
233 | 311 | |
251 | 329 | |
252 | 330 | def smallerterm(self): |
253 | 331 | if self.scanner.consume("#"): |
254 | return AST('ArgRef', value=0) | |
332 | return AST('ArgRef', index=0) | |
255 | 333 | else: |
256 | 334 | return self.smaller() |
257 | 335 | |
258 | 336 | |
259 | 337 | ### Runtime ### |
260 | 338 | |
261 | class Cons(object): | |
339 | ||
340 | class SExpr(object): | |
341 | pass | |
342 | ||
343 | ||
344 | class Atom(SExpr): | |
345 | def __init__(self, text): | |
346 | self.text = text | |
347 | ||
348 | def __str__(self): | |
349 | return self.text | |
350 | ||
351 | def __repr__(self): | |
352 | return "Atom(%r)" % self.text | |
353 | ||
354 | def __eq__(self, other): | |
355 | return isinstance(other, Atom) and self.text == other.text | |
356 | ||
357 | ||
358 | class Cons(SExpr): | |
262 | 359 | def __init__(self, head, tail): |
263 | 360 | self.head = head |
264 | 361 | self.tail = tail |
267 | 364 | return "(%s %s)" % (self.head, self.tail) |
268 | 365 | |
269 | 366 | def __repr__(self): |
270 | return "Cons(%r,%r)" % (self.head, self.tail) | |
367 | return "Cons(%r, %r)" % (self.head, self.tail) | |
368 | ||
369 | def __eq__(self, other): | |
370 | return False # isinstance(other, Cons) and self.head == other.head and self.tail == other.tail | |
371 | ||
372 | ||
373 | class FunDef(object): | |
374 | def __init__(self, expr, args): | |
375 | self.expr = expr | |
376 | self.args = args | |
377 | ||
378 | ||
379 | TRUE = Atom(':true') | |
380 | FALSE = Atom(':false') | |
271 | 381 | |
272 | 382 | |
273 | 383 | class Evaluator(object): |
274 | 384 | def __init__(self, ast): |
275 | 385 | self.fundefs = {} |
276 | 386 | for fundef in ast.children[1:]: |
277 | self.fundefs[fundef.value] = { | |
278 | 'expr': fundef.children[0], | |
279 | 'args': fundef.children[1:], | |
280 | } | |
387 | self.fundefs[fundef.value] = FunDef( | |
388 | fundef.children[0], fundef.children[1:] | |
389 | ) | |
281 | 390 | self.bindings = [] |
282 | 391 | |
283 | 392 | def eval(self, ast): |
284 | 393 | if ast.type == 'Atom': |
285 | return ast.value | |
394 | return Atom(ast.value) | |
286 | 395 | elif ast.type == 'Cons': |
287 | 396 | v1 = self.eval(ast.children[0]) |
288 | 397 | v2 = self.eval(ast.children[1]) |
289 | 398 | return Cons(v1, v2) |
290 | 399 | elif ast.type == 'Head': |
291 | 400 | v1 = self.eval(ast.children[0]) |
292 | try: | |
293 | return v1.head | |
294 | except AttributeError: | |
401 | if not isinstance(v1, Cons): | |
295 | 402 | raise TypeError("head: Not a cons cell") |
403 | return v1.head | |
296 | 404 | elif ast.type == 'Tail': |
297 | 405 | v1 = self.eval(ast.children[0]) |
298 | 406 | try: |
301 | 409 | raise TypeError("tail: Not a cons cell") |
302 | 410 | elif ast.type == 'If': |
303 | 411 | v1 = self.eval(ast.children[0]) |
304 | if v1 == ':true': | |
412 | if v1 == TRUE: | |
305 | 413 | return self.eval(ast.children[1]) |
306 | 414 | else: |
307 | 415 | return self.eval(ast.children[2]) |
309 | 417 | v1 = self.eval(ast.children[0]) |
310 | 418 | v2 = self.eval(ast.children[1]) |
311 | 419 | if v1 == v2: |
312 | return ':true' | |
420 | return TRUE | |
313 | 421 | else: |
314 | return ':false' | |
422 | return FALSE | |
315 | 423 | elif ast.type == 'Cons?': |
316 | 424 | v1 = self.eval(ast.children[0]) |
317 | 425 | if isinstance(v1, Cons): |
318 | return ':true' | |
426 | return TRUE | |
319 | 427 | else: |
320 | return ':false' | |
428 | return FALSE | |
321 | 429 | elif ast.type == 'Not': |
322 | 430 | v1 = self.eval(ast.children[0]) |
323 | if v1 == ':true': | |
324 | return ':false' | |
431 | if v1 == TRUE: | |
432 | return FALSE | |
325 | 433 | else: |
326 | return ':true' | |
434 | return TRUE | |
327 | 435 | elif ast.type == 'Call': |
328 | fun = self.fundefs[ast.value] | |
436 | fundef = self.fundefs[ast.value] | |
329 | 437 | bindings = self.bindings |
330 | 438 | self.bindings = [self.eval(expr) for expr in ast.children] |
331 | result = self.eval(fun['expr']) | |
439 | result = self.eval(fundef.expr) | |
332 | 440 | self.bindings = bindings |
333 | 441 | return result |
334 | 442 | elif ast.type == 'ArgRef': |
335 | return self.bindings[ast.value] | |
443 | return self.bindings[ast.index] | |
336 | 444 | elif ast.type == 'Program': |
337 | 445 | return self.eval(ast.children[0]) |
338 | 446 | else: |
378 | 486 | sys.exit(0) |
379 | 487 | |
380 | 488 | |
489 | def target(*args): | |
490 | import os | |
491 | ||
492 | def rpython_load(filename): | |
493 | fd = os.open(filename, os.O_RDONLY, 0644) | |
494 | text = '' | |
495 | chunk = os.read(fd, 1024) | |
496 | text += chunk | |
497 | while len(chunk) == 1024: | |
498 | chunk = os.read(fd, 1024) | |
499 | text += chunk | |
500 | os.close(fd) | |
501 | return text | |
502 | ||
503 | def rpython_input(): | |
504 | accum = '' | |
505 | done = False | |
506 | while not done: | |
507 | s = os.read(1, 1) | |
508 | if not s: | |
509 | done = True | |
510 | accum += s | |
511 | return accum | |
512 | ||
513 | def rpython_main(argv): | |
514 | #inp = rpython_input() | |
515 | #if not inp: | |
516 | # inp = 'ifeq' | |
517 | text = rpython_load(argv[1]) | |
518 | p = Parser(text) | |
519 | prog = p.program() | |
520 | print "%r" % prog | |
521 | ev = Evaluator(prog) | |
522 | result = ev.eval(prog) | |
523 | print "%r" % result | |
524 | return 0 | |
525 | ||
526 | return rpython_main, None | |
527 | ||
528 | ||
381 | 529 | if __name__ == "__main__": |
382 | 530 | main(sys.argv) |