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.

110 rivejä
3.4 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 __init__(self, name):
  13. self.name = name
  14. def __str__(self):
  15. return self.name
  16. def __repr__(self):
  17. return str(self)
  18. Shift = Action('Shift')
  19. Reduce = Action('Reduce')
  20. class ParseTable:
  21. def __init__(self, states, start_state, end_state):
  22. self.states = states
  23. self.start_state = start_state
  24. self.end_state = end_state
  25. class IntParseTable(ParseTable):
  26. @classmethod
  27. def from_ParseTable(cls, parse_table):
  28. enum = list(parse_table.states)
  29. state_to_idx = {s:i for i,s in enumerate(enum)}
  30. int_states = {}
  31. for s, la in parse_table.states.items():
  32. la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v
  33. for k,v in la.items()}
  34. int_states[ state_to_idx[s] ] = la
  35. start_state = state_to_idx[parse_table.start_state]
  36. end_state = state_to_idx[parse_table.end_state]
  37. return cls(int_states, start_state, end_state)
  38. class LALR_Analyzer(GrammarAnalyzer):
  39. def compute_lookahead(self):
  40. self.end_states = []
  41. self.states = {}
  42. def step(state):
  43. lookahead = defaultdict(list)
  44. sat, unsat = classify_bool(state, lambda rp: rp.is_satisfied)
  45. for rp in sat:
  46. for term in self.FOLLOW.get(rp.rule.origin, ()):
  47. lookahead[term].append((Reduce, rp.rule))
  48. d = classify(unsat, lambda rp: rp.next)
  49. for sym, rps in d.items():
  50. rps = {rp.advance(sym) for rp in rps}
  51. for rp in set(rps):
  52. if not rp.is_satisfied and not is_terminal(rp.next):
  53. rps |= self.expand_rule(rp.next)
  54. new_state = fzset(rps)
  55. lookahead[sym].append((Shift, new_state))
  56. if sym == '$END':
  57. self.end_states.append( new_state )
  58. yield new_state
  59. for k, v in lookahead.items():
  60. if len(v) > 1:
  61. if self.debug:
  62. logging.warn("Shift/reduce conflict for %s: %s. Resolving as shift.", k, v)
  63. for x in v:
  64. # XXX resolving shift/reduce into shift, like PLY
  65. # Give a proper warning
  66. if x[0] is Shift:
  67. lookahead[k] = [x]
  68. for k, v in lookahead.items():
  69. if not len(v) == 1:
  70. raise GrammarError("Collision in %s: %s" %(k, ', '.join(['\n * %s: %s' % x for x in v])))
  71. self.states[state] = {k:v[0] for k, v in lookahead.items()}
  72. for _ in bfs([self.start_state], step):
  73. pass
  74. self.end_state ,= self.end_states
  75. self._parse_table = ParseTable(self.states, self.start_state, self.end_state)
  76. if self.debug:
  77. self.parse_table = self._parse_table
  78. else:
  79. self.parse_table = IntParseTable.from_ParseTable(self._parse_table)