# legalmd: Markdown-based legal markup # Copyright © 2019 Lee Yingtong Li (RunasSudo) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import re import mistletoe mistletoe.block_token.remove_token(mistletoe.block_token.BlockCode) class Heading(mistletoe.block_token.BlockToken): pattern = re.compile(r'(#{1,6})\s*([0-9A-Z]+)\s+(.+)') def __init__(self, match): self.level, self.label, content = match self.children = mistletoe.span_token.tokenize_inner(content) @classmethod def start(cls, line): return cls.pattern.match(line) @classmethod def read(cls, lines): line = next(lines) match = cls.pattern.match(line) level = len(match.group(1)) label = match.group(2) content = match.group(3) if content == '***': content = '★★★' return level, label, content mistletoe.block_token.remove_token(mistletoe.block_token.Heading) mistletoe.block_token.add_token(Heading) class Subrules(mistletoe.block_token.BlockToken): pattern = re.compile(r'\t*\(([0-9A-Za-z–]+)\)\s+.') def __init__(self, children): self.children = children @classmethod def start(cls, line): return cls.pattern.match(line) @classmethod def read(cls, lines): children = [] while True: lines.anchor() try: line = next(lines) except StopIteration: break if len(line.strip()) == 0: continue lines.reset() if not SubrulesItem.start(line): break children.append(SubrulesItem(*SubrulesItem.read(lines))) return children mistletoe.block_token.add_token(Subrules) class SubrulesItem(mistletoe.block_token.BlockToken): pattern = re.compile(r'(\t*)(\([0-9A-Za-z–]+\))?\s*(.+)') def __init__(self, level, label, children): self.level = level self.label = label self.children = children @classmethod def start(cls, line): match = cls.pattern.match(line) if not match.group(1) and not match.group(2): # Neither an indent nor a label return False return True @classmethod def read(cls, lines): line = next(lines) lead_in, label, content = cls.pattern.match(line).group(1, 2, 3) if content == '***': content = '★★★' level = len(lead_in) children = mistletoe.span_token.tokenize_inner(content) if label is None: level -= 1 return level, label, children class Note(mistletoe.block_token.BlockToken): pattern = re.compile(r'([0-9A-Z ]+):\s+(.+)') def __init__(self, match): self.label, content = match self.children = mistletoe.span_token.tokenize_inner(content) @classmethod def start(cls, line): return cls.pattern.match(line) @classmethod def read(cls, lines): line = next(lines) match = cls.pattern.match(line) label, content = match.group(1, 2) label = label[0].upper() + label[1:].lower() return label, content mistletoe.block_token.add_token(Note) class Definition(mistletoe.block_token.BlockToken): pattern = re.compile(r'\*\*\*(.+)\*\*\*(:|.*means)') def __init__(self, match): self.children = mistletoe.span_token.tokenize_inner(match) @classmethod def start(cls, line): return cls.pattern.match(line) @classmethod def read(cls, lines): return next(lines).rstrip('\n') mistletoe.block_token.add_token(Definition)