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.3 KiB

  1. # This module provides a LALR interactive parser, which is used for debugging and error handling
  2. from copy import copy
  3. from .. import Token
  4. from ..exceptions import UnexpectedToken
  5. class InteractiveParser(object):
  6. """InteractiveParser gives you advanced control over parsing and error handling when parsing with LALR.
  7. For a simpler interface, see the ``on_error`` argument to ``Lark.parse()``.
  8. """
  9. def __init__(self, parser, parser_state, lexer_state):
  10. self.parser = parser
  11. self.parser_state = parser_state
  12. self.lexer_state = lexer_state
  13. def feed_token(self, token):
  14. """Feed the parser with a token, and advance it to the next state, as if it received it from the lexer.
  15. Note that ``token`` has to be an instance of ``Token``.
  16. """
  17. return self.parser_state.feed_token(token, token.type == '$END')
  18. def exhaust_lexer(self):
  19. """Try to feed the rest of the lexer state into the interactive parser.
  20. Note that this modifies the instance in place and does not feed an '$END' Token"""
  21. for token in self.lexer_state.lex(self.parser_state):
  22. self.parser_state.feed_token(token)
  23. def feed_eof(self, last_token=None):
  24. """Feed a '$END' Token. Borrows from 'last_token' if given."""
  25. eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else Token('$END', '', 0, 1, 1)
  26. return self.feed_token(eof)
  27. def __copy__(self):
  28. """Create a new interactive parser with a separate state.
  29. Calls to feed_token() won't affect the old instance, and vice-versa.
  30. """
  31. return type(self)(
  32. self.parser,
  33. copy(self.parser_state),
  34. copy(self.lexer_state),
  35. )
  36. def copy(self):
  37. return copy(self)
  38. def __eq__(self, other):
  39. if not isinstance(other, InteractiveParser):
  40. return False
  41. return self.parser_state == other.parser_state and self.lexer_state == other.lexer_state
  42. def as_immutable(self):
  43. """Convert to an ``ImmutableInteractiveParser``."""
  44. p = copy(self)
  45. return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_state)
  46. def pretty(self):
  47. """Print the output of ``choices()`` in a way that's easier to read."""
  48. out = ["Parser choices:"]
  49. for k, v in self.choices().items():
  50. out.append('\t- %s -> %r' % (k, v))
  51. out.append('stack size: %s' % len(self.parser_state.state_stack))
  52. return '\n'.join(out)
  53. def choices(self):
  54. """Returns a dictionary of token types, matched to their action in the parser.
  55. Only returns token types that are accepted by the current state.
  56. Updated by ``feed_token()``.
  57. """
  58. return self.parser_state.parse_conf.parse_table.states[self.parser_state.position]
  59. def accepts(self):
  60. """Returns the set of possible tokens that will advance the parser into a new valid state."""
  61. accepts = set()
  62. for t in self.choices():
  63. if t.isupper(): # is terminal?
  64. new_cursor = copy(self)
  65. try:
  66. new_cursor.feed_token(Token(t, ''))
  67. except UnexpectedToken:
  68. pass
  69. else:
  70. accepts.add(t)
  71. return accepts
  72. def resume_parse(self):
  73. """Resume automated parsing from the current state."""
  74. return self.parser.parse_from_state(self.parser_state)
  75. class ImmutableInteractiveParser(InteractiveParser):
  76. """Same as ``InteractiveParser``, but operations create a new instance instead
  77. of changing it in-place.
  78. """
  79. result = None
  80. def __hash__(self):
  81. return hash((self.parser_state, self.lexer_state))
  82. def feed_token(self, token):
  83. c = copy(self)
  84. c.result = InteractiveParser.feed_token(c, token)
  85. return c
  86. def exhaust_lexer(self):
  87. """Try to feed the rest of the lexer state into the parser.
  88. Note that this returns a new ImmutableInteractiveParser and does not feed an '$END' Token"""
  89. cursor = self.as_mutable()
  90. cursor.exhaust_lexer()
  91. return cursor.as_immutable()
  92. def as_mutable(self):
  93. """Convert to an ``InteractiveParser``."""
  94. p = copy(self)
  95. return InteractiveParser(p.parser, p.parser_state, p.lexer_state)