3
"""S-expressions for python.
5
This provides a wire-protocol reader and object representation for
15
from twisted.protocols import protocol
18
return '"'+string.replace(string.replace(st, '\\', '\\\\'),'"','\\"')+'"'
21
assert st[0] == '"' and st[-1] == '"'
22
# strip off the quotes
24
# unescape backslashes
25
st = string.replace(st, '\\\\', '\\')
27
st = string.replace(st, '\\"', '"')
32
class to represent atoms, to distinguish them from strings.
34
def __init__(self, st):
36
def __cmp__(self, other):
37
if isinstance(other, Atom):
38
return cmp(self.string, other.string)
40
return cmp(self.string, other)
43
return hash(self.string)
46
return "atom(%s)" % repr(self.string)
55
return an atom, first checking to see if it's valid
57
assert ATOM.match(st), "invalid atom"
60
# SYMBOL = re.compile(r'[a-zA-Z]([a-zA-Z0-9]|\\.)*')
61
ATOM = re.compile(r'[^ \-\n\r\t0-9"\\()]([^ \n\r\t"\[\]()\\]|\\.)*')
62
STRING = re.compile(r'"([^\\"]|\\.)*"')
63
NUMBER = re.compile(r'-?[0-9]+(\.[0-9]*)?')
64
WHITESPACE = re.compile('[ \n\r\t]+')
67
class SymbolicExpressionReceiver(protocol.Protocol):
74
# I don't ever want to buffer more than 64k of data before bailing.
75
maxUnparsedBufferSize = 32 * 1024
77
def symbolicExpressionReceived(self, expr):
79
This class's raison d'etre, this callback is made when a full
80
S-expression is received. (Note that a full expression may be a single
83
print "unimplemented symbolicExpressionReceived(%s)" % repr(expr)
86
def sendSymbolicExpression(self, expr):
88
Sends a symbolic expression to the other end.
90
assert isinstance(expr, SymbolicExpression)
92
self.transport.write(str(expr))
94
self.expq.append(expr)
96
def connectionMade(self):
101
self.sendSymbolicExpression(xp)
106
self.listStack[-1].append(newCurrentSexp)
107
self.listStack.append(newCurrentSexp)
109
def openQuote(self, name):
110
newCurrentSexp = [Atom(name)]
111
self.quotes.append(newCurrentSexp)
113
self.listStack[-1].append(newCurrentSexp)
114
self.listStack.append(newCurrentSexp)
118
def closeParen(self):
119
aList = self.listStack.pop()
120
for i in range(len(self.quotes)):
121
if aList is self.quotes[i][1]:
123
i = self.listStack.pop()
124
if not self.listStack:
127
if not self.listStack:
128
self._sexpRecv(aList)
130
def _tokenReceived(self, tok):
133
self.listStack[-1].append(tok)
134
if self.quotes and self.listStack[-1] is self.quotes[-1]:
136
i = self.listStack.pop()
137
if not self.listStack:
143
def _sexpRecv(self, xp):
144
self.symbolicExpressionReceived(xp)
146
def dataReceived(self, data):
147
buffer = self.buffer + data
149
# eat any whitespace at the beginning of the string.
150
m = WHITESPACE.match(buffer)
152
buffer = buffer[m.end():]
164
self.quoteLevel = self.quoteLevel + 1
166
self.listStack[-1].append(Atom("backquote"))
171
self.quoteLevel = self.quoteLevel - 1
172
if self.quoteLevel < 0:
173
raise Error("Too many )s")
180
self.openQuote("unquote-splice")
183
self.openQuote("unquote")
187
self.openQuote("quote")
190
m = STRING.match(buffer)
193
st, buffer = buffer[:end], buffer[end:]
194
self._tokenReceived(pythonString(st))
196
m = NUMBER.match(buffer)
199
if end != len(buffer):
200
number, buffer = buffer[:end], buffer[end:]
201
# If this fails, the RE is buggy.
203
number = float(number)
206
self._tokenReceived(number)
208
m = ATOM.match(buffer)
211
if end != len(buffer):
212
symbol, buffer = buffer[:end], buffer[end:]
213
self._tokenReceived(Atom(symbol))
216
if len(buffer) > self.maxUnparsedBufferSize:
217
raise SymbolicExpressionParseError("Too much unparsed data.")
220
def connectionLost(self):
222
self.symbolicExpressionReceived(self.listStack[-1])
224
class _fromString(SymbolicExpressionReceiver):
226
def symbolicExpressionReceived(self, expr):
229
def __init__(self, st):
230
SymbolicExpressionReceiver.__init__(self)
231
self.connectionMade()
232
self.dataReceived(st)
233
self.connectionLost()