diff options
Diffstat (limited to 'fsm.py')
-rw-r--r-- | fsm.py | 174 |
1 files changed, 174 insertions, 0 deletions
@@ -0,0 +1,174 @@ +import sys + +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: + self._fsm.update() + + def text_state(self): + if len(self._document) <= self._offset: + self._fsm.pop_state() + return + line = self._document[self._offset] + if line.strip() == '': + self._blanks += 1 + else: + self._blanks = 0 + if line.strip() == '```': + self._fsm.pop_state() + self._fsm.push_state(self.pre_state) + self._output.write('<pre>\n') + self._offset += 1 + elif line.startswith('* '): + self._fsm.pop_state() + self._fsm.push_state(self.list_state) + self._output.write('<ul>\n') + elif line.startswith('=>'): + self._fsm.pop_state() + self._fsm.push_state(self.link_state) + self._output.write('<ul>\n') + else: + if line.startswith('# '): + self._output.write('<h1>{}</h1>\n'.format(line[2:])) + elif line.startswith('## '): + self._output.write('<h2>{}</h2>\n'.format(line[3:])) + elif line.startswith('### '): + self._output.write('<h3>{}</h3>\n'.format(line[4:])) + elif line.startswith('> '): + self._output.write('<blockquote>{}</blockquote>\n'.format(line[2:])) + elif line.strip() == '': + if self._blanks > 1: + self._output.write('<br/>\n') + else: + self._output.write('<p>{}</p>\n'.format(line)) + self._offset += 1 + + def pre_state(self): + if len(self._document) < self._offset: + self.pop_state() + return + line = self._document[self._offset] + if line.strip() == '```': + self._fsm.pop_state() + self._fsm.push_state(self.text_state) + self._output.write('</pre>\n') + self._offset += 1 + elif line.startswith('* '): + self._fsm.pop_state() + self._fsm.push_state(self.list_state) + self._output.write('<ul>\n') + elif line.startswith('=>'): + self._fsm.pop_state() + self._fsm.push_state(self.link_state) + self._output.write('<ul>\n') + else: + self._output.write(line + '\n') + self._offset += 1 + + def list_state(self): + if len(self._document) < self._offset: + self.pop_state() + return + line = self._document[self._offset] + if line.startswith('* '): + self._output.write('<li>{}</li>\n'.format(line[2:])) + self._offset += 1 + else: + self._fsm.pop_state() + self._fsm.push_state(self.text_state) + self._output.write('</ul>\n') + + def link_state(self): + if len(self._document) < self._offset: + self.pop_state() + return + line = self._document[self._offset] + if line.startswith('=>'): + parts = line[2:].split(None, 2) + if len(parts) == 1: + self._output.write('<li><a href="{}">{}</a></li>\n'.format(parts[0], parts[0])) + else: + self._output.write('<li><a href="{}">{}</a></li>\n'.format(parts[0], parts[1])) + self._offset += 1 + else: + self._fsm.pop_state() + self._fsm.push_state(self.text_state) + self._output.write('</ul>\n') + +document = """ +# h1 +hello +hello + +## h2 +``` +code +code +``` +### h3 +hello + + +hello + +### lists +* hello +* two +* three + +text + +* one +* two +* three + +### links +=>https://example.com hello +=> https://example.com two +=> https://example.com three + +text + +=>https://example.com +=> https://example.com +=> https://example.com +""" + +p = Parser(document.split('\n')) +p.parse() |