Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Formatter/BoostBook.py
    1| #  $Id: BoostBook.py,v 1.1 2003/03/06 10:02:28 chalky Exp $
    2| #
    3| #  This file is a part of Synopsis.
    4| #  Copyright (C) 2003 by Stephen Davies
    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: BoostBook.py,v $
   22| # Revision 1.1  2003/03/06 10:02:28  chalky
   23| # First cut at a BoostBook formatter
   24| #
   25| 
   26| """a BoostBook formatter"""
   27| # THIS-IS-A-FORMATTER
   28| 
   29| import sys, getopt, os, os.path, string, re
   30| from Synopsis.Core import AST, Type, Util
   31| 
   32| verbose = 0
   33| 
   34| languages = {
   35|     'IDL': 'idl',
   36|     'C++': 'cxx',
   37|     'Python': 'python'
   38|     }
   39| 
   40| class Formatter (Type.Visitor, AST.Visitor):
   41|     """
   42|     The type visitors should generate names relative to the current scope.
   43|     The generated references however are fully scoped names
   44|     """
   45|     def __init__(self, os):
   46|         self.__os = os
   47|         self.__scope = ()
   48|         self.__scopestack = []
   49|         self.__indent = 0
   50| 
   51|     def scope(self): return self.__scope
   52|     def push_scope(self, newscope):
   53|         self.__scopestack.append(self.__scope)
   54|         self.__scope = newscope
   55|     def pop_scope(self):
   56|         self.__scope = self.__scopestack[-1]
   57|         del self.__scopestack[-1]
   58|     def write(self, text):
   59|         """Write some text to the output stream, replacing \n's with \n's and
   60|         indents."""
   61|         indent = ' ' * self.__indent
   62|         self.__os.write(text.replace('\n', '\n'+indent))
   63|     def start_entity(self, __type, **__params):
   64|         """Write the start of an entity, ending with a newline"""
   65|         param_text = ""
   66|         if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
   67|         self.write("<" + __type + param_text + ">")
   68|         self.__indent = self.__indent + 2
   69|         self.write("\n")
   70|     def end_entity(self, type):
   71|         """Write the end of an entity, starting with a newline"""
   72|         self.__indent = self.__indent - 2
   73|         self.write("\n</" + type + ">")
   74|     def write_entity(self, __type, __body, **__params):
   75|         """Write a single entity on one line (though body may contain
   76|         newlines)"""
   77|         param_text = ""
   78|         if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
   79|         self.write("<" + __type + param_text + ">")
   80|         self.__indent = self.__indent + 2
   81|         self.write(__body)
   82|         self.__indent = self.__indent - 2
   83|         self.write("</" + __type + ">")
   84|     def entity(self, __type, __body, **__params):
   85|         """Return but do not write the text for an entity on one line"""
   86|         param_text = ""
   87|         if __params: param_text = " " + string.join(map(lambda p:'%s="%s"'%(p[0].lower(), p[1]), __params.items()))
   88|         return "<%s%s>%s</%s>"%(__type, param_text, __body, __type)
   89| 
   90|     def reference(self, ref, label):
   91|         """reference takes two strings, a reference (used to look up the symbol and generated the reference),
   92|         and the label (used to actually write it)"""
   93|         location = self.__toc.lookup(ref)
   94|         if location != "": return href("#" + location, label)
   95|         else: return span("type", str(label))
   96|         
   97|     def label(self, ref):
   98|         location = self.__toc.lookup(Util.ccolonName(ref))
   99|         ref = Util.ccolonName(ref, self.scope())
  100|         if location != "": return name("\"" + location + "\"", ref)
  101|         else: return ref
  102| 
  103|     def type_label(self): return self.__type_label
  104|     #################### Type Visitor ###########################################
  105| 
  106|     def visitBaseType(self, type):
  107|         self.__type_ref = Util.ccolonName(type.name())
  108|         self.__type_label = Util.ccolonName(type.name())
  109|         
  110|     def visitUnknown(self, type):
  111|         self.__type_ref = Util.ccolonName(type.name())
  112|         self.__type_label = Util.ccolonName(type.name(), self.scope())
  113|         
  114|     def visitDeclared(self, type):
  115|         self.__type_label = Util.ccolonName(type.name(), self.scope())
  116|         self.__type_ref = Util.ccolonName(type.name())
  117|         
  118|     def visitModifier(self, type):
  119|         type.alias().accept(self)
  120|         self.__type_ref = string.join(type.premod()) + " " + self.__type_ref + " " + string.join(type.postmod())
  121|         self.__type_label = string.join(type.premod()) + " " + self.__type_label + " " + string.join(type.postmod())
  122|             
  123|     def visitParametrized(self, type):
  124|         type.template().accept(self)
  125|         type_label = self.__type_label + "&lt;"
  126|         parameters_label = []
  127|         for p in type.parameters():
  128|             p.accept(self)
  129|             parameters_label.append(self.__type_label)
  130|         self.__type_label = type_label + string.join(parameters_label, ", ") + "&gt;"
  131| 
  132|     def formatType(self, type):
  133|         type.accept(self)
  134|         return self.__type_label
  135| 
  136|     def visitFunctionType(self, type):
  137|         # TODO: this needs to be implemented
  138|         self.__type_ref = 'function_type'
  139|         self.__type_label = 'function_type'
  140| 
  141|     def visitComment(self, comment):
  142|         text = comment.text()
  143|         text = text.replace('\n\n', '</para><para>')
  144|         self.write(self.entity("para", text)+'\n')
  145| 
  146|     #################### AST Visitor ############################################
  147|             
  148|     def visitDeclarator(self, node):
  149|         self.__declarator = node.name()
  150|         for i in node.sizes():
  151|             self.__declarator[-1] = self.__declarator[-1] + "[" + str(i) + "]"
  152| 
  153|     def visitTypedef(self, typedef):
  154|         self.start_entity("typedef", name=Util.ccolonName(self.scope(), typedef.name()))
  155|         self.write_entity("type", self.formatType(typedef.alias()))
  156|         self.end_entity("typedef")
  157| 
  158|     def visitVariable(self, variable):
  159|         self.start_entity("fieldsynopsis")
  160|         variable.vtype().accept(self)
  161|         self.entity("type", self.type_label())
  162|         self.entity("varname", variable.name()[-1])
  163|         self.end_entity("fieldsynopsis")
  164| 
  165|     def visitConst(self, const):
  166|         print "sorry, <const> not implemented"
  167| 
  168|     def visitModule(self, module):
  169|         self.start_entity("namespace", name=Util.ccolonName(self.scope(), module.name()))
  170|         self.write("\n")
  171|         map(self.visitComment, module.comments())
  172|         self.push_scope(module.name())
  173|         for declaration in module.declarations(): declaration.accept(self)
  174|         self.pop_scope()
  175|         self.end_entity("namespace")
  176| 
  177|     def visitClass(self, clas):
  178|         self.start_entity("class", name=Util.ccolonName(self.scope(), clas.name()))
  179|         # clas.type()
  180|         if len(clas.parents()):
  181|             for parent in clas.parents(): parent.accept(self)
  182|         self.push_scope(clas.name())
  183|         if clas.comments():
  184|             self.start_entity("purpose")
  185|             map(self.visitComment, clas.comments())
  186|             self.end_entity("purpose")
  187|         for declaration in clas.declarations():
  188|             declaration.accept(self)
  189|         self.pop_scope()
  190|         self.end_entity("class")
  191| 
  192|     def visitInheritance(self, inheritance):
  193|         if len(inheritance.attributes()):
  194|             self.start_entity("inherit", access=inheritance.attributes()[0])
  195|         else:
  196|             self.start_entity("inherit")
  197|         self.write_entity("classname", self.formatType(inheritance.parent()))
  198|         self.end_entity("inherit")
  199| 
  200|     def visitParameter(self, parameter):
  201|         self.start_entity("parameter", name=parameter.identifier())
  202|         self.write_entity("param_type", self.formatType(parameter.type()))
  203|         #map(lambda m, this=self: this.write_entity("modifier", m), parameter.premodifier())
  204|         #map(lambda m, this=self: this.write_entity("modifier", m), parameter.postmodifier())
  205|         self.end_entity("parameter")
  206|         self.write("\n")
  207| 
  208|     def visitFunction(self, function):
  209|         self.start_entity("function", name=Util.ccolonName(self.scope(), function.realname()))
  210|         self.do_function(function)
  211|         self.end_entity("function")
  212|         self.write("\n")
  213| 
  214|     def visitOperation(self, operation):
  215|         name = operation.name()
  216|         tag = None
  217|         if len(name) > 1:
  218|             if name[-1] == name[-2]:
  219|                tag = "constructor"
  220|                self.start_entity(tag)
  221|             elif name[-1] == "~"+name[-2]:
  222|                tag = "destructor"
  223|                self.start_entity(tag)
  224|         if tag is None:
  225|             tag = "method"
  226|             self.start_entity(tag, name=Util.ccolonName(self.scope(), operation.realname()))
  227|         self.do_function(operation)
  228|         self.end_entity(tag)
  229|         self.write("\n")
  230| 
  231|     def do_function(self, func):
  232|         """Stuff common to functions and methods, contructors, destructors"""
  233|         for parameter in func.parameters(): parameter.accept(self)
  234|         if func.returnType():
  235|             self.write_entity("type", self.formatType(func.returnType()))
  236|             self.write("\n")
  237|         if func.comments():
  238|             self.start_entity("purpose")
  239|             map(self.visitComment, func.comments())
  240|             self.end_entity("purpose")
  241|             self.write("\n")
  242|         
  243|         if func.exceptions():
  244|             self.start_entity("throws")
  245|             for ex in func.exceptions():
  246|                self.write_entity("simpara", ex)
  247|             self.end_entity("throws")
  248|             self.write("\n")
  249| 
  250|     def visitEnumerator(self, enumerator):
  251|         print "sorry, <enumerator> not implemented"
  252|     def visitEnum(self, enum):
  253|         print "sorry, <enum> not implemented"
  254| 
  255| def usage():
  256|     """Print usage to stdout"""
  257|     print \
  258| """
  259|   -v                         Verbose mode
  260|   -o <filename>              Output filename
  261|   -d                       Don't oupu
  262| 
  263| def __parseArgs(args):
  264|     global output, verbose, no_doctype, is_manual
  265|     output = sys.stdout
  266|     no_doctype = 0
  267|     is_manual = 0
  268|     try:
  269|         opts,remainder = getopt.getopt(args, "o:vdm")
  270|     except getopt.error, e:
  271|         sys.stderr.write("Error in arguments: " + str(e) + "\n")
  272|         sys.exit(1)
  273| 
  274|     for opt in opts:
  275|         o,a = opt
  276|         if o == "-o": output = open(a, "w")
  277|         elif o == "-v": verbose = 1
  278|         elif o == "-d": no_doctype = 1
  279|         elif o == "-m": is_manual = 1
  280| 
  281| def format(args, ast, config):
  282|     global output
  283|     __parseArgs(args)
  284|     #if not no_doctype:
  285|     #   output.write("<!DOCTYPE classsynopsis PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\">\
  286|     formatter = Formatter(output)
  287|     for d in ast.declarations():
  288|         d.accept(formatter)
  289| 
  290|     if output is not sys.stdout:
  291|         output.close()