summaryrefslogtreecommitdiff
path: root/fsm.py
diff options
context:
space:
mode:
Diffstat (limited to 'fsm.py')
-rw-r--r--fsm.py174
1 files changed, 174 insertions, 0 deletions
diff --git a/fsm.py b/fsm.py
new file mode 100644
index 0000000..40f9c2d
--- /dev/null
+++ b/fsm.py
@@ -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()