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.

142 lines
4.0 KiB

  1. import re
  2. import sys
  3. from .utils import get_regexp_width
  4. Py36 = (sys.version_info[:2] >= (3, 6))
  5. ###{standalone
  6. def is_terminal(sym):
  7. return sym.isupper()
  8. class GrammarError(Exception):
  9. pass
  10. class ParseError(Exception):
  11. pass
  12. class UnexpectedToken(ParseError):
  13. def __init__(self, token, expected, seq, index, considered_rules=None, state=None):
  14. self.token = token
  15. self.expected = expected
  16. self.line = getattr(token, 'line', '?')
  17. self.column = getattr(token, 'column', '?')
  18. self.considered_rules = considered_rules
  19. self.state = state
  20. try:
  21. context = ' '.join(['%r(%s)' % (t.value, t.type) for t in seq[index:index+5]])
  22. except AttributeError:
  23. context = seq[index:index+5]
  24. except TypeError:
  25. context = "<no context>"
  26. message = ("Unexpected token %r at line %s, column %s.\n"
  27. "Expected: %s\n"
  28. "Context: %s" % (token, self.line, self.column, expected, context))
  29. super(UnexpectedToken, self).__init__(message)
  30. def match_examples(self, parse_fn, examples):
  31. """ Given a parser instance and a dictionary mapping some label with
  32. some malformed syntax examples, it'll return the label for the
  33. example that bests matches the current error.
  34. """
  35. if not self.state:
  36. return None
  37. candidate = None
  38. for label,example in examples.items():
  39. if not isinstance(example, (tuple, list)):
  40. example = [example]
  41. for malformed in example:
  42. try:
  43. parse_fn(malformed)
  44. except UnexpectedToken as ut:
  45. if ut.state == self.state:
  46. if ut.token == self.token:
  47. return label
  48. elif not candidate:
  49. candidate = label
  50. except:
  51. pass
  52. return candidate
  53. ###}
  54. class LexerConf:
  55. def __init__(self, tokens, ignore=(), postlex=None, callbacks=None):
  56. self.tokens = tokens
  57. self.ignore = ignore
  58. self.postlex = postlex
  59. self.callbacks = callbacks or {}
  60. class ParserConf:
  61. def __init__(self, rules, callback, start):
  62. self.rules = rules
  63. self.callback = callback
  64. self.start = start
  65. class Pattern(object):
  66. def __init__(self, value, flags=()):
  67. self.value = value
  68. self.flags = frozenset(flags)
  69. def __repr__(self):
  70. return repr(self.to_regexp())
  71. # Pattern Hashing assumes all subclasses have a different priority!
  72. def __hash__(self):
  73. return hash((type(self), self.value, self.flags))
  74. def __eq__(self, other):
  75. return type(self) == type(other) and self.value == other.value and self.flags == other.flags
  76. if Py36:
  77. # Python 3.6 changed syntax for flags in regular expression
  78. def _get_flags(self, value):
  79. for f in self.flags:
  80. value = ('(?%s:%s)' % (f, value))
  81. return value
  82. else:
  83. def _get_flags(self, value):
  84. for f in self.flags:
  85. value = ('(?%s)' % f) + value
  86. return value
  87. class PatternStr(Pattern):
  88. def to_regexp(self):
  89. return self._get_flags(re.escape(self.value))
  90. @property
  91. def min_width(self):
  92. return len(self.value)
  93. max_width = min_width
  94. class PatternRE(Pattern):
  95. def to_regexp(self):
  96. return self._get_flags(self.value)
  97. @property
  98. def min_width(self):
  99. return get_regexp_width(self.to_regexp())[0]
  100. @property
  101. def max_width(self):
  102. return get_regexp_width(self.to_regexp())[1]
  103. class TokenDef(object):
  104. def __init__(self, name, pattern, priority=1):
  105. assert isinstance(pattern, Pattern), pattern
  106. self.name = name
  107. self.pattern = pattern
  108. self.priority = priority
  109. def __repr__(self):
  110. return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern)