This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
3.3 KiB

  1. """This module builds a LALR(1) transition-table for lalr_parser.py
  2. For now, shift/reduce conflicts are automatically resolved as shifts.
  3. """
  4. # Author: Erez Shinan (2017)
  5. # Email : erezshin@gmail.com
  6. import logging
  7. from collections import defaultdict
  8. from ..utils import classify, classify_bool, bfs, fzset
  9. from ..common import GrammarError, is_terminal
  10. from .grammar_analysis import GrammarAnalyzer
  11. class Action:
  12. def __str__(self):
  13. return self.__name__
  14. def __repr__(self):
  15. return str(self)
  16. class Shift(Action): pass
  17. class Reduce(Action): pass
  18. class ParseTable:
  19. def __init__(self, states, start_state, end_state):
  20. self.states = states
  21. self.start_state = start_state
  22. self.end_state = end_state
  23. class IntParseTable(ParseTable):
  24. @classmethod
  25. def from_ParseTable(cls, parse_table):
  26. enum = list(parse_table.states)
  27. state_to_idx = {s:i for i,s in enumerate(enum)}
  28. int_states = {}
  29. for s, la in parse_table.states.items():
  30. la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v
  31. for k,v in la.items()}
  32. int_states[ state_to_idx[s] ] = la
  33. start_state = state_to_idx[parse_table.start_state]
  34. end_state = state_to_idx[parse_table.end_state]
  35. return cls(int_states, start_state, end_state)
  36. class LALR_Analyzer(GrammarAnalyzer):
  37. def compute_lookahead(self):
  38. self.end_states = []
  39. self.states = {}
  40. def step(state):
  41. lookahead = defaultdict(list)
  42. sat, unsat = classify_bool(state, lambda rp: rp.is_satisfied)
  43. for rp in sat:
  44. for term in self.FOLLOW.get(rp.rule.origin, ()):
  45. lookahead[term].append((Reduce, rp.rule))
  46. d = classify(unsat, lambda rp: rp.next)
  47. for sym, rps in d.items():
  48. rps = {rp.advance(sym) for rp in rps}
  49. for rp in set(rps):
  50. if not rp.is_satisfied and not is_terminal(rp.next):
  51. rps |= self.expand_rule(rp.next)
  52. new_state = fzset(rps)
  53. lookahead[sym].append((Shift, new_state))
  54. if sym == '$END':
  55. self.end_states.append( new_state )
  56. yield fzset(rps)
  57. for k, v in lookahead.items():
  58. if len(v) > 1:
  59. if self.debug:
  60. logging.warn("Shift/reduce conflict for %s: %s. Resolving as shift.", k, v)
  61. for x in v:
  62. # XXX resolving shift/reduce into shift, like PLY
  63. # Give a proper warning
  64. if x[0] is Shift:
  65. lookahead[k] = [x]
  66. for k, v in lookahead.items():
  67. if not len(v) == 1:
  68. raise GrammarError("Collision in %s: %s" %(k, v))
  69. self.states[state] = {k:v[0] for k, v in lookahead.items()}
  70. for _ in bfs([self.start_state], step):
  71. pass
  72. self.end_state ,= self.end_states
  73. self._parse_table = ParseTable(self.states, self.start_state, self.end_state)
  74. if self.debug:
  75. self.parse_table = self._parse_table
  76. else:
  77. self.parse_table = IntParseTable.from_ParseTable(self._parse_table)