from typing import Callable, List, Optional class TemplateParserException(Exception): def __init__(self, message): # type: (str) -> None self.message = message def __str__(self): # type: () -> str return self.message class TokenizationException(Exception): def __init__(self, message, line_content=None): # type: (str, str) -> None self.message = message self.line_content = line_content class TokenizerState: def __init__(self): # type: () -> None self.i = 0 self.line = 1 self.col = 1 class Token: def __init__(self, kind, s, tag, line, col, line_span): # type: (str, str, str, int, int, int) -> None self.kind = kind self.s = s self.tag = tag self.line = line self.col = col self.line_span = line_span def tokenize(text): # type: (str) -> List[Token] def advance(n): # type: (int) -> None for _ in range(n): state.i += 1 if state.i >= 0 and text[state.i - 1] == '\n': state.line += 1 state.col = 1 else: state.col += 1 def looking_at(s): # type: (str) -> bool return text[state.i:state.i+len(s)] == s def looking_at_htmlcomment(): # type: () -> bool return looking_at("': return text[i:end] if not unclosed_end and text[end] == '<': unclosed_end = end end += 1 raise TokenizationException('Unclosed comment', text[i:unclosed_end]) def get_handlebar_comment(text, i): # type: (str, int) -> str end = i + 5 unclosed_end = 0 while end <= len(text): if text[end-2:end] == '}}': return text[i:end] if not unclosed_end and text[end] == '<': unclosed_end = end end += 1 raise TokenizationException('Unclosed comment', text[i:unclosed_end]) def get_django_comment(text, i): # type: (str, int) -> str end = i + 4 unclosed_end = 0 while end <= len(text): if text[end-2:end] == '#}': return text[i:end] if not unclosed_end and text[end] == '<': unclosed_end = end end += 1 raise TokenizationException('Unclosed comment', text[i:unclosed_end])