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.

117 lines
4.0 KiB

  1. from .utils import STRING_TYPE
  2. ###{standalone
  3. class LarkError(Exception):
  4. pass
  5. class GrammarError(LarkError):
  6. pass
  7. class ParseError(LarkError):
  8. pass
  9. class LexError(LarkError):
  10. pass
  11. class UnexpectedEOF(ParseError):
  12. def __init__(self, expected):
  13. self.expected = expected
  14. message = ("Unexpected end-of-input. Expected one of: \n\t* %s\n" % '\n\t* '.join(x.name for x in self.expected))
  15. super(UnexpectedEOF, self).__init__(message)
  16. class UnexpectedInput(LarkError):
  17. pos_in_stream = None
  18. def get_context(self, text, span=40):
  19. pos = self.pos_in_stream
  20. start = max(pos - span, 0)
  21. end = pos + span
  22. before = text[start:pos].rsplit('\n', 1)[-1]
  23. after = text[pos:end].split('\n', 1)[0]
  24. return before + after + '\n' + ' ' * len(before) + '^\n'
  25. def match_examples(self, parse_fn, examples):
  26. """ Given a parser instance and a dictionary mapping some label with
  27. some malformed syntax examples, it'll return the label for the
  28. example that bests matches the current error.
  29. """
  30. assert self.state is not None, "Not supported for this exception"
  31. candidate = (None, False)
  32. for label, example in examples.items():
  33. assert not isinstance(example, STRING_TYPE)
  34. for malformed in example:
  35. try:
  36. parse_fn(malformed)
  37. except UnexpectedInput as ut:
  38. if ut.state == self.state:
  39. try:
  40. if ut.token == self.token: # Try exact match first
  41. return label
  42. if (ut.token.type == self.token.type) and not candidate[-1]: # Fallback to token types match
  43. candidate = label, True
  44. except AttributeError:
  45. pass
  46. if not candidate[0]:
  47. candidate = label, False
  48. return candidate[0]
  49. class UnexpectedCharacters(LexError, UnexpectedInput):
  50. def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None):
  51. message = "No terminal defined for '%s' at line %d col %d" % (seq[lex_pos], line, column)
  52. self.line = line
  53. self.column = column
  54. self.allowed = allowed
  55. self.considered_tokens = considered_tokens
  56. self.pos_in_stream = lex_pos
  57. self.state = state
  58. message += '\n\n' + self.get_context(seq)
  59. if allowed:
  60. message += '\nExpecting: %s\n' % allowed
  61. if token_history:
  62. message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in token_history)
  63. super(UnexpectedCharacters, self).__init__(message)
  64. class UnexpectedToken(ParseError, UnexpectedInput):
  65. def __init__(self, token, expected, considered_rules=None, state=None):
  66. self.token = token
  67. self.expected = expected # XXX str shouldn't necessary
  68. self.line = getattr(token, 'line', '?')
  69. self.column = getattr(token, 'column', '?')
  70. self.considered_rules = considered_rules
  71. self.state = state
  72. self.pos_in_stream = getattr(token, 'pos_in_stream', None)
  73. message = ("Unexpected token %r at line %s, column %s.\n"
  74. "Expected one of: \n\t* %s\n"
  75. % (token, self.line, self.column, '\n\t* '.join(self.expected)))
  76. super(UnexpectedToken, self).__init__(message)
  77. class VisitError(LarkError):
  78. """VisitError is raised when visitors are interrupted by an exception
  79. It provides the following attributes for inspection:
  80. - obj: the tree node or token it was processing when the exception was raised
  81. - orig_exc: the exception that cause it to fail
  82. """
  83. def __init__(self, rule, obj, orig_exc):
  84. self.obj = obj
  85. self.orig_exc = orig_exc
  86. message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc)
  87. super(VisitError, self).__init__(message)
  88. ###}