faster: Extract table builder
This commit is contained in:
parent
d60566c5a4
commit
7ea47075ea
1 changed files with 76 additions and 40 deletions
116
parser_faster.py
116
parser_faster.py
|
|
@ -62,6 +62,74 @@ class Configuration(
|
||||||
lookahead=la,
|
lookahead=la,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class TableBuilder(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.errors = []
|
||||||
|
self.table = []
|
||||||
|
self.row = None
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
self._flush_row()
|
||||||
|
if len(self.errors) > 0:
|
||||||
|
raise ValueError("\n\n".join(self.errors))
|
||||||
|
return self.table
|
||||||
|
|
||||||
|
def new_row(self, config_set):
|
||||||
|
self._flush_row()
|
||||||
|
self.row = {}
|
||||||
|
self.current_config_set = config_set
|
||||||
|
|
||||||
|
def _flush_row(self):
|
||||||
|
if self.row:
|
||||||
|
actions = {k: v[0] for k,v in self.row.items()}
|
||||||
|
self.table.append(actions)
|
||||||
|
|
||||||
|
|
||||||
|
def set_table_reduce(self, symbol, config):
|
||||||
|
action = ('reduce', config.name, len(config.symbols))
|
||||||
|
self._set_table_action(symbol, action, config)
|
||||||
|
|
||||||
|
def set_table_accept(self, config):
|
||||||
|
action = ('accept',)
|
||||||
|
self._set_table_action('$', action, config)
|
||||||
|
|
||||||
|
def set_table_shift(self, index, config):
|
||||||
|
action = ('shift', index)
|
||||||
|
self._set_table_action(config.next, action, config)
|
||||||
|
|
||||||
|
def set_table_goto(self, symbol, index):
|
||||||
|
action = ('goto', index)
|
||||||
|
self._set_table_action(symbol, action, None)
|
||||||
|
|
||||||
|
def _set_table_action(self, symbol, action, config):
|
||||||
|
"""Set the action for 'symbol' in the table row to 'action'.
|
||||||
|
|
||||||
|
This is destructive; it changes the table. It raises an error if
|
||||||
|
there is already an action for the symbol in the row.
|
||||||
|
"""
|
||||||
|
existing, existing_config = self.row.get(symbol, (None, None))
|
||||||
|
if existing is not None and existing != action:
|
||||||
|
config_old = str(existing_config)
|
||||||
|
config_new = str(config)
|
||||||
|
max_len = max(len(config_old), len(config_new)) + 1
|
||||||
|
error = (
|
||||||
|
"Conflicting actions for token '{symbol}':\n"
|
||||||
|
" {config_old: <{max_len}}: {old}\n"
|
||||||
|
" {config_new: <{max_len}}: {new}\n".format(
|
||||||
|
config_old=config_old,
|
||||||
|
config_new=config_new,
|
||||||
|
max_len=max_len,
|
||||||
|
old=existing,
|
||||||
|
new=action,
|
||||||
|
symbol=symbol,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.errors.append(error)
|
||||||
|
self.row[symbol] = (action, config)
|
||||||
|
|
||||||
|
def get_table_action(self, symbol):
|
||||||
|
return self.row[symbol][0]
|
||||||
|
|
||||||
|
|
||||||
class GenerateLR0(object):
|
class GenerateLR0(object):
|
||||||
"""Generate parser tables for an LR0 parser.
|
"""Generate parser tables for an LR0 parser.
|
||||||
|
|
@ -281,68 +349,36 @@ class GenerateLR0(object):
|
||||||
|
|
||||||
Anything missing from the row indicates an error.
|
Anything missing from the row indicates an error.
|
||||||
"""
|
"""
|
||||||
errors = []
|
builder = TableBuilder()
|
||||||
action_table = []
|
|
||||||
config_sets = self.gen_all_sets()
|
config_sets = self.gen_all_sets()
|
||||||
for config_set in config_sets:
|
for config_set in config_sets:
|
||||||
actions = {}
|
builder.new_row(config_set)
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
for config in config_set:
|
for config in config_set:
|
||||||
if config.at_end:
|
if config.at_end:
|
||||||
if config.name != '__start':
|
if config.name != '__start':
|
||||||
for a in self.gen_reduce_set(config):
|
for a in self.gen_reduce_set(config):
|
||||||
self.set_table_action(
|
builder.set_table_reduce(a, config)
|
||||||
errors,
|
|
||||||
actions,
|
|
||||||
a,
|
|
||||||
('reduce', config.name, len(config.symbols)),
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.set_table_action(
|
builder.set_table_accept(config)
|
||||||
errors,
|
|
||||||
actions,
|
|
||||||
'$',
|
|
||||||
('accept',),
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if config.next in self.terminals:
|
if config.next in self.terminals:
|
||||||
successor = self.gen_successor(config_set, config.next)
|
successor = self.gen_successor(config_set, config.next)
|
||||||
index = self.find_set_index(config_sets, successor)
|
index = self.find_set_index(config_sets, successor)
|
||||||
self.set_table_action(
|
builder.set_table_shift(index, config)
|
||||||
errors,
|
|
||||||
actions,
|
|
||||||
config.next,
|
|
||||||
('shift', index),
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Gotos
|
# Gotos
|
||||||
for symbol in self.nonterminals:
|
for symbol in self.nonterminals:
|
||||||
successor = self.gen_successor(config_set, symbol)
|
successor = self.gen_successor(config_set, symbol)
|
||||||
index = self.find_set_index(config_sets, successor)
|
index = self.find_set_index(config_sets, successor)
|
||||||
if index is not None:
|
if index is not None:
|
||||||
self.set_table_action(
|
builder.set_table_goto(symbol, index)
|
||||||
errors,
|
|
||||||
actions,
|
|
||||||
symbol,
|
|
||||||
('goto', index),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# set_table_action stores the configs that generated the actions in
|
|
||||||
# the table, for diagnostic purposes. This filters them out again
|
|
||||||
# so that the parser has something clean to work with.
|
|
||||||
actions = {k: self.get_table_action(actions, k) for k in actions}
|
|
||||||
action_table.append(actions)
|
|
||||||
|
|
||||||
if len(errors) > 0:
|
return builder.flush()
|
||||||
raise ValueError("\n\n".join(errors))
|
|
||||||
|
|
||||||
return action_table
|
|
||||||
|
|
||||||
def set_table_action(self, errors, row, symbol, action, config):
|
def set_table_action(self, errors, row, symbol, action, config):
|
||||||
"""Set the action for 'symbol' in the table row to 'action'.
|
"""Set the action for 'symbol' in the table row to 'action'.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue