6
6
##
7
7
## Stripped down for Lark by Scott Wolchok.
8
8
9
+ from roots import cons , list , nconc1
10
+ from string import *
11
+ from symbol import *
9
12
10
13
class SExprIllegalClosingParenError (ValueError ):
11
14
pass
@@ -29,6 +32,7 @@ class SExprReader(object):
29
32
PAREN_END = ')'
30
33
QUOTE = '"'
31
34
ESCAPE = '\\ '
35
+ LISP_QUOTE = '\' '
32
36
33
37
def __init__ (self , next_filter ,
34
38
comment_begin = COMMENT_BEGIN ,
@@ -37,6 +41,7 @@ def __init__(self, next_filter,
37
41
paren_begin = PAREN_BEGIN ,
38
42
paren_end = PAREN_END ,
39
43
quote = QUOTE ,
44
+ lisp_quote = LISP_QUOTE ,
40
45
escape = ESCAPE ):
41
46
self .next_filter = next_filter
42
47
self .comment_begin = comment_begin
@@ -45,10 +50,10 @@ def __init__(self, next_filter,
45
50
self .paren_begin = paren_begin
46
51
self .paren_end = paren_end
47
52
self .quote = quote
53
+ self .lisp_quote = lisp_quote
48
54
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
50
56
self .reset ()
51
- self .symbols = set ()
52
57
53
58
# called if redundant parantheses are found.
54
59
def illegal_close_quote (self , i ):
@@ -64,22 +69,50 @@ def reset(self):
64
69
self .inquote = False # if within a quote
65
70
self .inescape = False # if within a escape.
66
71
self .sym = [] # partially constructed symbol.
67
- # NOTICE: None != nil (an empty list)
68
- self .build = None # partially constructed list.
69
72
self .build_stack = [] # to store a chain of partial lists.
70
73
return self
71
74
72
75
73
- def close_str (self ):
74
- # XXX: need mutable strings.
76
+ def _close_helper (self ):
75
77
sym = '' .join (self .sym )
76
78
self .sym = []
77
79
return sym
78
80
81
+ def close_str (self ):
82
+ return String (self ._close_helper ())
83
+
79
84
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 )
83
116
84
117
def feed_next (self , s ):
85
118
self .next_filter .feed (s )
@@ -109,10 +142,10 @@ def feed(self, tokens):
109
142
sym = self .close_str ()
110
143
else :
111
144
sym = self .close_symbol ()
112
- if self .build is None :
145
+ if not self .build_stack :
113
146
self .feed_next (sym )
114
147
else :
115
- self .build . append ( sym )
148
+ self .build_stack [ - 1 ] = nconc1 ( self . build_stack [ - 1 ], sym )
116
149
if c in self .comment_begin :
117
150
# comment
118
151
self .incomment = True
@@ -121,40 +154,27 @@ def feed(self, tokens):
121
154
self .inquote = not self .inquote
122
155
elif c in self .paren_begin :
123
156
# 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 )
133
158
elif c in self .paren_end :
134
- # terminating the current list
135
- if self .build == None :
159
+ if not self .build_stack :
136
160
# there must be a working list.
137
161
self .illegal_close_paren (i )
138
162
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 )
143
168
return self
144
169
145
170
# terminate
146
171
def terminate (self ):
147
172
# a working list should not exist.
148
- if self .build != None :
173
+ if self .build_stack :
149
174
# error - still try to construct a partial structure.
150
175
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 ]
158
178
self .premature_eof (len (self .build_stack ), x )
159
179
elif self .sym :
160
180
# flush the current working symbol.
@@ -195,22 +215,3 @@ def sexpr2str(e):
195
215
if not isinstance (e , list ):
196
216
return e
197
217
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 \n symbol"
207
- this\\ way\\ also. "escape is \\ "better than\\ " quote")''' ) == \
208
- [['paren()theses_in#symbol' , 'space in \n symbol' , '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 ()
0 commit comments