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.

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