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.

119 lines
3.9 KiB

  1. # This module provide a LALR puppet, which is used to debugging and error handling
  2. from copy import copy
  3. from .lalr_analysis import Shift, Reduce
  4. from .. import Token
  5. from ..exceptions import UnexpectedToken
  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, parser_state, lexer_state):
  11. self.parser = parser
  12. self.parser_state = parser_state
  13. self.lexer_state = lexer_state
  14. def feed_token(self, token):
  15. """Feed the parser with a token, and advance it to the next state, as if it received it from the lexer.
  16. Note that ``token`` has to be an instance of ``Token``.
  17. """
  18. return self.parser_state.feed_token(token, token.type == '$END')
  19. def exhaust_lexer(self):
  20. """Try to feed the rest of the lexer state into the puppet.
  21. Note that this modifies the puppet in place and does not feed an '$END' Token"""
  22. for token in self.lexer_state.lex(self.parser_state):
  23. self.parser_state.feed_token(token)
  24. def feed_eof(self, last_token=None):
  25. """Feed a '$END' Token. Borrows from 'last_token' if given."""
  26. eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else Token('$END', '', 0, 1, 1)
  27. return self.feed_token(eof)
  28. def __copy__(self):
  29. """Create a new puppet with a separate state.
  30. Calls to feed_token() won't affect the old puppet, and vice-versa.
  31. """
  32. return type(self)(
  33. self.parser,
  34. copy(self.parser_state),
  35. copy(self.lexer_state),
  36. )
  37. def copy(self):
  38. return copy(self)
  39. def __eq__(self, other):
  40. if not isinstance(other, ParserPuppet):
  41. return False
  42. return self.parser_state == other.parser_state and self.lexer_state == other.lexer_state
  43. def as_immutable(self):
  44. p = copy(self)
  45. return ImmutableParserPuppet(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 = ["Puppet choices:"]
  49. for k, v in self.choices().items():
  50. out.append('\t- %s -> %s' % (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. accepts = set()
  61. for t in self.choices():
  62. if t.isupper(): # is terminal?
  63. new_puppet = copy(self)
  64. try:
  65. new_puppet.feed_token(Token(t, ''))
  66. except UnexpectedToken:
  67. pass
  68. else:
  69. accepts.add(t)
  70. return accepts
  71. def resume_parse(self):
  72. """Resume parsing from the current puppet state."""
  73. return self.parser.parse_from_state(self.parser_state)
  74. class ImmutableParserPuppet(ParserPuppet):
  75. result = None
  76. def __hash__(self):
  77. return hash((self.parser_state, self.lexer_state))
  78. def feed_token(self, token):
  79. c = copy(self)
  80. c.result = ParserPuppet.feed_token(c, token)
  81. return c
  82. def exhaust_lexer(self):
  83. """Try to feed the rest of the lexer state into the puppet.
  84. Note that this returns a new ImmutableParserPuppet and does not feed an '$END' Token"""
  85. res = copy(self)
  86. for token in res.lexer_state.lex(res.parser_state):
  87. res = res.parser_state.feed_token(token)
  88. return res