Mercurial > lcfOS
diff python/pyyacc.py @ 194:b01429a5d695
Fixed test
author | Windel Bouwman |
---|---|
date | Wed, 29 May 2013 22:36:37 +0200 |
parents | 6cd6260789a1 |
children | 37ac6c016e0f |
line wrap: on
line diff
--- a/python/pyyacc.py Sun May 26 23:58:59 2013 +0200 +++ b/python/pyyacc.py Wed May 29 22:36:37 2013 +0200 @@ -28,14 +28,15 @@ self.productions = [] self._first = None # Cached first set - def add_production(self, name, symbols): + def add_production(self, name, symbols, f=None): """ Add a production rule to the grammar """ - production = Production(name, symbols) + production = Production(name, symbols, f) self.productions.append(production) if name in self.terminals: raise ParserGenerationException("Cannot redefine terminal {0}".format(name)) if not name in self.nonterminals: self.nonterminals.append(name) + self._first = None # Invalidate cached version def productionsForName(self, name): """ Retrieve all productions for a non terminal """ @@ -156,7 +157,7 @@ """ Checks no symbols are undefined """ for production in self.productions: for symbol in production.symbols: - if symbol not in self.Symbols: + if symbol not in self.Symbols + [EPS]: raise ParserGenerationException('Symbol {0} undefined'.format(symbol)) @@ -169,13 +170,31 @@ # First generate all item sets by using the nextItemset function: states, transitions = self.genCanonicalSet(iis) + + def action_str(act): + a, p = act + if a is SHIFT: + return 'Shift {0}'.format(0) + elif a is REDUCE: + return 'Reduce {0}'.format(p) + return 'Other' def setAction(state, t, action): key = (state, t) if key in action_table: action2 = action_table[key] if action != action2: - raise ParserGenerationException('LR construction conflict') + if (action2[0] == REDUCE) and (action[0] == SHIFT): + # Automatically resolve and do the shift action! + # Simple, but almost always what you want!! + action_table[key] = action + else: + if (action2[0] == SHIFT) and (action[0] == REDUCE): + pass + else: + a1 = action_str(action) + a2 = action_str(action2) + raise ParserGenerationException('LR construction conflict {0} vs {1}'.format(a1, a2)) else: action_table[key] = action @@ -204,9 +223,10 @@ class Production: """ Production rule for a grammar """ - def __init__(self, name, symbols): + def __init__(self, name, symbols, f=None): self.name = name self.symbols = symbols + self.f = f def __repr__(self): return '{0} -> {1}'.format(self.name, self.symbols) @@ -287,18 +307,26 @@ """ Parse an iterable with tokens """ assert hasattr(toks, '__iter__'), '{0} not iter type'.format(type(toks)) stack = [0] - look_ahead = toks.__next__() + try: + look_ahead = toks.__next__() + except StopIteration: + look_ahead = Token(EOF, EOF) assert type(look_ahead) is Token while True: state = stack[-1] # top of stack key = (state, look_ahead.typ) if not key in self.action_table: - raise Exception('Error parsing at character {0}'.format(look_ahead)) + raise ParserException('Error parsing at character {0}'.format(look_ahead)) action, param = self.action_table[key] if action == REDUCE: + #print('reduce', param) + f_args = [] for s in param.symbols: stack.pop() stack.pop() + f_args.append(0) + if param.f: + param.f(*f_args) state = stack[-1] stack.append(param.name) stack.append(self.goto_table[(state, param.name)]) @@ -308,34 +336,8 @@ try: look_ahead = toks.__next__() except StopIteration: - look_ahead = Token(EOF, EOF, 0) + look_ahead = Token(EOF, EOF) assert type(look_ahead) is Token elif action == ACCEPT: break - -def testSimpleGrammar(): - # 1. define a simple grammar: - g = Grammar(['EOF', 'identifier', '(', ')', '+', '*']) - g.add_production('input', ['expression']) - g.add_production('expression', ['term']) - g.add_production('expression', ['expression', '+', 'term']) - g.add_production('term', ['factor']) - g.add_production('term', ['term', '*', 'factor']) - g.add_production('factor', ['(', 'expression', ')']) - g.add_production('factor', ['identifier']) - g.start_symbol = 'input' - # 2. define input: - tokens = ['identifier', '+', 'identifier', '+', 'identifier'] - # 3. build parser: - p = g.genParser() - # 4. feed input: - def genTokens(lst): - for t in lst: - yield Token(t, t, 0) - p.parse(genTokens(tokens)) - - -if __name__ == '__main__': - testSimpleGrammar() -