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.

129 lines
4.0 KiB

  1. # This module provide a LALR puppet, which is used to debugging and error handling
  2. from copy import deepcopy
  3. from .lalr_analysis import Shift, Reduce
  4. from .. import Token
  5. class ParserPuppet(object):
  6. """ParserPuppet gives you advanced control over error handling when parsing with LALR.
  7. For a simpler, more streamlined interface, see the ``on_error`` argument to ``Lark.parse()``.
  8. """
  9. def __init__(self, parser, state_stack, value_stack, start, stream, set_state):
  10. self.parser = parser
  11. self._state_stack = state_stack
  12. self._value_stack = value_stack
  13. self._start = start
  14. self._stream = stream
  15. self._set_state = set_state
  16. self.result = None
  17. def feed_token(self, token):
  18. """Feed the parser with a token, and advance it to the next state, as if it recieved it from the lexer.
  19. Note that ``token`` has to be an instance of ``Token``.
  20. """
  21. end_state = self.parser.parse_table.end_states[self._start]
  22. state_stack = self._state_stack
  23. value_stack = self._value_stack
  24. state = state_stack[-1]
  25. action, arg = self.parser.parse_table.states[state][token.type]
  26. assert arg != end_state
  27. while action is Reduce:
  28. rule = arg
  29. size = len(rule.expansion)
  30. if size:
  31. s = value_stack[-size:]
  32. del state_stack[-size:]
  33. del value_stack[-size:]
  34. else:
  35. s = []
  36. value = self.parser.callbacks[rule](s)
  37. _action, new_state = self.parser.parse_table.states[state_stack[-1]][rule.origin.name]
  38. assert _action is Shift
  39. state_stack.append(new_state)
  40. value_stack.append(value)
  41. if state_stack[-1] == end_state:
  42. self.result = value_stack[-1]
  43. return self.result
  44. state = state_stack[-1]
  45. action, arg = self.parser.parse_table.states[state][token.type]
  46. assert arg != end_state
  47. assert action is Shift
  48. state_stack.append(arg)
  49. value_stack.append(token)
  50. def copy(self):
  51. """Create a new puppet with a separate state.
  52. Calls to feed_token() won't affect the old puppet, and vice-versa.
  53. """
  54. return type(self)(
  55. self.parser,
  56. list(self._state_stack),
  57. deepcopy(self._value_stack),
  58. self._start,
  59. self._stream,
  60. self._set_state,
  61. )
  62. def __eq__(self, other):
  63. if not isinstance(other, ParserPuppet):
  64. return False
  65. return (
  66. self._state_stack == other._state_stack and
  67. self._value_stack == other._value_stack and
  68. self._stream == other._stream and
  69. self._start == other._start
  70. )
  71. def __hash__(self):
  72. return hash((tuple(self._state_stack), self._start))
  73. def pretty(self):
  74. """Print the output of ``choices()`` in a way that's easier to read."""
  75. out = ["Puppet choices:"]
  76. for k, v in self.choices().items():
  77. out.append('\t- %s -> %s' % (k, v))
  78. out.append('stack size: %s' % len(self._state_stack))
  79. return '\n'.join(out)
  80. def choices(self):
  81. """Returns a dictionary of token types, matched to their action in the parser.
  82. Only returns token types that are accepted by the current state.
  83. Updated by ``feed_token()``.
  84. """
  85. return self.parser.parse_table.states[self._state_stack[-1]]
  86. def accepts(self):
  87. accepts = set()
  88. for t in self.choices():
  89. new_puppet = self.copy()
  90. try:
  91. new_puppet.feed_token(Token(t, ''))
  92. except KeyError:
  93. pass
  94. else:
  95. accepts.add(t)
  96. return accepts
  97. def resume_parse(self):
  98. """Resume parsing from the current puppet state."""
  99. return self.parser.parse(
  100. self._stream, self._start, self._set_state,
  101. self._value_stack, self._state_stack
  102. )