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.

102 lines
2.7 KiB

  1. "Provides Indentation services for languages with indentation similar to Python"
  2. from abc import ABC, abstractmethod
  3. from .exceptions import LarkError
  4. from .lark import PostLex
  5. from .lexer import Token
  6. ###{standalone
  7. from typing import Tuple, List, Iterator, Optional
  8. class DedentError(LarkError):
  9. pass
  10. class Indenter(PostLex, ABC):
  11. def __init__(self) -> None:
  12. self.paren_level: Optional[int] = None
  13. self.indent_level: Optional[List[int]] = None
  14. assert self.tab_len > 0
  15. def handle_NL(self, token: Token) -> Iterator[Token]:
  16. if self.paren_level > 0:
  17. return
  18. yield token
  19. indent_str = token.rsplit('\n', 1)[1] # Tabs and spaces
  20. indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len
  21. if indent > self.indent_level[-1]:
  22. self.indent_level.append(indent)
  23. yield Token.new_borrow_pos(self.INDENT_type, indent_str, token)
  24. else:
  25. while indent < self.indent_level[-1]:
  26. self.indent_level.pop()
  27. yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token)
  28. if indent != self.indent_level[-1]:
  29. raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1]))
  30. def _process(self, stream):
  31. for token in stream:
  32. if token.type == self.NL_type:
  33. for t in self.handle_NL(token):
  34. yield t
  35. else:
  36. yield token
  37. if token.type in self.OPEN_PAREN_types:
  38. self.paren_level += 1
  39. elif token.type in self.CLOSE_PAREN_types:
  40. self.paren_level -= 1
  41. assert self.paren_level >= 0
  42. while len(self.indent_level) > 1:
  43. self.indent_level.pop()
  44. yield Token(self.DEDENT_type, '')
  45. assert self.indent_level == [0], self.indent_level
  46. def process(self, stream):
  47. self.paren_level = 0
  48. self.indent_level = [0]
  49. return self._process(stream)
  50. # XXX Hack for ContextualLexer. Maybe there's a more elegant solution?
  51. @property
  52. def always_accept(self):
  53. return (self.NL_type,)
  54. @property
  55. @abstractmethod
  56. def NL_type(self) -> str:
  57. ...
  58. @property
  59. @abstractmethod
  60. def OPEN_PAREN_types(self) -> List[str]:
  61. ...
  62. @property
  63. @abstractmethod
  64. def CLOSE_PAREN_types(self) -> List[str]:
  65. ...
  66. @property
  67. @abstractmethod
  68. def INDENT_type(self) -> str:
  69. ...
  70. @property
  71. @abstractmethod
  72. def DEDENT_type(self) -> str:
  73. ...
  74. @property
  75. @abstractmethod
  76. def tab_len(self) -> int:
  77. ...
  78. ###}