import re class DirectiveParser: name = 'directive' @staticmethod def parse_type(m: re.Match): raise NotImplementedError() @staticmethod def parse_title(m: re.Match): raise NotImplementedError() @staticmethod def parse_content(m: re.Match): raise NotImplementedError() @classmethod def parse_tokens(cls, block, text, state): if state.depth() >= block.max_nested_level - 1 and cls.name in block.rules: rules = list(block.rules) rules.remove(cls.name) else: rules = block.rules child = state.child_state(text) block.parse(child, rules) return child.tokens @staticmethod def parse_options(m: re.Match): text = m.group('options') if not text.strip(): return [] options = [] for line in re.split(r'\n+', text): line = line.strip()[1:] if not line: continue i = line.find(':') k = line[:i] v = line[i + 1:].strip() options.append((k, v)) return options class BaseDirective: parser = DirectiveParser directive_pattern = None def __init__(self, plugins): self._methods = {} self.__plugins = plugins def register(self, name, fn): self._methods[name] = fn def parse_method(self, block, m, state): _type = self.parser.parse_type(m) method = self._methods.get(_type) if method: try: token = method(block, m, state) except ValueError as e: token = {'type': 'block_error', 'raw': str(e)} else: text = m.group(0) token = { 'type': 'block_error', 'raw': text, } if isinstance(token, list): for tok in token: state.append_token(tok) else: state.append_token(token) return token def parse_directive(self, block, m, state): raise NotImplementedError() def register_block_parser(self, md, before=None): md.block.register( self.parser.name, self.directive_pattern, self.parse_directive, before=before, ) def __call__(self, md): for plugin in self.__plugins: plugin.parser = self.parser plugin(self, md) class DirectivePlugin: def __init__(self): self.parser = None def parse_options(self, m: re.Match): return self.parser.parse_options(m) def parse_type(self, m: re.Match): return self.parser.parse_type(m) def parse_title(self, m: re.Match): return self.parser.parse_title(m) def parse_content(self, m: re.Match): return self.parser.parse_content(m) def parse_tokens(self, block, text, state): return self.parser.parse_tokens(block, text, state) def parse(self, block, m, state): raise NotImplementedError() def __call__(self, md): raise NotImplementedError()