| 8 | |
| 9 | |
| 10 | class Parser: |
| 11 | # List of tuples (regex_str, lambda(regex_match, formatter)) |
| 12 | # If a lambda returns True it will be called repeatedly with replacement |
| 13 | # otherwise it will only be called on text that hasn't been parsed yet. |
| 14 | regexes = [ |
| 15 | # Code blocks of various formats |
| 16 | ('````(.+?)````', |
| 17 | lambda m, f: f.addCode(m.group(1)) |
| 18 | ), |
| 19 | ('```(.+?)```', |
| 20 | lambda m, f: f.addCode(m.group(1)) |
| 21 | ), |
| 22 | (r'((( {2})+)(\S.*)(\n\s*\n|\n))+', |
| 23 | lambda m, f: f.addCode(m.group(0)) |
| 24 | ), |
| 25 | (r'([^\.])\n', |
| 26 | lambda m, f: f.addRaw('{c} '.format(c=m.group(1))) or True |
| 27 | ), |
| 28 | ('`(.+?)`', |
| 29 | lambda m, f: f.addCode(m.group(1), True) |
| 30 | ), |
| 31 | # Make links clickable |
| 32 | ('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]' |
| 33 | r'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', |
| 34 | lambda m, f: f.addLink(m.group(0), m.group(0)) |
| 35 | ), |
| 36 | (r'\*\*(.+?)\*\*', |
| 37 | lambda m, f: f.addEmphasis(m.group(1), 2) |
| 38 | ), |
| 39 | (r'\*(.+?)\*', |
| 40 | lambda m, f: f.addEmphasis(m.group(1), 1) |
| 41 | ), |
| 42 | ] |
| 43 | |
| 44 | def __init__(self, text, formatter): |
| 45 | self.text = text |
| 46 | self.lines = [] |
| 47 | self.formatter = formatter |
| 48 | |
| 49 | def parseText(self): |
| 50 | UNPARSED = 0 |
| 51 | PARSED = 1 |
| 52 | parsed_block = [(UNPARSED, self.text)] |
| 53 | for regex, func in self.regexes: |
| 54 | index = 0 |
| 55 | while index < len(parsed_block): |
| 56 | label, text = parsed_block[index] |
| 57 | |
| 58 | # Already been parsed |
| 59 | if (label == PARSED): |
| 60 | index += 1 |
| 61 | continue |
| 62 | |
| 63 | match = re.search(regex, text) |
| 64 | if match: |
| 65 | parsed_block.pop(index) |
| 66 | start = match.start(0) |
| 67 | end = match.end(0) |
no test coverage detected
searching dependent graphs…