@@ -7,9 +7,10 @@ from ..lexer import Token | |||
from ..utils import Enumerator, Serialize | |||
from .lalr_analysis import LALR_Analyzer, Shift, Reduce, IntParseTable | |||
from .lalr_puppet import ParserPuppet | |||
###{standalone | |||
class LALR_Parser(object): | |||
def __init__(self, parser_conf, debug=False): | |||
assert all(r.options.priority is None for r in parser_conf.rules), "LALR doesn't yet support prioritization" | |||
@@ -59,7 +60,11 @@ class _Parser: | |||
return states[state][token.type] | |||
except KeyError: | |||
expected = [s for s in states[state].keys() if s.isupper()] | |||
raise UnexpectedToken(token, expected, state=state, puppet=_ParserPuppet(self, state_stack, value_stack, start, stream, set_state)) | |||
try: | |||
puppet = ParserPuppet(self, state_stack, value_stack, start, stream, set_state) | |||
except NameError: | |||
puppet = None | |||
raise UnexpectedToken(token, expected, state=state, puppet=puppet) | |||
def reduce(rule): | |||
size = len(rule.expansion) | |||
@@ -112,58 +117,3 @@ class _Parser: | |||
###} | |||
class _ParserPuppet: | |||
def __init__(self, parser, state_stack, value_stack, start, stream, set_state): | |||
self.parser = parser | |||
self._state_stack = state_stack | |||
self._value_stack = value_stack | |||
self._start = start | |||
self._stream = stream | |||
self._set_state = set_state | |||
def feed_token(self, token): | |||
end_state = self.parser.parse_table.end_states[self._start] | |||
state_stack = self._state_stack | |||
value_stack = self._value_stack | |||
state = state_stack[-1] | |||
action, arg = self.parser.parse_table.states[state][token.type] | |||
assert arg != end_state | |||
while action is Reduce: | |||
rule = arg | |||
size = len(rule.expansion) | |||
if size: | |||
s = value_stack[-size:] | |||
del state_stack[-size:] | |||
del value_stack[-size:] | |||
else: | |||
s = [] | |||
value = self.parser.callbacks[rule](s) | |||
_action, new_state = self.parser.parse_table.states[state_stack[-1]][rule.origin.name] | |||
assert _action is Shift | |||
state_stack.append(new_state) | |||
value_stack.append(value) | |||
if state_stack[-1] == end_state: | |||
return value_stack[-1] | |||
state = state_stack[-1] | |||
action, arg = self.parser.parse_table.states[state][token.type] | |||
assert arg != end_state | |||
assert action is Shift | |||
state_stack.append(arg) | |||
value_stack.append(token) | |||
def choices(self): | |||
return self.parser.parse_table.states[self._state_stack[-1]] | |||
def resume_parse(self): | |||
return self.parser.parse(self._stream, self._start, self._set_state, self._value_stack, self._state_stack) |
@@ -0,0 +1,76 @@ | |||
# This module provide a LALR puppet, which is used to debugging and error handling | |||
from copy import deepcopy | |||
from .lalr_analysis import Shift, Reduce | |||
class ParserPuppet: | |||
def __init__(self, parser, state_stack, value_stack, start, stream, set_state): | |||
self.parser = parser | |||
self._state_stack = state_stack | |||
self._value_stack = value_stack | |||
self._start = start | |||
self._stream = stream | |||
self._set_state = set_state | |||
self.result = None | |||
def feed_token(self, token): | |||
end_state = self.parser.parse_table.end_states[self._start] | |||
state_stack = self._state_stack | |||
value_stack = self._value_stack | |||
state = state_stack[-1] | |||
action, arg = self.parser.parse_table.states[state][token.type] | |||
assert arg != end_state | |||
while action is Reduce: | |||
rule = arg | |||
size = len(rule.expansion) | |||
if size: | |||
s = value_stack[-size:] | |||
del state_stack[-size:] | |||
del value_stack[-size:] | |||
else: | |||
s = [] | |||
value = self.parser.callbacks[rule](s) | |||
_action, new_state = self.parser.parse_table.states[state_stack[-1]][rule.origin.name] | |||
assert _action is Shift | |||
state_stack.append(new_state) | |||
value_stack.append(value) | |||
if state_stack[-1] == end_state: | |||
self.result = value_stack[-1] | |||
return self.result | |||
state = state_stack[-1] | |||
action, arg = self.parser.parse_table.states[state][token.type] | |||
assert arg != end_state | |||
assert action is Shift | |||
state_stack.append(arg) | |||
value_stack.append(token) | |||
def copy(self): | |||
return type(self)( | |||
self.parser, | |||
list(self._state_stack), | |||
deepcopy(self._value_stack), | |||
self._start, | |||
self._stream, | |||
self._set_state, | |||
) | |||
def pretty(): | |||
print("Puppet choices:") | |||
for k, v in self.choices.items(): | |||
print('\t-', k, '->', v) | |||
print('stack size:', len(self._state_stack)) | |||
def choices(self): | |||
return self.parser.parse_table.states[self._state_stack[-1]] | |||
def resume_parse(self): | |||
return self.parser.parse(self._stream, self._start, self._set_state, self._value_stack, self._state_stack) |
@@ -105,7 +105,7 @@ class Tree(object): | |||
stack.append(n) | |||
def __deepcopy__(self, memo): | |||
return type(self)(self.data, deepcopy(self.children, memo)) | |||
return type(self)(self.data, deepcopy(self.children, memo), meta=self._meta) | |||
def copy(self): | |||
return type(self)(self.data, self.children) | |||
@@ -50,6 +50,10 @@ class TestStandalone(TestCase): | |||
x = l.parse('16 candles') | |||
self.assertEqual(x.children, ['16', 'candles']) | |||
self.assertRaises(context['UnexpectedToken'], l.parse, 'twelve monkeys') | |||
self.assertRaises(context['UnexpectedToken'], l.parse, 'twelve') | |||
self.assertRaises(context['UnexpectedCharacters'], l.parse, '$ talks') | |||
def test_contextual(self): | |||
grammar = """ | |||
start: a b | |||