Skip to content
This repository was archived by the owner on Jan 4, 2025. It is now read-only.

Commit 690d60b

Browse files
committed
factor tests out of the reader. make it use Lark data structures. remove stupid build stack.
1 parent b1d354d commit 690d60b

File tree

2 files changed

+60
-56
lines changed

2 files changed

+60
-56
lines changed

sexpr.py

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
##
77
## Stripped down for Lark by Scott Wolchok.
88

9+
from roots import cons, list, nconc1
10+
from string import *
11+
from symbol import *
912

1013
class SExprIllegalClosingParenError(ValueError):
1114
pass
@@ -29,6 +32,7 @@ class SExprReader(object):
2932
PAREN_END = ')'
3033
QUOTE = '"'
3134
ESCAPE = '\\'
35+
LISP_QUOTE = '\''
3236

3337
def __init__(self, next_filter,
3438
comment_begin=COMMENT_BEGIN,
@@ -37,6 +41,7 @@ def __init__(self, next_filter,
3741
paren_begin=PAREN_BEGIN,
3842
paren_end=PAREN_END,
3943
quote=QUOTE,
44+
lisp_quote = LISP_QUOTE,
4045
escape=ESCAPE):
4146
self.next_filter = next_filter
4247
self.comment_begin = comment_begin
@@ -45,10 +50,10 @@ def __init__(self, next_filter,
4550
self.paren_begin = paren_begin
4651
self.paren_end = paren_end
4752
self.quote = quote
53+
self.lisp_quote = lisp_quote
4854
self.escape = escape
49-
self.special = comment_begin + separator + paren_begin + paren_end + quote + escape
55+
self.special = comment_begin + separator + paren_begin + paren_end + quote + escape # + lisp_quote
5056
self.reset()
51-
self.symbols = set()
5257

5358
# called if redundant parantheses are found.
5459
def illegal_close_quote(self, i):
@@ -64,22 +69,50 @@ def reset(self):
6469
self.inquote = False # if within a quote
6570
self.inescape = False # if within a escape.
6671
self.sym = [] # partially constructed symbol.
67-
# NOTICE: None != nil (an empty list)
68-
self.build = None # partially constructed list.
6972
self.build_stack = [] # to store a chain of partial lists.
7073
return self
7174

7275

73-
def close_str(self):
74-
# XXX: need mutable strings.
76+
def _close_helper(self):
7577
sym = ''.join(self.sym)
7678
self.sym = []
7779
return sym
7880

81+
def close_str(self):
82+
return String(self._close_helper())
83+
7984
def close_symbol(self):
80-
sym = intern(self.close_str())
81-
self.symbols.add(sym)
82-
return sym
85+
s = self._close_helper()
86+
87+
if s == '#t':
88+
return True
89+
elif s == '#f':
90+
return False
91+
elif s.startswith('#\\'):
92+
rest = s[2:]
93+
if rest == 'newline':
94+
return '\n'
95+
elif rest == 'space':
96+
return ' '
97+
elif rest == 'return':
98+
return '\r'
99+
elif rest == 'tab':
100+
return '\t'
101+
elif len(rest) == 1:
102+
return rest
103+
else:
104+
raise Exception('Illegal character literal %d!' % s)
105+
try:
106+
return int(s)
107+
except ValueError:
108+
pass
109+
110+
try:
111+
return float(s)
112+
except ValueError:
113+
pass
114+
115+
return Symbol(s)
83116

84117
def feed_next(self, s):
85118
self.next_filter.feed(s)
@@ -109,10 +142,10 @@ def feed(self, tokens):
109142
sym = self.close_str()
110143
else:
111144
sym = self.close_symbol()
112-
if self.build is None:
145+
if not self.build_stack:
113146
self.feed_next(sym)
114147
else:
115-
self.build.append(sym)
148+
self.build_stack[-1] = nconc1(self.build_stack[-1], sym)
116149
if c in self.comment_begin:
117150
# comment
118151
self.incomment = True
@@ -121,40 +154,27 @@ def feed(self, tokens):
121154
self.inquote = not self.inquote
122155
elif c in self.paren_begin:
123156
# beginning a new list.
124-
self.build_stack.append(self.build)
125-
empty = []
126-
if self.build == None:
127-
# begin from a scratch.
128-
self.build = empty
129-
else:
130-
# begin from the end of the current list.
131-
self.build.append(empty)
132-
self.build = empty
157+
self.build_stack.append(nil)
133158
elif c in self.paren_end:
134-
# terminating the current list
135-
if self.build == None:
159+
if not self.build_stack:
136160
# there must be a working list.
137161
self.illegal_close_paren(i)
138162
else:
139-
if len(self.build_stack) == 1:
140-
# current working list is the last one in the stack.
141-
self.feed_next(self.build)
142-
self.build = self.build_stack.pop()
163+
build = self.build_stack.pop()
164+
if self.build_stack:
165+
nconc1(self.build_stack[-1], build)
166+
else:
167+
self.feed_next(build)
143168
return self
144169

145170
# terminate
146171
def terminate(self):
147172
# a working list should not exist.
148-
if self.build != None:
173+
if self.build_stack:
149174
# error - still try to construct a partial structure.
150175
if self.sym:
151-
self.build.append(self.close_symbol())
152-
if len(self.build_stack) == 1:
153-
x = self.build
154-
else:
155-
x = self.build_stack[1]
156-
self.build = None
157-
self.build_stack = []
176+
self.build_stack[-1].append(self.close_symbol())
177+
x = self.build_stack[-1]
158178
self.premature_eof(len(self.build_stack), x)
159179
elif self.sym:
160180
# flush the current working symbol.
@@ -195,22 +215,3 @@ def sexpr2str(e):
195215
if not isinstance(e, list):
196216
return e
197217
return '('+' '.join(map(sexpr2str, e))+')'
198-
199-
200-
# test stuff
201-
def test():
202-
assert str2sexpr('"string"') == ['string']
203-
assert str2sexpr('\\"string\\"') == ['"string"']
204-
assert str2sexpr('(this ;comment\n is (a test (sentences) (des()) (yo)))') == \
205-
[['this', 'is', ['a', 'test', ['sentences'], ['des', []], ['yo']]]]
206-
assert str2sexpr('''(paren\\(\\)theses_in\\#symbol "space in \nsymbol"
207-
this\\ way\\ also. "escape is \\"better than\\" quote")''') == \
208-
[['paren()theses_in#symbol', 'space in \nsymbol', 'this way also.', 'escape is "better than" quote']]
209-
assert str2sexpr('()') == [[]]
210-
# str2sexpr('(this (is (a (parial (sentence')
211-
return
212-
213-
214-
# main
215-
if __name__ == '__main__':
216-
test()

test_sexpr.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import unittest
22

33
from roots import list
4+
from symbol import *
5+
46
import sexpr
57

68
def interned(L):
79
return [intern(x) for x in L]
810

911
class TestSexpr(unittest.TestCase):
1012
def testsymbol(self):
11-
self.assert_(sexpr.str2sexpr('a')[0] is intern('a'))
13+
self.assertEqual(sexpr.str2sexpr('a')[0], Symbol('a'))
1214

1315
def teststring(self):
1416
self.assertEqual(sexpr.str2sexpr('"1"')[0], '1')
@@ -20,7 +22,7 @@ def testfloat(self):
2022
self.assertEqual(sexpr.str2sexpr('0.5')[0], 0.5)
2123

2224
def testescape(self):
23-
self.assertEqual(sexpr.str2sexpr('\\"string\\"')[0], '"string"')
25+
self.assertEqual(sexpr.str2sexpr('\\"string\\"')[0], Symbol('"string"'))
2426

2527
def testemptylist(self):
2628
self.assertEqual(sexpr.str2sexpr('()')[0], list())
@@ -29,7 +31,8 @@ def testshortlist(self):
2931
self.assertEqual(sexpr.str2sexpr('(1)')[0], list(1))
3032

3133
def testlist(self):
32-
self.assertEqual(sexpr.str2sexpr('(a b c)')[0], list('a', 'b', 'c'))
34+
self.assertEqual(sexpr.str2sexpr('(a b c)')[0],
35+
list(Symbol('a'), Symbol('b'), Symbol('c')))
3336

3437
if __name__ == '__main__':
3538
unittest.main()

0 commit comments

Comments
 (0)