Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/Formatter/DocBook.py
1| # $Id: DocBook.py,v 1.10 2003/02/02 00:22:44 chalky Exp $
2| #
3| # This file is a part of Synopsis.
4| # Copyright (C) 2000, 2001 Stefan Seefeld
5| #
6| # Synopsis is free software; you can redistribute it and/or modify it
7| # under the terms of the GNU General Public License as published by
8| # the Free Software Foundation; either version 2 of the License, or
9| # (at your option) any later version.
10| #
11| # This program is distributed in the hope that it will be useful,
12| # but WITHOUT ANY WARRANTY; without even the implied warranty of
13| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14| # General Public License for more details.
15| #
16| # You should have received a copy of the GNU General Public License
17| # along with this program; if not, write to the Free Software
18| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19| # 02111-1307, USA.
20| #
21| # $Log: DocBook.py,v $
22| # Revision 1.10 2003/02/02 00:22:44 chalky
23| # Remove obsolete parseTags method
24| #
25| # Revision 1.9 2003/02/01 23:57:37 chalky
26| # Use new tags
27| #
28| # Revision 1.8 2002/11/18 11:49:29 chalky
29| # Tried to fix it up, but the *synopsis docbook entities aren't what we need.
30| # Shortcut to what I needed and just wrote a DocFormatter for the Synopsis
31| # DocBook manual's Config section.
32| #
33| # Revision 1.7 2001/07/19 04:03:05 chalky
34| # New .syn file format.
35| #
36| # Revision 1.6 2001/05/25 13:45:49 stefan
37| # fix problem with getopt error reporting
38| #
39| # Revision 1.5 2001/02/13 06:55:23 chalky
40| # Made synopsis -l work again
41| #
42| # Revision 1.4 2001/02/11 19:33:32 stefan
43| # made the C++ parser formally accept the config object; synopsis now correctly formats if a formatter is present, and only dumps a syn file otherwise; some minor fixes to DocBook
44| #
45| # Revision 1.3 2001/02/11 05:39:33 stefan
46| # first try at a more powerful config framework for synopsis
47| #
48| # Revision 1.2 2001/01/31 06:51:24 stefan
49| # add support for '-v' to all modules; modified toc lookup to use additional url as prefix
50| #
51| # Revision 1.1 2001/01/27 06:01:21 stefan
52| # a first take at a docbook formatter
53| #
54| #
55| """a DocBook formatter (producing Docbook 4.2 XML output"""
56| # THIS-IS-A-FORMATTER
57|
58| import sys, getopt, os, os.path, string, re
59| from Synopsis.Core import AST, Type, Util
60|
61| verbose = 0
62|
63| languages = {
64| 'IDL': 'idl',
65| 'C++': 'cxx',
66| 'Python': 'python'
67| }
68|
69| class Formatter (Type.Visitor, AST.Visitor):
70| """
71| The type visitors should generate names relative to the current scope.
72| The generated references however are fully scoped names
73| """
74| def __init__(self, os):
75| self.__os = os
76| self.__scope = ()
77| self.__scopestack = []
78| self.__indent = 0
79|
80| def scope(self): return self.__scope
81| def push_scope(self, newscope):
82| self.__scopestack.append(self.__scope)
83| self.__scope = newscope
84| def pop_scope(self):
85| self.__scope = self.__scopestack[-1]
86| del self.__scopestack[-1]
87| def write(self, text):
88| """Write some text to the output stream, replacing \n's with \n's and
89| indents."""
90| indent = ' ' * self.__indent
91| self.__os.write(text.replace('\n', '\n'+indent))
92| def start_entity(self, __type, **__params):
93| """Write the start of an entity, ending with a newline"""
94| param_text = ""
95| if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
96| self.write("<" + __type + param_text + ">")
97| self.__indent = self.__indent + 2
98| self.write("\n")
99| def end_entity(self, type):
100| """Write the end of an entity, starting with a newline"""
101| self.__indent = self.__indent - 2
102| self.write("\n</" + type + ">")
103| def write_entity(self, __type, __body, **__params):
104| """Write a single entity on one line (though body may contain
105| newlines)"""
106| param_text = ""
107| if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
108| self.write("<" + __type + param_text + ">")
109| self.__indent = self.__indent + 2
110| self.write(__body)
111| self.__indent = self.__indent - 2
112| self.write("</" + __type + ">")
113| def entity(self, __type, __body, **__params):
114| """Return but do not write the text for an entity on one line"""
115| param_text = ""
116| if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
117| return "<%s%s>%s</%s>"%(__type, param_text, __body, __type)
118|
119| def reference(self, ref, label):
120| """reference takes two strings, a reference (used to look up the symbol and generated the reference),
121| and the label (used to actually write it)"""
122| location = self.__toc.lookup(ref)
123| if location != "": return href("#" + location, label)
124| else: return span("type", str(label))
125|
126| def label(self, ref):
127| location = self.__toc.lookup(Util.ccolonName(ref))
128| ref = Util.ccolonName(ref, self.scope())
129| if location != "": return name("\"" + location + "\"", ref)
130| else: return ref
131|
132| def type_label(self): return self.__type_label
133| #################### Type Visitor ###########################################
134|
135| def visitBaseType(self, type):
136| self.__type_ref = Util.ccolonName(type.name())
137| self.__type_label = Util.ccolonName(type.name())
138|
139| def visitUnknown(self, type):
140| self.__type_ref = Util.ccolonName(type.name())
141| self.__type_label = Util.ccolonName(type.name(), self.scope())
142|
143| def visitDeclared(self, type):
144| self.__type_label = Util.ccolonName(type.name(), self.scope())
145| self.__type_ref = Util.ccolonName(type.name())
146|
147| def visitModifier(self, type):
148| type.alias().accept(self)
149| self.__type_ref = string.join(type.premod()) + " " + self.__type_ref + " " + string.join(type.postmod())
150| self.__type_label = string.join(type.premod()) + " " + self.__type_label + " " + string.join(type.postmod())
151|
152| def visitParametrized(self, type):
153| type.template().accept(self)
154| type_label = self.__type_label + "<"
155| parameters_label = []
156| for p in type.parameters():
157| p.accept(self)
158| parameters_label.append(self.__type_label)
159| self.__type_label = type_label + string.join(parameters_label, ", ") + ">"
160|
161| def visitFunctionType(self, type):
162| # TODO: this needs to be implemented
163| self.__type_ref = 'function_type'
164| self.__type_label = 'function_type'
165|
166| def visitComment(self, comment):
167| text = comment.text()
168| text = text.replace('\n\n', '</para><para>')
169| self.write(self.entity("para", text)+'\n')
170|
171| #################### AST Visitor ############################################
172|
173| def visitDeclarator(self, node):
174| self.__declarator = node.name()
175| for i in node.sizes():
176| self.__declarator[-1] = self.__declarator[-1] + "[" + str(i) + "]"
177|
178| def visitTypedef(self, typedef):
179| print "sorry, <typedef> not implemented"
180|
181| def visitVariable(self, variable):
182| self.start_entity("fieldsynopsis")
183| variable.vtype().accept(self)
184| self.entity("type", self.type_label())
185| self.entity("varname", variable.name()[-1])
186| self.end_entity("fieldsynopsis")
187|
188| def visitConst(self, const):
189| print "sorry, <const> not implemented"
190|
191| def visitModule(self, module):
192| self.start_entity("section")
193| self.write_entity("title", module.type()+" "+Util.ccolonName(module.name()))
194| self.write("\n")
195| map(self.visitComment, module.comments())
196| self.push_scope(module.name())
197| for declaration in module.declarations(): declaration.accept(self)
198| self.pop_scope()
199| self.end_entity("section")
200|
201| def visitClass(self, clas):
202| self.start_entity("classsynopsis", Class=clas.type(), language=languages[clas.language()])
203| classname = self.entity("classname", Util.ccolonName(clas.name()))
204| self.write_entity("ooclass", classname)
205| self.start_entity("classsynopsisinfo")
206| if len(clas.parents()):
207| for parent in clas.parents(): parent.accept(self)
208| self.push_scope(clas.name())
209| if clas.comments():
210| self.start_entity("textobject")
211| map(self.visitComment, clas.comments())
212| self.end_entity("textobject")
213| self.end_entity("classsynopsisinfo")
214| classes = []
215| for declaration in clas.declarations():
216| # Store classes for later
217| if isinstance(declaration, AST.Class):
218| classes.append(declaration)
219| else:
220| declaration.accept(self)
221| self.pop_scope()
222| self.end_entity("classsynopsis")
223| # Classes can't be nested (in docbook 4.2), so do them after
224| for clas in classes: clas.accept(self)
225|
226| def visitInheritance(self, inheritance):
227| map(lambda a, this=self: this.entity("modifier", a), inheritance.attributes())
228| self.entity("classname", Util.ccolonName(inheritance.parent().name(), self.scope()))
229|
230| def visitParameter(self, parameter):
231| self.start_entity("methodparam")
232| map(lambda m, this=self: this.write_entity("modifier", m), parameter.premodifier())
233| parameter.type().accept(self)
234| self.write_entity("type", self.type_label())
235| self.write_entity("parameter", parameter.identifier())
236| map(lambda m, this=self: this.write_entity("modifier", m), parameter.postmodifier())
237| self.end_entity("methodparam")
238|
239| def visitFunction(self, function):
240| print "sorry, <function> not implemented"
241|
242| def visitOperation(self, operation):
243| if operation.language() == "IDL" and operation.type() == "attribute":
244| self.start_entity("fieldsynopsis")
245| map(lambda m, this=self: this.entity("modifier", m), operation.premodifiers())
246| self.write_entity("modifier", "attribute")
247| operation.returnType().accept(self)
248| self.write_entity("type", self.type_label())
249| self.write_entity("varname", operation.realname())
250| self.end_entity("fieldsynopsis")
251| else:
252| self.start_entity("methodsynopsis")
253| if operation.language() != "Python":
254| ret = operation.returnType()
255| if ret:
256| ret.accept(self)
257| self.write_entity("type", self.type_label())
258| else:
259| self.write_entity("modifier", "def")
260| self.write_entity("methodname", Util.ccolonName(operation.realname(), self.scope()))
261| for parameter in operation.parameters(): parameter.accept(self)
262| map(lambda e, this=self: this.entity("exceptionname", e), operation.exceptions())
263| self.end_entity("methodsynopsis")
264|
265| def visitEnumerator(self, enumerator):
266| print "sorry, <enumerator> not implemented"
267| def visitEnum(self, enum):
268| print "sorry, <enum> not implemented"
269|
270| class DocFormatter (Formatter):
271| """A specialized version that just caters for the needs of the DocBook
272| manual's Config section. Only modules and classes are printed, and the
273| docbook elements classsynopsis etc are not used."""
274| def visitComment(self, comment):
275| text = comment.text()
276| see_tags, attr_tags = [], []
277| tags = comment.tags()
278| # Parse each of the tags
279| for tag in tags:
280| name, rest = tag.name(), tag.text()
281| if name == '@see':
282| see_tags.append(rest)
283| elif name == '@attr':
284| attr_tags.append(string.split(rest,' ',1))
285| # Do the body of the comment
286| text = text.replace('\n\n', '</para><para>')
287| text = text.replace('<heading>', '<emphasis>')
288| text = text.replace('</heading>', '</emphasis>')
289| text = text.replace('<example>', '<programlisting>')
290| text = text.replace('</example>', '</programlisting>')
291| self.write(self.entity("para", text)+'\n')
292| # Do the attributes
293| if len(attr_tags):
294| self.start_entity('variablelist')
295| self.write_entity('title', 'Attributes:')
296| for attr, desc in attr_tags:
297| self.start_entity('varlistentry')
298| self.write_entity('term', attr)
299| self.write_entity('listitem', self.entity('para', desc))
300| self.end_entity('varlistentry')
301| self.end_entity('variablelist')
302| # Do the see-also
303| if len(see_tags):
304| self.start_entity('itemizedlist')
305| self.write_entity('title', 'See also:')
306| for text in see_tags:
307| self.write_entity('listitem', self.entity('para', text))
308| self.end_entity('itemizedlist')
309|
310| def visitModule(self, module):
311| self.start_entity("section")
312| self.write_entity("title", module.type()+" "+Util.ccolonName(module.name()))
313| self.write("\n")
314| map(self.visitComment, module.comments())
315| self.end_entity("section")
316| self.push_scope(module.name())
317| for declaration in module.declarations():
318| if isinstance(declaration, AST.Class):
319| self.visitClass(declaration)
320| self.pop_scope()
321|
322| def visitClass(self, clas):
323| self.start_entity("section")
324| if len(clas.name()) > 3 and clas.name()[:2] == ("Config.py", "Base"):
325| self.write_entity("title", clas.name()[2]+" class "+Util.ccolonName(clas.name()[3:], self.scope()))
326| else:
327| self.write_entity("title", "Class "+Util.ccolonName(clas.name(), self.scope()))
328| if len(clas.parents()):
329| for parent in clas.parents():
330| self.visitInheritance(parent)
331| map(self.visitComment, clas.comments())
332| self.end_entity("section")
333| for declaration in clas.declarations():
334| if isinstance(declaration, AST.Class):
335| self.visitClass(declaration)
336|
337| def visitInheritance(self, inheritance):
338| self.write_entity("para", "Inherits "+Util.ccolonName(inheritance.parent().name(), self.scope()))
339|
340|
341| def usage():
342| """Print usage to stdout"""
343| print \
344| """
345| -v Verbose mode
346| -o <filename> Output filename
347| -d Don't oupu
348|
349| def __parseArgs(args):
350| global output, verbose, no_doctype, is_manual
351| output = sys.stdout
352| no_doctype = 0
353| is_manual = 0
354| try:
355| opts,remainder = getopt.getopt(args, "o:vdm")
356| except getopt.error, e:
357| sys.stderr.write("Error in arguments: " + str(e) + "\n")
358| sys.exit(1)
359|
360| for opt in opts:
361| o,a = opt
362| if o == "-o": output = open(a, "w")
363| elif o == "-v": verbose = 1
364| elif o == "-d": no_doctype = 1
365| elif o == "-m": is_manual = 1
366|
367| def format(args, ast, config):
368| global output
369| __parseArgs(args)
370| if not no_doctype:
371| output.write("<!DOCTYPE classsynopsis PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\">\n")
372| if is_manual:
373| formatter = DocFormatter(output)
374| else:
375| formatter = Formatter(output)
376| for d in ast.declarations():
377| d.accept(formatter)
378|
379| if output is not sys.stdout:
380| output.close()