3617
|
1 |
#Copyright ReportLab Europe Ltd. 2000-2012
|
2963
|
2 |
#see license.txt for license details
|
|
3 |
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/yaml.py
|
|
4 |
# parses "Yet Another Markup Language" into a list of tuples.
|
|
5 |
# Each tuple says what the data is e.g.
|
|
6 |
# ('Paragraph', 'Heading1', 'Why Reportlab Rules')
|
|
7 |
# and the pattern depends on type.
|
|
8 |
"""
|
|
9 |
Parser for "Aaron's Markup Language" - a markup language
|
|
10 |
which is easier to type in than XML, yet gives us a
|
|
11 |
reasonable selection of formats.
|
|
12 |
|
|
13 |
The general rule is that if a line begins with a '.',
|
|
14 |
it requires special processing. Otherwise lines
|
|
15 |
are concatenated to paragraphs, and blank lines
|
|
16 |
separate paragraphs.
|
|
17 |
|
|
18 |
If the line ".foo bar bletch" is encountered,
|
|
19 |
it immediately ends and writes out any current
|
|
20 |
paragraph.
|
|
21 |
|
|
22 |
It then looks for a parser method called 'foo';
|
|
23 |
if found, it is called with arguments (bar, bletch).
|
|
24 |
|
|
25 |
If this is not found, it assumes that 'foo' is a
|
|
26 |
paragraph style, and the text for the first line
|
|
27 |
of the paragraph is 'bar bletch'. It would be
|
|
28 |
up to the formatter to decide whether on not 'foo'
|
|
29 |
was a valid paragraph.
|
|
30 |
|
|
31 |
Special commands understood at present are:
|
|
32 |
.image filename
|
|
33 |
- adds the image to the document
|
|
34 |
.beginPre Code
|
|
35 |
- begins a Preformatted object in style 'Code'
|
|
36 |
.endPre
|
|
37 |
- ends a preformatted object.
|
|
38 |
"""
|
|
39 |
|
|
40 |
|
|
41 |
import sys
|
|
42 |
import string
|
|
43 |
import imp
|
3721
|
44 |
from . import codegrab
|
2963
|
45 |
|
|
46 |
#modes:
|
|
47 |
PLAIN = 1
|
|
48 |
PREFORMATTED = 2
|
|
49 |
|
|
50 |
BULLETCHAR = '\267' # assumes font Symbol, but works on all platforms
|
|
51 |
|
|
52 |
class Parser:
|
|
53 |
def __init__(self):
|
|
54 |
self.reset()
|
|
55 |
|
|
56 |
def reset(self):
|
|
57 |
self._lineNo = 0
|
|
58 |
self._style = 'Normal' # the default
|
|
59 |
self._results = []
|
|
60 |
self._buf = []
|
|
61 |
self._mode = PLAIN
|
|
62 |
|
|
63 |
def parseFile(self, filename):
|
|
64 |
#returns list of objects
|
|
65 |
data = open(filename, 'r').readlines()
|
|
66 |
|
|
67 |
for line in data:
|
|
68 |
#strip trailing newlines
|
|
69 |
self.readLine(line[:-1])
|
|
70 |
self.endPara()
|
|
71 |
return self._results
|
|
72 |
|
|
73 |
def readLine(self, line):
|
|
74 |
#this is the inner loop
|
|
75 |
self._lineNo = self._lineNo + 1
|
|
76 |
stripped = string.lstrip(line)
|
|
77 |
if len(stripped) == 0:
|
|
78 |
if self._mode == PLAIN:
|
|
79 |
self.endPara()
|
|
80 |
else: #preformatted, append it
|
|
81 |
self._buf.append(line)
|
|
82 |
elif line[0]=='.':
|
|
83 |
# we have a command of some kind
|
|
84 |
self.endPara()
|
|
85 |
words = string.split(stripped[1:])
|
|
86 |
cmd, args = words[0], words[1:]
|
|
87 |
|
|
88 |
#is it a parser method?
|
|
89 |
if hasattr(self.__class__, cmd):
|
|
90 |
#this was very bad; any type error in the method was hidden
|
|
91 |
#we have to hack the traceback
|
|
92 |
try:
|
3326
|
93 |
getattr(self,cmd)(*args)
|
3721
|
94 |
except TypeError as err:
|
3326
|
95 |
sys.stderr.write("Parser method: %s(*%s) %s at line %d\n" % (cmd, tuple(args), err, self._lineNo))
|
2963
|
96 |
raise
|
|
97 |
else:
|
|
98 |
# assume it is a paragraph style -
|
|
99 |
# becomes the formatter's problem
|
|
100 |
self.endPara() #end the last one
|
|
101 |
words = string.split(stripped, ' ', 1)
|
|
102 |
assert len(words)==2, "Style %s but no data at line %d" % (words[0], self._lineNo)
|
|
103 |
(styletag, data) = words
|
|
104 |
self._style = styletag[1:]
|
|
105 |
self._buf.append(data)
|
|
106 |
else:
|
|
107 |
#we have data, add to para
|
|
108 |
self._buf.append(line)
|
|
109 |
|
|
110 |
def endPara(self):
|
|
111 |
#ends the current paragraph, or preformatted block
|
|
112 |
|
|
113 |
text = string.join(self._buf, ' ')
|
|
114 |
if text:
|
|
115 |
if self._mode == PREFORMATTED:
|
|
116 |
#item 3 is list of lines
|
|
117 |
self._results.append(('Preformatted', self._style,
|
|
118 |
string.join(self._buf,'\n')))
|
|
119 |
else:
|
|
120 |
self._results.append(('Paragraph', self._style, text))
|
|
121 |
self._buf = []
|
|
122 |
self._style = 'Normal'
|
|
123 |
|
|
124 |
def beginPre(self, stylename):
|
|
125 |
self._mode = PREFORMATTED
|
|
126 |
self._style = stylename
|
|
127 |
|
|
128 |
def endPre(self):
|
|
129 |
self.endPara()
|
|
130 |
self._mode = PLAIN
|
|
131 |
|
|
132 |
def image(self, filename):
|
|
133 |
self.endPara()
|
|
134 |
self._results.append(('Image', filename))
|
|
135 |
|
|
136 |
def vSpace(self, points):
|
|
137 |
"""Inserts a vertical spacer"""
|
|
138 |
self._results.append(('VSpace', points))
|
|
139 |
|
|
140 |
def pageBreak(self):
|
|
141 |
"""Inserts a frame break"""
|
|
142 |
self._results.append(('PageBreak','blah')) # must be a tuple
|
|
143 |
|
|
144 |
def custom(self, moduleName, funcName):
|
|
145 |
"""Goes and gets the Python object and adds it to the story"""
|
|
146 |
self.endPara()
|
|
147 |
self._results.append(('Custom',moduleName, funcName))
|
|
148 |
|
|
149 |
|
|
150 |
|
|
151 |
def getModuleDoc(self, modulename, pathname=None):
|
|
152 |
"""Documents the entire module at this point by making
|
|
153 |
paragraphs and preformatted objects"""
|
|
154 |
docco = codegrab.getObjectsDefinedIn(modulename, pathname)
|
3326
|
155 |
if docco.doc != None:
|
2963
|
156 |
self._results.append(('Paragraph', 'DocString', docco.doc))
|
|
157 |
if len(docco.functions) > 0:
|
|
158 |
for fn in docco.functions:
|
|
159 |
if fn.status == 'official':
|
|
160 |
self._results.append(('Preformatted','FunctionHeader', fn.proto))
|
|
161 |
self._results.append(('Preformatted','DocString', fn.doc))
|
|
162 |
|
|
163 |
if len(docco.classes) > 0:
|
|
164 |
for cls in docco.classes:
|
|
165 |
if cls.status == 'official':
|
|
166 |
self._results.append(('Preformatted','FunctionHeader', 'Class %s:' % cls.name))
|
|
167 |
self._results.append(('Preformatted','DocString', cls.doc))
|
|
168 |
for mth in cls.methods:
|
|
169 |
if mth.status == 'official':
|
|
170 |
self._results.append(('Preformatted','FunctionHeader', mth.proto))
|
|
171 |
self._results.append(('Preformatted','DocStringIndent', mth.doc))
|
|
172 |
|
|
173 |
|
|
174 |
def getClassDoc(self, modulename, classname, pathname=None):
|
|
175 |
"""Documents the class and its public methods"""
|
|
176 |
docco = codegrab.getObjectsDefinedIn(modulename, pathname)
|
|
177 |
found = 0
|
|
178 |
for cls in docco.classes:
|
|
179 |
if cls.name == classname:
|
|
180 |
found = 1
|
|
181 |
self._results.append(('Preformatted','FunctionHeader', 'Class %s:' % cls.name))
|
|
182 |
self._results.append(('Preformatted','DocString', cls.doc))
|
|
183 |
for mth in cls.methods:
|
|
184 |
if mth.status == 'official':
|
|
185 |
self._results.append(('Preformatted','FunctionHeader', mth.proto))
|
|
186 |
self._results.append(('Preformatted','DocStringIndent', mth.doc))
|
|
187 |
break
|
|
188 |
assert found, 'No Classes Defined in ' + modulename
|
|
189 |
|
|
190 |
def nextPageTemplate(self, templateName):
|
|
191 |
self._results.append(('NextPageTemplate',templateName))
|
|
192 |
|
|
193 |
if __name__=='__main__': #NORUNTESTS
|
3326
|
194 |
if len(sys.argv) != 2:
|
3721
|
195 |
print('usage: yaml.py source.txt')
|
2963
|
196 |
else:
|
|
197 |
p = Parser()
|
|
198 |
results = p.parseFile(sys.argv[1])
|
|
199 |
import pprint
|
3326
|
200 |
pprint.pprint(results)
|