682
|
1 |
#copyright ReportLab Inc. 2000
|
|
2 |
#see license.txt for license details
|
817
|
3 |
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/lib/yaml.py?cvsroot=reportlab
|
1683
|
4 |
#$Header: /tmp/reportlab/reportlab/lib/yaml.py,v 1.4 2002/07/24 19:56:37 andy_robinson Exp $
|
682
|
5 |
# parses "Yet Another Markup Language" into a list of tuples.
|
|
6 |
# Each tuple says what the data is e.g.
|
|
7 |
# ('Paragraph', 'Heading1', 'Why Reportlab Rules')
|
|
8 |
# and the pattern depends on type.
|
|
9 |
"""
|
|
10 |
.h1 Welcome to YAML!
|
|
11 |
YAML is "Yet Another Markup Language" - a markup language
|
|
12 |
which is easier to type in than XML, yet gives us a
|
|
13 |
reasonable selection of formats.
|
|
14 |
|
|
15 |
The general rule is that if a line begins with a '.',
|
|
16 |
it requires special processing. Otherwise lines
|
|
17 |
are concatenated to paragraphs, and blank lines
|
1683
|
18 |
separate paragraphs.
|
682
|
19 |
|
|
20 |
If the line ".foo bar bletch" is encountered,
|
|
21 |
it immediately ends and writes out any current
|
|
22 |
paragraph.
|
|
23 |
|
|
24 |
It then looks for a parser method called 'foo';
|
|
25 |
if found, it is called with arguments (bar, bletch).
|
|
26 |
|
|
27 |
If this is not found, it assumes that 'foo' is a
|
|
28 |
paragraph style, and the text for the first line
|
|
29 |
of the paragraph is 'bar bletch'. It would be
|
|
30 |
up to the formatter to decide whether on not 'foo'
|
|
31 |
was a valid paragraph.
|
|
32 |
|
|
33 |
Special commands understood at present are:
|
|
34 |
dot image filename
|
|
35 |
- adds the image to the document
|
|
36 |
dot beginPre Code
|
|
37 |
- begins a Preformatted object in style 'Code'
|
|
38 |
dot endPre
|
|
39 |
- ends a preformatted object.
|
|
40 |
"""
|
1683
|
41 |
__version__=''' $Id: yaml.py,v 1.4 2002/07/24 19:56:37 andy_robinson Exp $ '''
|
682
|
42 |
|
|
43 |
|
|
44 |
import sys
|
|
45 |
import string
|
|
46 |
|
|
47 |
#modes:
|
|
48 |
PLAIN = 1
|
|
49 |
PREFORMATTED = 2
|
|
50 |
|
|
51 |
BULLETCHAR = '\267' # assumes font Symbol, but works on all platforms
|
|
52 |
|
|
53 |
class BaseParser:
|
|
54 |
""""Simplest possible parser with only the most basic options.
|
|
55 |
|
|
56 |
This defines the line-handling abilities and basic mechanism.
|
|
57 |
The class YAMLParser includes capabilities for a fairly rich
|
|
58 |
story."""
|
1683
|
59 |
|
682
|
60 |
def __init__(self):
|
|
61 |
self.reset()
|
1683
|
62 |
|
682
|
63 |
def reset(self):
|
|
64 |
self._lineNo = 0
|
|
65 |
self._style = 'Normal' # the default
|
|
66 |
self._results = []
|
|
67 |
self._buf = []
|
|
68 |
self._mode = PLAIN
|
1683
|
69 |
|
682
|
70 |
def parseFile(self, filename):
|
|
71 |
#returns list of objects
|
|
72 |
data = open(filename, 'r').readlines()
|
1683
|
73 |
|
682
|
74 |
for line in data:
|
|
75 |
#strip trailing newlines
|
|
76 |
self.readLine(line[:-1])
|
|
77 |
self.endPara()
|
|
78 |
return self._results
|
|
79 |
|
|
80 |
def parseText(self, textBlock):
|
|
81 |
"Parses the a possible multi-line text block"
|
|
82 |
lines = string.split(textBlock, '\n')
|
|
83 |
for line in lines:
|
|
84 |
self.readLine(line)
|
|
85 |
self.endPara()
|
|
86 |
return self._results
|
1683
|
87 |
|
|
88 |
def readLine(self, line):
|
682
|
89 |
#this is the inner loop
|
|
90 |
self._lineNo = self._lineNo + 1
|
|
91 |
stripped = string.lstrip(line)
|
|
92 |
if len(stripped) == 0:
|
|
93 |
if self._mode == PLAIN:
|
|
94 |
self.endPara()
|
|
95 |
else: #preformatted, append it
|
|
96 |
self._buf.append(line)
|
|
97 |
elif line[0]=='.':
|
|
98 |
# we have a command of some kind
|
|
99 |
self.endPara()
|
|
100 |
words = string.split(stripped[1:])
|
|
101 |
cmd, args = words[0], words[1:]
|
1683
|
102 |
|
682
|
103 |
#is it a parser method?
|
|
104 |
if hasattr(self.__class__, cmd):
|
|
105 |
method = eval('self.'+cmd)
|
|
106 |
#this was very bad; any type error in the method was hidden
|
|
107 |
#we have to hack the traceback
|
|
108 |
try:
|
|
109 |
apply(method, tuple(args))
|
|
110 |
except TypeError, err:
|
|
111 |
sys.stderr.write("Parser method: apply(%s,%s) %s at line %d\n" % (cmd, tuple(args), err, self._lineNo))
|
|
112 |
raise
|
|
113 |
else:
|
|
114 |
# assume it is a paragraph style -
|
|
115 |
# becomes the formatter's problem
|
|
116 |
self.endPara() #end the last one
|
|
117 |
words = string.split(stripped, ' ', 1)
|
|
118 |
assert len(words)==2, "Style %s but no data at line %d" % (words[0], self._lineNo)
|
|
119 |
(styletag, data) = words
|
|
120 |
self._style = styletag[1:]
|
|
121 |
self._buf.append(data)
|
|
122 |
else:
|
|
123 |
#we have data, add to para
|
1683
|
124 |
self._buf.append(line)
|
682
|
125 |
|
|
126 |
def endPara(self):
|
|
127 |
#ends the current paragraph, or preformatted block
|
1683
|
128 |
|
682
|
129 |
text = string.join(self._buf, ' ')
|
|
130 |
if text:
|
|
131 |
if self._mode == PREFORMATTED:
|
|
132 |
#item 3 is list of lines
|
|
133 |
self._results.append(('PREFORMATTED', self._style,
|
|
134 |
string.join(self._buf,'\n')))
|
|
135 |
else:
|
|
136 |
self._results.append(('PARAGRAPH', self._style, text))
|
|
137 |
self._buf = []
|
|
138 |
self._style = 'Normal'
|
|
139 |
|
|
140 |
def beginPre(self, stylename):
|
|
141 |
self._mode = PREFORMATTED
|
|
142 |
self._style = stylename
|
1683
|
143 |
|
682
|
144 |
def endPre(self):
|
|
145 |
self.endPara()
|
|
146 |
self._mode = PLAIN
|
|
147 |
|
|
148 |
def image(self, filename):
|
|
149 |
self.endPara()
|
|
150 |
self._results.append(('IMAGE', filename))
|
|
151 |
|
|
152 |
|
|
153 |
class Parser(BaseParser):
|
|
154 |
"""This adds a basic set of "story" components compatible with HTML & PDF.
|
|
155 |
|
|
156 |
Images, spaces"""
|
|
157 |
|
|
158 |
def vSpace(self, points):
|
|
159 |
"""Inserts a vertical spacer"""
|
|
160 |
self._results.append(('VSpace', points))
|
1683
|
161 |
|
682
|
162 |
def pageBreak(self):
|
|
163 |
"""Inserts a frame break"""
|
|
164 |
self._results.append(('PageBreak','blah')) # must be a tuple
|
|
165 |
|
|
166 |
def custom(self, moduleName, funcName):
|
|
167 |
"""Goes and gets the Python object and adds it to the story"""
|
|
168 |
self.endPara()
|
|
169 |
self._results.append(('Custom',moduleName, funcName))
|
|
170 |
|
|
171 |
def nextPageTemplate(self, templateName):
|
|
172 |
self._results.append(('NextPageTemplate',templateName))
|
|
173 |
|
|
174 |
def parseFile(filename):
|
|
175 |
p = Parser()
|
|
176 |
return p.parseFile(filename)
|
|
177 |
|
|
178 |
def parseText(textBlock):
|
|
179 |
p = Parser()
|
|
180 |
return p.parseText(textBlock)
|
|
181 |
|
1683
|
182 |
|
682
|
183 |
if __name__=='__main__': #NORUNTESTS
|
|
184 |
if len(sys.argv) <> 2:
|
|
185 |
results = parseText(__doc__)
|
|
186 |
else:
|
|
187 |
results = parseFile(sys.argv[1])
|
|
188 |
import pprint
|
1683
|
189 |
pprint.pprint(results) |