import html import sys import urllib.parse class StackFSM(object): """ Implement a finite state machine that uses a stack to manage state. """ def __init__(self): self._state_stack = [] def _current_state(self): if len(self._state_stack) > 0: return self._state_stack[-1] return None def update(self): fn = self._current_state() if fn is not None: fn() def push_state(self, state): self._state_stack.append(state) def pop_state(self): return self._state_stack.pop() class Parser(object): def __init__(self, document, output=None): self._document = document if output is None: output = sys.stdout self._output = output self._offset = 0 self._blanks = 0 self._fsm = StackFSM() def parse(self): self._fsm.push_state(self.text_state) while self._fsm._current_state() is not None and len(self._document) > self._offset: self._fsm.update() def text_state(self): line = self._document[self._offset] if line.strip() == '': self._blanks += 1 else: self._blanks = 0 if line.startswith('```'): self._fsm.push_state(self.pre_state) self._output.write('
\n')
            self._offset += 1
        elif line.startswith('* '):
            self._fsm.push_state(self.list_state)
            self._output.write('
\n') self._offset += 1 else: self._output.write(line + '\n') self._offset += 1 def list_state(self): line = self._document[self._offset] if line.startswith('* '): self._output.write('
  • {}
  • \n'.format(html.escape(line[2:]))) self._offset += 1 else: self._fsm.pop_state() self._output.write('\n') def link_state(self): line = self._document[self._offset] if line.startswith('=>'): parts = line[2:].split(None, 1) url = parts[0] url_parts = urllib.parse.urlsplit(url) if url_parts.scheme in ('gemini', ''): external = '' else: external = open('external_link.svg').read() if len(parts) == 1: text = url else: text = html.escape(parts[1]) self._output.write('\n'.format(url, text, external)) self._offset += 1 else: self._fsm.pop_state() self._output.write('\n')