29 | 29 |
return result
|
30 | 30 |
|
31 | 31 |
def error(self, expected):
|
|
32 |
report = self.buffer[:20]
|
|
33 |
if len(self.buffer) > 20:
|
|
34 |
report += '...'
|
32 | 35 |
raise ValueError("Expected %s, found '%s' at '%s...'" %
|
33 | |
(expected, self.token, self.buffer[:20]))
|
|
36 |
(expected, self.token, report))
|
34 | 37 |
|
35 | 38 |
def scan(self):
|
36 | 39 |
self.scan_impl()
|
|
185 | 188 |
self.input = input_.split(' ')
|
186 | 189 |
self.position = 0
|
187 | 190 |
self.scan()
|
|
191 |
self.contexts = []
|
188 | 192 |
#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 ---------------------------------------- ###
|
189 | 229 |
|
190 | 230 |
def scan(self):
|
191 | 231 |
if self.position >= len(self.input):
|
|
198 | 238 |
self.position = position
|
199 | 239 |
self.token = self.input[position - 1]
|
200 | 240 |
|
|
241 |
### grammar stuff ---------------------------------------- ###
|
|
242 |
|
201 | 243 |
def find_production(self, name):
|
202 | 244 |
found = None
|
203 | 245 |
for x in self.program[1]:
|
|
208 | 250 |
raise ValueError("No '%s' production defined" % name)
|
209 | 251 |
return found
|
210 | 252 |
|
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."""
|
213 | 258 |
|
214 | 259 |
if ast[0] == 'ATOM':
|
215 | 260 |
return ast
|
216 | 261 |
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]])
|
218 | 263 |
elif ast[0] == 'VAR':
|
219 | |
return context[ast[1]] # context.get(ast[1], None)
|
|
264 |
return self.fetch(ast[1])
|
220 | 265 |
else:
|
221 | 266 |
raise NotImplementedError("internal error: bad term")
|
222 | 267 |
|
|
230 | 275 |
else:
|
231 | 276 |
raise NotImplementedError("internal error: bad term")
|
232 | 277 |
|
233 | |
def interpret(self, ast, context):
|
|
278 |
### interpreter proper ---------------------------------- ###
|
|
279 |
|
|
280 |
def interpret(self, ast):
|
234 | 281 |
if ast[0] == 'PROGRAM':
|
235 | |
return self.interpret(self.find_production('main'), context)
|
|
282 |
return self.interpret(self.find_production('main'))
|
236 | 283 |
elif ast[0] == 'PROD':
|
237 | 284 |
#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
|
239 | 289 |
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]))
|
242 | 291 |
if ast[2] is not None:
|
243 | 292 |
assert ast[2][0] == 'VAR', ast
|
244 | 293 |
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)
|
249 | 295 |
return result
|
250 | 296 |
elif ast[0] == 'SET':
|
251 | 297 |
assert ast[1][0] == 'VAR', ast
|
252 | 298 |
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)
|
258 | 301 |
return result
|
259 | 302 |
elif ast[0] == 'AND':
|
260 | 303 |
lhs = ast[1]
|
261 | 304 |
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)
|
264 | 307 |
return value_rhs
|
265 | 308 |
elif ast[0] == 'OR':
|
266 | 309 |
lhs = ast[1]
|
267 | 310 |
rhs = ast[2]
|
268 | |
saved_context = context.copy()
|
|
311 |
saved_context = self.current_context()
|
269 | 312 |
saved_position = self.position
|
270 | 313 |
try:
|
271 | |
return self.interpret(lhs, context)
|
|
314 |
return self.interpret(lhs)
|
272 | 315 |
except TamsinParseError as e:
|
273 | |
context = saved_context
|
|
316 |
self.install_context(saved_context)
|
274 | 317 |
self.rewind(saved_position)
|
275 | |
return self.interpret(rhs, context)
|
|
318 |
return self.interpret(rhs)
|
276 | 319 |
elif ast[0] == 'RETURN':
|
277 | |
return self.replace_vars(ast[1], context)
|
|
320 |
return self.replace_vars(ast[1])
|
278 | 321 |
elif ast[0] == 'FAIL':
|
279 | 322 |
raise TamsinParseError("fail")
|
280 | 323 |
elif ast[0] == 'WHILE':
|
281 | 324 |
result = ('ATOM', 'nil')
|
282 | 325 |
while True:
|
283 | 326 |
try:
|
284 | |
result = self.interpret(ast[1], context)
|
|
327 |
result = self.interpret(ast[1])
|
285 | 328 |
except TamsinParseError as e:
|
286 | 329 |
return result
|
287 | 330 |
elif ast[0] == 'LITERAL':
|
|
296 | 339 |
raise NotImplementedError(repr(ast))
|
297 | 340 |
|
298 | 341 |
|
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:
|
302 | 349 |
contents = f.read()
|
303 | 350 |
parser = Parser(contents)
|
304 | 351 |
ast = parser.grammar()
|
305 | 352 |
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:
|
308 | 355 |
contents = f.read()
|
309 | 356 |
parser = Parser(contents)
|
310 | 357 |
ast = parser.grammar()
|
311 | 358 |
#print repr(ast)
|
312 | 359 |
interpreter = Interpreter(ast, sys.stdin.read())
|
313 | |
result = interpreter.interpret(ast, {})
|
|
360 |
result = interpreter.interpret(ast)
|
314 | 361 |
print interpreter.stringify_term(result)
|
315 | 362 |
else:
|
316 | 363 |
raise ValueError("first argument must be 'parse' or 'run'")
|
|
364 |
|
|
365 |
|
|
366 |
if __name__ == '__main__':
|
|
367 |
main(sys.argv[1:])
|