-
Notifications
You must be signed in to change notification settings - Fork 0
/
scheme_reader.py
131 lines (113 loc) · 4.02 KB
/
scheme_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
125
126
127
128
129
130
131
"""This module implements the built-in data types of the Scheme language, along
with a parser for Scheme expressions.
In addition to the types defined in this file, some data types in Scheme are
represented by their corresponding type in Python:
number: int or float
symbol: string
boolean: bool
unspecified: None
The __repr__ method of a Scheme value will return a Python expression that
would be evaluated to the value, where possible.
The __str__ method of a Scheme value will return a Scheme expression that
would be read to the value, where possible.
"""
from ucb import main, trace, interact
from scheme_primitives import Pair, nil, intern, scnum, scstr, scbool
from scheme_tokens import tokenize_lines, DELIMITERS
from buffer import Buffer, InputReader, LineReader
# Scheme list Parser
def scheme_read(src):
"""Read the next expression from SRC, a Buffer of tokens.
>>> lines = ["(+ 1 ", "(+ 23 4)) ("]
>>> src = Buffer(tokenize_lines(lines))
>>> print(scheme_read(src))
(+ 1 (+ 23 4))
>>> read_line("'hello")
Pair('quote', Pair('hello', nil))
>>> print(read_line("(car '(1 2))"))
(car (quote (1 2)))
"""
if src.current() is None:
raise EOFError
val = src.pop()
if type(val) is int or type(val) is float:
return scnum(val)
elif type(val) is bool:
return scbool(val)
elif val not in DELIMITERS:
if val[0] == '"':
return scstr(eval(val))
else:
return intern(val)
elif val == "'":
return Pair('quote', Pair(scheme_read(src), nil))
elif val == "(":
return read_tail(src)
else:
raise SyntaxError("unexpected token: {0}".format(val))
def read_tail(src):
"""Return the remainder of a list in SRC, starting before an element or ).
>>> read_tail(Buffer(tokenize_lines([")"])))
nil
>>> read_tail(Buffer(tokenize_lines(["2 3)"])))
Pair(2, Pair(3, nil))
>>> read_tail(Buffer(tokenize_lines(["2 (3 4))"])))
Pair(2, Pair(Pair(3, Pair(4, nil)), nil))
>>> read_line("(1 . 2)")
Pair(1, 2)
>>> read_line("(1 2 . 3)")
Pair(1, Pair(2, 3))
>>> read_line("(1 . 2 3)")
Traceback (most recent call last):
...
SyntaxError: Expected one element after .
>>> scheme_read(Buffer(tokenize_lines(["(1", "2 .", "'(3 4))", "4"])))
Pair(1, Pair(2, Pair('quote', Pair(Pair(3, Pair(4, nil)), nil))))
"""
try:
if src.current() is None:
raise SyntaxError("unexpected end of file")
if src.current() == ")":
src.pop()
return nil
if src.current() == ".":
a= src.pop()
after_period = scheme_read(src)
b= src.pop()
if b != ')':
raise SyntaxError
else:
return after_period
first = scheme_read(src)
rest = read_tail(src)
return Pair(first, rest)
except EOFError:
raise SyntaxError("unexpected end of file")
# Convenience methods
def buffer_input(prompt="scm> "):
"""Return a Buffer instance containing interactive input."""
return Buffer(tokenize_lines(InputReader(prompt)))
def buffer_lines(lines, prompt="scm> ", show_prompt=False):
"""Return a Buffer instance iterating through LINES."""
if show_prompt:
input_lines = lines
else:
input_lines = LineReader(lines, prompt)
return Buffer(tokenize_lines(input_lines))
def read_line(line):
"""Read a single string LINE as a Scheme expression."""
return scheme_read(Buffer(tokenize_lines([line])))
# Interactive loop
@main
def read_print_loop():
"""Run a read-print loop for Scheme expressions."""
while True:
try:
src = buffer_input("read> ")
while src.more_on_line:
expression = scheme_read(src)
print(expression)
except (SyntaxError, ValueError) as err:
print(type(err).__name__ + ":", err)
except (KeyboardInterrupt, EOFError): # <Control>-D, etc.
return