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.

126 lines
3.9 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 pretty(self):
  72. """Print the output of ``choices()`` in a way that's easier to read."""
  73. out = ["Puppet choices:"]
  74. for k, v in self.choices().items():
  75. out.append('\t- %s -> %s' % (k, v))
  76. out.append('stack size: %s' % len(self._state_stack))
  77. return '\n'.join(out)
  78. def choices(self):
  79. """Returns a dictionary of token types, matched to their action in the parser.
  80. Only returns token types that are accepted by the current state.
  81. Updated by ``feed_token()``.
  82. """
  83. return self.parser.parse_table.states[self._state_stack[-1]]
  84. def accepts(self):
  85. accepts = set()
  86. for t in self.choices():
  87. new_puppet = self.copy()
  88. try:
  89. new_puppet.feed_token(Token(t, ''))
  90. except KeyError:
  91. pass
  92. else:
  93. accepts.add(t)
  94. return accepts
  95. def resume_parse(self):
  96. """Resume parsing from the current puppet state."""
  97. return self.parser.parse(
  98. self._stream, self._start, self._set_state,
  99. self._value_stack, self._state_stack
  100. )