-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.py
124 lines (105 loc) · 3.45 KB
/
reader.py
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
from datatypes import (cons, nil, true, false,
Exact, Symbol, String,
make_list, make_dotted, from_cons)
from lex import lex
def read(string):
r = Reader(string)
return r.expression()
def read_many(string):
r = Reader(string)
return from_cons(r.expressions())
class Reader:
def __init__(self, string):
self.tokens = lex(string)
self.current_token = next(self.tokens)
self.at_last = False
self.reader_macros = {
'e': self.rm_exact,
'i': self.rm_inexact,
't': self.rm_true,
'f': self.rm_false,
}
def get_token(self):
"""Advance the token stream and return the popped-off token."""
tok = self.lookahead()
self.next_token()
return tok
def lookahead(self):
"""Return the current lookahead token."""
return self.current_token
def next_token(self):
"""Advance the token stream."""
try:
self.current_token = next(self.tokens)
except StopIteration:
if self.at_last:
raise
self.at_last = True
def expression(self):
tok = self.lookahead()
if tok.type == 'lparen':
return self.list()
elif tok.type == 'symbol':
return self.symbol()
elif tok.type == 'number':
return self.number()
elif tok.type == 'quote':
self.next_token()
expr = self.expression()
return make_list((Symbol('quote'), expr))
elif tok.type == 'hashsym':
return self.reader_macro()
elif tok.type == 'string':
return self.string()
else:
raise RuntimeError('unexpected token {!r}'.format(self.get_token()))
def reader_macro(self):
def raise_error():
raise RuntimeError('unexpected reader macro {!r}'.format(self.get_token()))
char = self.reader_macro_char(self.lookahead())
return self.reader_macros.get(char, raise_error)()
def reader_macro_char(self, tok):
return tok.string[1]
def require(self, tok_type):
token = self.get_token()
if token.type != tok_type:
raise RuntimeError('unexpected token {!r}'.format(token))
def list(self):
self.require('lparen')
l = self.expressions()
self.require('rparen')
return l
def expressions(self):
if self.lookahead().type == 'rparen':
return nil
expressions = []
while True:
if self.lookahead().type == 'rparen':
return make_list(expressions)
elif self.lookahead().type == 'dot':
self.next_token()
return make_dotted(expressions, self.expression())
else:
expressions.append(self.expression())
def symbol(self):
return Symbol(self.get_token().string)
def number(self):
return Exact(self.get_token().string)
def string(self):
return String(self.get_token().string)
def rm_exact(self):
self.next_token()
return make_list((
Symbol('exact-number'),
self.expression()))
def rm_inexact(self):
self.next_token()
return make_list((
Symbol('inexact-number'),
self.expression()))
def rm_true(self):
self.next_token()
return true
def rm_false(self):
self.next_token()
return false