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.

81 lines
2.7 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. ACTION_SHIFT = 0
  12. class LALR_Analyzer(GrammarAnalyzer):
  13. def compute_lookahead(self):
  14. self.end_states = []
  15. self.states = {}
  16. def step(state):
  17. lookahead = defaultdict(list)
  18. sat, unsat = classify_bool(state, lambda rp: rp.is_satisfied)
  19. for rp in sat:
  20. for term in self.FOLLOW.get(rp.rule.origin, ()):
  21. lookahead[term].append(('reduce', rp.rule))
  22. d = classify(unsat, lambda rp: rp.next)
  23. for sym, rps in d.items():
  24. rps = {rp.advance(sym) for rp in rps}
  25. for rp in set(rps):
  26. if not rp.is_satisfied and not is_terminal(rp.next):
  27. rps |= self.expand_rule(rp.next)
  28. new_state = fzset(rps)
  29. lookahead[sym].append(('shift', new_state))
  30. if sym == '$end':
  31. self.end_states.append( new_state )
  32. yield fzset(rps)
  33. for k, v in lookahead.items():
  34. if len(v) > 1:
  35. if self.debug:
  36. logging.warn("Shift/reduce conflict for %s: %s. Resolving as shift.", k, v)
  37. for x in v:
  38. # XXX resolving shift/reduce into shift, like PLY
  39. # Give a proper warning
  40. if x[0] == 'shift':
  41. lookahead[k] = [x]
  42. for k, v in lookahead.items():
  43. if not len(v) == 1:
  44. raise GrammarError("Collision in %s: %s" %(k, v))
  45. self.states[state] = {k:v[0] for k, v in lookahead.items()}
  46. for _ in bfs([self.init_state], step):
  47. pass
  48. self.end_state ,= self.end_states
  49. # --
  50. self.enum = list(self.states)
  51. self.enum_rev = {s:i for i,s in enumerate(self.enum)}
  52. self.states_idx = {}
  53. for s, la in self.states.items():
  54. la = {k:(ACTION_SHIFT, self.enum_rev[v[1]]) if v[0]=='shift'
  55. else (v[0], (v[1], len(v[1].expansion))) # Reduce
  56. for k,v in la.items()}
  57. self.states_idx[ self.enum_rev[s] ] = la
  58. self.init_state_idx = self.enum_rev[self.init_state]
  59. self.end_state_idx = self.enum_rev[self.end_state]