git @ Cat's Eye Technologies Tamsin / c1fb806
Buffers go back to being mutable. Beautiful mutable buffers. Chris Pressey 10 years ago
4 changed file(s) with 81 addition(s) and 87 deletion(s). Raw diff Collapse all Expand all
88 class Buffer(object):
99 """Abstract base class for all Buffer objects.
1010
11 You should treat Buffer objects as immutable.
11 Buffer objects are mutable, but must be capable of saving and restoring
12 their state indefinitely.
1213
1314 """
1415 def __init__(self, filename='<data>', position=0, line_number=1, column_number=1):
1617 be given too, to match.
1718
1819 """
19 self._filename = filename
20 self.filename = filename
2021 self.position = position
21 self._line_number = line_number
22 self._column_number = column_number
22 self.line_number = line_number
23 self.column_number = column_number
2324
24 @property
25 def filename(self):
26 return self._filename
25 def save_state(self):
26 raise NotImplementedError
2727
28 @property
29 def line_number(self):
30 return self._line_number
28 def restore_state(self):
29 raise NotImplementedError
3130
32 @property
33 def column_number(self):
34 return self._column_number
31 def pop_state(self):
32 raise NotImplementedError
3533
3634 def advance(self, inp):
3735 """Given a string that we have just consumed from the buffer,
7371 """
7472 assert not isinstance(string, unicode)
7573 self.string = string
74 self.stack = []
7675 Buffer.__init__(self, **kwargs)
76
77 def save_state(self):
78 self.stack.append((self.position, self.line_number, self.column_number))
79
80 def restore_state(self):
81 (self.position, self.line_number, self.column_number) = self.stack.pop()
82
83 def pop_state(self):
84 self.stack.pop()
7785
7886 def __str__(self):
7987 return self.string
8694 def chop(self, amount):
8795 assert self.position <= len(self.string) - amount, \
8896 "attempt made to chop past end of buffer"
89 chars = self.string[self.position:self.position + amount]
97 bytes = self.string[self.position:self.position + amount]
9098
91 (line_number, column_number) = self.advance(chars)
92 new_buffer = StringBuffer(self.string,
93 filename=self._filename,
94 position=self.position + amount,
95 line_number=line_number,
96 column_number=column_number
97 )
99 self.position += amount
100 (self.line_number, self.column_number) = self.advance(bytes)
98101
99 return (chars, new_buffer)
102 return bytes
100103
101104 def first(self, amount):
102 #assert self.position <= len(self.string) - amount, \
103 # "attempt made to first past end of buffer"
104 #if self.position > len(self.string) - amount:
105 # return None
106 chars = self.string[self.position:self.position + amount]
105 bytes = self.string[self.position:self.position + amount]
107106
108 # did not modify self, so it's OK to return it
109 return (chars, self)
107 return bytes
110108
111109
112110 class FileBuffer(Buffer):
114112 # filehandles are mutable :/ ... and Buffers can't be mutable
115113 # at all. TODO: make it so that Buffers can be mutable, oi
116114 def __init__(self, file, pre_buffer='', **kwargs):
115 assert False, 'NO'
117116 self.file = file
118117 self.pre_buffer = pre_buffer
118 self.stack = []
119119 Buffer.__init__(self, **kwargs)
120
121 def save_state(self):
122 self.stack.append((self.position, self.line_number, self.column_number))
123
124 def restore_state(self):
125 (self.position, self.line_number, self.column_number) = self.stack.pop()
126
127 def pop_state(self):
128 self.stack.pop()
120129
121130 def get_bytes(self, amount):
122131 """Returns a new pre_buffer."""
128137
129138 def chop(self, amount):
130139 pre_buffer = self.get_bytes(amount)
131 chars = pre_buffer[0:amount]
140 bytes = pre_buffer[0:amount]
132141 remaining = pre_buffer[amount:]
133142
134 (line_number, column_number) = self.advance(chars)
135 new_buffer = FileBuffer(self.file,
136 filename=self._filename,
137 pre_buffer=remaining,
138 position=self.position + amount,
139 line_number=line_number,
140 column_number=column_number
141 )
142 self.file = None
143 return (chars, new_buffer)
143 (self.line_number, self.column_number) = self.advance(bytes)
144 return bytes
144145
145146 def first(self, amount):
146147 pre_buffer = self.get_bytes(amount)
147 chars = pre_buffer[0:amount]
148 bytes = pre_buffer[0:amount]
148149
149 new_buffer = FileBuffer(self.file,
150 filename=self._filename,
151 pre_buffer=pre_buffer,
152 position=self.position,
153 line_number=self._line_number,
154 column_number=self._column_number
155 )
156 self.file = None
157 return (chars, new_buffer)
150 return bytes
134134 return (success, value_rhs)
135135 elif isinstance(ast, Or):
136136 saved_context = self.context.clone()
137 saved_scanner_state = self.scanner.get_state()
138 self.event('begin_or', ast.lhs, ast.rhs, saved_context, saved_scanner_state)
137 self.scanner.save_state()
138 self.event('begin_or', ast.lhs, ast.rhs, saved_context)
139139 (succeeded, result) = self.interpret(ast.lhs)
140140 if succeeded:
141141 self.event('succeed_or', result)
142 self.scanner.pop_state()
142143 return (True, result)
143144 else:
144145 self.event('fail_or', self.context, self.scanner, result)
145146 self.context = saved_context
146 self.scanner.install_state(saved_scanner_state)
147 self.scanner.restore_state()
147148 return self.interpret(ast.rhs)
148149 elif isinstance(ast, Call):
149150 prodref = ast.prodref
186187 (success, result) = self.interpret(ast.texpr)
187188 buffer = str(result.expand(self.context))
188189 self.event('interpret_on_buffer', buffer)
189 saved_scanner_state = self.scanner.get_state()
190 new_state = StringBuffer(buffer)
191 self.scanner.install_state(new_state)
190 previous_buffer = self.scanner.get_buffer()
191 self.scanner.install_buffer(StringBuffer(buffer))
192192 (success, result) = self.interpret(ast.rule)
193 self.scanner.install_state(saved_scanner_state)
193 self.scanner.install_buffer(previous_buffer)
194194 return (success, result)
195195 elif isinstance(ast, Set):
196196 (success, variable) = self.interpret(ast.variable)
201201 elif isinstance(ast, Not):
202202 expr = ast.rule
203203 saved_context = self.context.clone()
204 saved_scanner_state = self.scanner.get_state()
205 self.event('begin_not', expr, saved_context, saved_scanner_state)
204 self.scanner.save_state()
205 self.event('begin_not', expr, saved_context)
206206 (succeeded, result) = self.interpret(expr)
207207 self.context = saved_context
208 self.scanner.install_state(saved_scanner_state)
208 self.scanner.restore_state()
209209 if succeeded:
210210 return (False, Atom(self.scanner.error_message(
211211 "anything else", self.scanner.peek()
219219 successful_result = result
220220 while succeeded:
221221 saved_context = self.context.clone()
222 saved_scanner_state = self.scanner.get_state()
222 self.scanner.save_state()
223223 (succeeded, result) = self.interpret(ast.rule)
224224 if succeeded:
225 self.scanner.pop_state()
225226 successful_result = result
226227 self.event('repeating_while', result)
228 else:
229 self.scanner.restore_state()
227230 self.context = saved_context
228 self.scanner.install_state(saved_scanner_state)
229231 self.event('end_while', result)
230232 return (True, successful_result)
231233 elif isinstance(ast, Concat):
2222 def parse(filename):
2323 with open(filename, 'r') as f:
2424 scanner = Scanner(
25 FileBuffer(f, filename=filename),
26 #StringBuffer(f.read(), filename=filename),
25 #FileBuffer(f, filename=filename),
26 StringBuffer(f.read(), filename=filename),
2727 engines=(TamsinScannerEngine(),)
2828 )
2929 parser = Parser(scanner)
4848
4949 def run(ast, listeners=None):
5050 scanner = Scanner(
51 FileBuffer(sys.stdin, filename='<stdin>'),
52 #StringBuffer(sys.stdin.read(), filename='<stdin>'),
51 #FileBuffer(sys.stdin, filename='<stdin>'),
52 StringBuffer(sys.stdin.read(), filename='<stdin>'),
5353 engines=(UTF8ScannerEngine(),),
5454 listeners=listeners
5555 )
2929 self.buffer, self.position
3030 )
3131
32 def get_state(self):
33 """Returns an object which saves the current state of this
32 def get_buffer(self):
33 """Returns an object which represents the current Buffer of this
3434 Scanner.
3535
3636 """
3737 return self.buffer
3838
39 def install_state(self, state):
40 """Restores the state of this Scanner to that which was saved by
41 a previous call to get_state().
39 def install_buffer(self, state):
40 """Restores the Buffer of this Scanner to that which was saved by
41 a previous call to get_buffer().
4242
4343 """
4444 self.buffer = state
4949 def pop_engine(self):
5050 engine = self.engines.pop()
5151
52 # # # # # # # Buffer interface # # # # # # #
53 #
54 # These methods hide the immutability of Buffer.
55 #
52 def save_state(self):
53 return self.buffer.save_state()
54
55 def restore_state(self):
56 return self.buffer.restore_state()
57
58 def pop_state(self):
59 return self.buffer.pop_state()
5660
5761 def chop(self, amount):
5862 """Returns amount characters from the buffer and advances the
6165 Should only be used by ScannerEngines.
6266
6367 """
64 (chars, buffer) = self.buffer.chop(amount)
65 self.buffer = buffer
66 return chars
68 return self.buffer.chop(amount)
6769
6870 def first(self, amount):
6971 """Returns amount characters from the buffer. Does not advance the
7375 reporting.
7476
7577 """
76 (chars, buffer) = self.buffer.first(amount)
77 self.buffer = buffer
78 return chars
79
80 # # # # # # # # # # # # # # # # # # # # # #
78 return self.buffer.first(amount)
8179
8280 def is_at_eof(self):
8381 """Returns True iff there is no more input to scan.
148146 return token
149147
150148 def peek(self):
151 backup = self.get_state()
149 self.buffer.save_state()
152150 token = self.scan()
153 self.install_state(backup)
151 self.buffer.restore_state()
154152 return token
155153
156154 def consume(self, t):
158156 t = t.encode('UTF-8')
159157 assert not isinstance(t, unicode)
160158 self.event('consume', t)
161 backup = self.get_state()
159 self.buffer.save_state()
162160 s = self.scan()
163161 if s == t:
162 self.buffer.pop_state()
164163 return t
165164 else:
166 self.install_state(backup)
165 self.buffer.restore_state()
167166 return None
168167
169168 def expect(self, t):