blob: c8c4e8505c4712d7e41cd2b7054f9a2cc96fc7d3 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
import html
import sys
import urllib.parse
from util import resource_path
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('<pre>\n')
self._offset += 1
elif line.startswith('* '):
self._fsm.push_state(self.list_state)
self._output.write('<ul>\n')
elif line.startswith('=>'):
self._fsm.push_state(self.link_state)
self._output.write('<ul>\n')
else:
if line.startswith('# '):
self._output.write('<h1>{}</h1>\n'.format(html.escape(line[2:])))
elif line.startswith('## '):
self._output.write('<h2>{}</h2>\n'.format(html.escape(line[3:])))
elif line.startswith('### '):
self._output.write('<h3>{}</h3>\n'.format(html.escape(line[4:])))
elif line.startswith('> '):
self._output.write('<blockquote>{}</blockquote>\n'.format(html.escape(line[2:])))
elif line.strip() == '':
if self._blanks > 1:
self._output.write('<br/>\n')
else:
self._output.write('<p>{}</p>\n'.format(html.escape(line)))
self._offset += 1
def pre_state(self):
line = self._document[self._offset]
if line.startswith('```'):
self._fsm.pop_state()
self._output.write('</pre>\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('<li>{}</li>\n'.format(html.escape(line[2:])))
self._offset += 1
else:
self._fsm.pop_state()
self._output.write('</ul>\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(resource_path('resources/external_link.svg')).read()
if len(parts) == 1:
text = url
else:
text = html.escape(parts[1])
self._output.write('<li class="link"><a href="{}">{}</a>{}</li>\n'.format(url, text, external))
self._offset += 1
else:
self._fsm.pop_state()
self._output.write('</ul>\n')
|