git @ Cat's Eye Technologies Tamsin / 7640d93
Refactor context stuff so that backtracking applies to variables. Cat's Eye Technologies 11 years ago
5 changed file(s) with 107 addition(s) and 34 deletion(s). Raw diff Collapse all Expand all
220220 + ignored
221221 = (whatever stuff)
222222
223 And note that variables are subject to backtracking, too; if a variable is
224 set while parsing something that failed, it is no longer set in the `|`
225 alternative.
226
227 | main = set E = original &
228 | (set E = changed && "0" && "1" | "0" && "2") &
229 | return E.
230 + 0 1
231 = changed
232
233 | main = set E = original &
234 | (set E = changed && "0" && "1" | "0" && "2") &
235 | return E.
236 + 0 2
237 = original
238
223239 The rule `fail` always fails. This lets you establish global flags, of
224240 a sort.
225241
0 main = set E = original &
1 (set E = changed && "0" && "1" | "0" && "2") &
2 return E.
0 main = bit → A & bit → B & return (A B).
1 bit = "0" | "1".
2
2929 return result
3030
3131 def error(self, expected):
32 report = self.buffer[:20]
33 if len(self.buffer) > 20:
34 report += '...'
3235 raise ValueError("Expected %s, found '%s' at '%s...'" %
33 (expected, self.token, self.buffer[:20]))
36 (expected, self.token, report))
3437
3538 def scan(self):
3639 self.scan_impl()
185188 self.input = input_.split(' ')
186189 self.position = 0
187190 self.scan()
191 self.contexts = []
188192 #print repr(self.input)
193
194 ### context stuff ---------------------------------------- ###
195
196 def push_context(self, purpose):
197 debug("pushing new context for %r" % purpose)
198 self.contexts.append({})
199
200 def pop_context(self, purpose):
201 debug("popping context for %r" % purpose)
202 self.contexts.pop()
203
204 def current_context(self):
205 """Don't assume anything about what this returns except that
206 you can pass it to install_context().
207
208 """
209 debug("retrieving current context %r" % self.contexts[-1])
210 return self.contexts[-1].copy()
211
212 def install_context(self, context):
213 debug("installing context %r" % context)
214 self.contexts[-1] = context
215
216 def fetch(self, name):
217 debug("fetching %s (it's %r)" %
218 (name, self.contexts[-1].get(name, 'undefined'))
219 )
220 return self.contexts[-1][name]
221
222 def store(self, name, value):
223 debug("updating %s (was %s) to %r" %
224 (name, self.contexts[-1].get(name, 'undefined'), value)
225 )
226 self.contexts[-1][name] = value
227
228 ### scanner stuff ---------------------------------------- ###
189229
190230 def scan(self):
191231 if self.position >= len(self.input):
198238 self.position = position
199239 self.token = self.input[position - 1]
200240
241 ### grammar stuff ---------------------------------------- ###
242
201243 def find_production(self, name):
202244 found = None
203245 for x in self.program[1]:
208250 raise ValueError("No '%s' production defined" % name)
209251 return found
210252
211 def replace_vars(self, ast, context):
212 """Expands a term."""
253 ### term stuff ---------------------------------------- ###
254
255 def replace_vars(self, ast):
256 """Expands a term, replacing all (VAR x) with the value of x
257 in the current context."""
213258
214259 if ast[0] == 'ATOM':
215260 return ast
216261 elif ast[0] == 'LIST':
217 return ('LIST', [self.replace_vars(x, context) for x in ast[1]])
262 return ('LIST', [self.replace_vars(x) for x in ast[1]])
218263 elif ast[0] == 'VAR':
219 return context[ast[1]] # context.get(ast[1], None)
264 return self.fetch(ast[1])
220265 else:
221266 raise NotImplementedError("internal error: bad term")
222267
230275 else:
231276 raise NotImplementedError("internal error: bad term")
232277
233 def interpret(self, ast, context):
278 ### interpreter proper ---------------------------------- ###
279
280 def interpret(self, ast):
234281 if ast[0] == 'PROGRAM':
235 return self.interpret(self.find_production('main'), context)
282 return self.interpret(self.find_production('main'))
236283 elif ast[0] == 'PROD':
237284 #print "interpreting %s" % repr(ast)
238 return self.interpret(ast[2], {})
285 self.push_context(ast[1])
286 x = self.interpret(ast[2])
287 self.pop_context(ast[1])
288 return x
239289 elif ast[0] == 'CALL':
240 new_context = {}
241 result = self.interpret(self.find_production(ast[1]), new_context)
290 result = self.interpret(self.find_production(ast[1]))
242291 if ast[2] is not None:
243292 assert ast[2][0] == 'VAR', ast
244293 varname = ast[2][1]
245 debug("updating %s (was %s) to %r" %
246 (varname, context.get(varname, 'undefined'), result)
247 )
248 context[varname] = result
294 self.store(varname, result)
249295 return result
250296 elif ast[0] == 'SET':
251297 assert ast[1][0] == 'VAR', ast
252298 varname = ast[1][1]
253 result = self.replace_vars(ast[2], context)
254 debug("setting %s (was %s) to %r" %
255 (varname, context.get(varname, 'undefined'), result)
256 )
257 context[varname] = result
299 result = self.replace_vars(ast[2])
300 self.store(varname, result)
258301 return result
259302 elif ast[0] == 'AND':
260303 lhs = ast[1]
261304 rhs = ast[2]
262 value_lhs = self.interpret(lhs, context)
263 value_rhs = self.interpret(rhs, context)
305 value_lhs = self.interpret(lhs)
306 value_rhs = self.interpret(rhs)
264307 return value_rhs
265308 elif ast[0] == 'OR':
266309 lhs = ast[1]
267310 rhs = ast[2]
268 saved_context = context.copy()
311 saved_context = self.current_context()
269312 saved_position = self.position
270313 try:
271 return self.interpret(lhs, context)
314 return self.interpret(lhs)
272315 except TamsinParseError as e:
273 context = saved_context
316 self.install_context(saved_context)
274317 self.rewind(saved_position)
275 return self.interpret(rhs, context)
318 return self.interpret(rhs)
276319 elif ast[0] == 'RETURN':
277 return self.replace_vars(ast[1], context)
320 return self.replace_vars(ast[1])
278321 elif ast[0] == 'FAIL':
279322 raise TamsinParseError("fail")
280323 elif ast[0] == 'WHILE':
281324 result = ('ATOM', 'nil')
282325 while True:
283326 try:
284 result = self.interpret(ast[1], context)
327 result = self.interpret(ast[1])
285328 except TamsinParseError as e:
286329 return result
287330 elif ast[0] == 'LITERAL':
296339 raise NotImplementedError(repr(ast))
297340
298341
299 if __name__ == '__main__':
300 if sys.argv[1] == 'parse':
301 with codecs.open(sys.argv[2], 'r', 'UTF-8') as f:
342 def main(args):
343 global DEBUG
344 if args[0] == '--debug':
345 DEBUG = True
346 args = args[1:]
347 if args[0] == 'parse':
348 with codecs.open(args[1], 'r', 'UTF-8') as f:
302349 contents = f.read()
303350 parser = Parser(contents)
304351 ast = parser.grammar()
305352 print repr(ast)
306 elif sys.argv[1] == 'run':
307 with codecs.open(sys.argv[2], 'r', 'UTF-8') as f:
353 elif args[0] == 'run':
354 with codecs.open(args[1], 'r', 'UTF-8') as f:
308355 contents = f.read()
309356 parser = Parser(contents)
310357 ast = parser.grammar()
311358 #print repr(ast)
312359 interpreter = Interpreter(ast, sys.stdin.read())
313 result = interpreter.interpret(ast, {})
360 result = interpreter.interpret(ast)
314361 print interpreter.stringify_term(result)
315362 else:
316363 raise ValueError("first argument must be 'parse' or 'run'")
364
365
366 if __name__ == '__main__':
367 main(sys.argv[1:])
00 #!/bin/sh
11
2 falderal --substring-error README.md
2 falderal --substring-error README.markdown