Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Formatter/HTML_Simple.py
    1| #  $Id: HTML_Simple.py,v 1.14 2001/07/19 04:03:05 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: HTML_Simple.py,v $
   22| # Revision 1.14  2001/07/19 04:03:05  chalky
   23| # New .syn file format.
   24| #
   25| # Revision 1.13  2001/06/08 04:50:13  stefan
   26| # add grouping support
   27| #
   28| # Revision 1.12  2001/05/25 13:45:49  stefan
   29| # fix problem with getopt error reporting
   30| #
   31| # Revision 1.11  2001/05/13 02:46:41  stefan
   32| # more fixes from oxygene
   33| #
   34| # Revision 1.10  2001/02/13 06:55:23  chalky
   35| # Made synopsis -l work again
   36| #
   37| # Revision 1.9  2001/01/31 06:51:24  stefan
   38| # add support for '-v' to all modules; modified toc lookup to use additional url as prefix
   39| #
   40| # Revision 1.8  2001/01/25 01:10:59  chalky
   41| # Fixed html_simple
   42| #
   43| # Revision 1.7  2001/01/24 01:38:36  chalky
   44| # Added docstrings to all modules
   45| #
   46| # Revision 1.6  2001/01/22 19:54:41  stefan
   47| # better support for help message
   48| #
   49| # Revision 1.5  2001/01/22 17:06:15  stefan
   50| # added copyright notice, and switched on logging
   51| #
   52| """
   53| Simpler one-page HTML output
   54| """
   55| # THIS-IS-A-FORMATTER
   56| 
   57| import sys, getopt, os, os.path, string
   58| from Synopsis.Core import AST, Type, Util
   59| 
   60| verbose = 0
   61| 
   62| def entity(type, body): return "<" + type + "> " + body + "</" + type + ">"
   63| def href(ref, label): return "<a href=" + ref + ">" + label + "</a>"
   64| def name(ref, label): return "<a name=" + ref + ">" + label + "</a>"
   65| def span(clas, body): return "<span class=\"" + clas + "\">" + body + "</span>"
   66| def div(clas, body): return "<div class=\"" + clas + "\">" + body + "</div>"
   67| def desc(text):
   68|     block = filter(lambda s: s.text()[0:3] == "//.", text)
   69|     if not len(block): return ""
   70|     block = map(lambda s: s.text()[3:], block)
   71|     return "<div class=\"desc\">" + string.join(block, '\n') + "</div>"
   72| 
   73| class TableOfContents(AST.Visitor):
   74|     """
   75|     generate a dictionary of all declarations which can be looked up to create
   76|     cross references. Names are fully scoped."""
   77|     def __init__(self): self.__toc__ = {}
   78|     def write(self, os):
   79|         keys = self.__toc__.keys()
   80|         keys.sort()
   81|         for key in keys:
   82|             os.write(href("#" + self.__toc__[key], key) + "<br>\n")
   83| 
   84|     def lookup(self, name):
   85|         if self.__toc__.has_key(name): return self.__toc__[name]
   86|         else: return ""
   87| 
   88|     def insert(self, name):
   89|         self.__toc__[Util.ccolonName(name)] = Util.dotName(name)
   90| 
   91|     def visitAST(self, node):
   92|         for d in node.declarations: d.accept(this)
   93|         
   94|     def visitDeclarator(self, node):
   95|         self.insert(node.name())
   96| 
   97|     def visitGroup(self, group):
   98|         for declaration in group.declarations():
   99|             declaration.accept(self)
  100| 
  101|     def visitModule(self, module):
  102|         self.insert(module.name())
  103|         for declaration in module.declarations():
  104|             declaration.accept(self)
  105| 
  106|     def visitClass(self, clas):
  107|         self.insert(clas.name())
  108|         for declaration in clas.declarations():
  109|             declaration.accept(self)
  110| 
  111|     def visitTypedef(self, typedef):
  112|         self.insert(typedef.name())
  113|     def visitEnumerator(self, enumerator):
  114|         self.insert(enumerator.name())
  115|     def visitEnum(self, enum):
  116|         self.insert(enum.name())
  117|         for e in enum.enumerators(): e.accept(self)
  118|         
  119|     def visitVariable(self, variable):
  120|         self.insert(variable.name())
  121|     def visitConst(self, const):
  122|         self.insert(const.name())
  123|     def visitParameter(self, parameter): pass
  124|     def visitFunction(self, function):
  125|         self.insert(function.name())
  126| 
  127|     def visitOperation(self, operation):
  128|         self.insert(operation.name())
  129|         for parameter in operation.parameters(): parameter.accept(self)
  130|     
  131| 
  132| class HTMLFormatter (Type.Visitor, AST.Visitor):
  133|     """
  134|     The type visitors should generate names relative to the current scope.
  135|     The generated references however are fully scoped names
  136|     """
  137|     def __init__(self, os, toc):
  138|         self.__os = os
  139|         self.__toc = toc
  140|         self.__scope = []
  141| 
  142|     def scope(self): return self.__scope
  143|     def write(self, text): self.__os.write(text)
  144| 
  145|     def reference(self, ref, label):
  146|         """reference takes two strings, a reference (used to look up the symbol and generated the reference),
  147|         and the label (used to actually write it)"""
  148|         location = self.__toc.lookup(ref)
  149|         if location != "": return href("#" + location, label)
  150|         else: return span("type", str(label))
  151|         
  152|     def label(self, ref):
  153|         location = self.__toc.lookup(Util.ccolonName(ref))
  154|         ref = Util.ccolonName(ref, self.scope())
  155|         if location != "": return name("\"" + location + "\"", ref)
  156|         else: return ref
  157| 
  158|     #################### Type Visitor ###########################################
  159| 
  160|     def visitBaseType(self, type):
  161|         self.__type_ref = Util.ccolonName(type.name())
  162|         self.__type_label = Util.ccolonName(type.name())
  163|         
  164|     def visitUnknown(self, type):
  165|         self.__type_ref = Util.ccolonName(type.name())
  166|         self.__type_label = Util.ccolonName(type.name(), self.scope())
  167|         
  168|     def visitDeclared(self, type):
  169|         self.__type_label = Util.ccolonName(type.name(), self.scope())
  170|         self.__type_ref = Util.ccolonName(type.name())
  171|         
  172|     def visitModifier(self, type):
  173|         type.alias().accept(self)
  174|         self.__type_ref = string.join(type.premod()) + " " + self.__type_ref + " " + string.join(type.postmod())
  175|         self.__type_label = string.join(type.premod()) + " " + self.__type_label + " " + string.join(type.postmod())
  176|             
  177|     def visitParametrized(self, type):
  178|         type.template().accept(self)
  179|         type_ref = self.__type_ref + "&lt;"
  180|         type_label = self.__type_label + "&lt;"
  181|         parameters_ref = []
  182|         parameters_label = []
  183|         for p in type.parameters():
  184|             p.accept(self)
  185|             parameters_ref.append(self.__type_ref)
  186|             parameters_label.append(self.reference(self.__type_ref, self.__type_label))
  187|         self.__type_ref = type_ref + string.join(parameters_ref, ", ") + "&gt;"
  188|         self.__type_label = type_label + string.join(parameters_label, ", ") + "&gt;"
  189| 
  190|     def visitFunctionType(self, type):
  191|         # TODO: this needs to be implemented
  192|         self.__type_ref = 'function_type'
  193|         self.__type_label = 'function_type'
  194| 
  195| 
  196|     #################### AST Visitor ############################################
  197|             
  198|     def visitDeclarator(self, node):
  199|         self.__declarator = node.name()
  200|         for i in node.sizes():
  201|             self.__declarator[-1] = self.__declarator[-1] + "[" + str(i) + "]"
  202| 
  203|     def visitTypedef(self, typedef):
  204|         self.write(span("keyword", typedef.type()) + " ")
  205|         typedef.alias().accept(self)
  206|         self.write(self.reference(self.__type_ref, self.__type_label) + " ")
  207|         self.write(self.label(typedef.name()))
  208|         if len(typedef.comments()): self.write("\n" + desc(typedef.comments()) + "\n")            
  209| 
  210|     def visitVariable(self, variable):
  211|         variable.vtype().accept(self)
  212|         self.write(self.reference(self.__type_ref, self.__type_label) + " ")
  213|         self.write(self.label(variable.name()))
  214|         if len(variable.comments()): self.write("\n" + desc(variable.comments()) + "\n")            
  215| 
  216|     def visitConst(self, const):
  217|         const.ctype().accept(self)
  218|         self.write(span("keyword", const.type()) + " " + self.reference(self.__type_ref, self.__type_label) + " ")
  219|         self.write(self.label(const.name()) + " = " + const.value())
  220|         if len(const.comments()): self.write("\n" + desc(const.comments()) + "\n")            
  221| 
  222|     def visitGroup(self, group):
  223|         self.write(span("keyword", group.type()) + "\n")
  224|         if len(group.comments()): self.write("\n" + desc(group.comments()) + "\n")            
  225|         self.write("<div class=\"group\">\n")
  226|         for declaration in group.declarations():
  227|             declaration.accept(self)
  228|             self.write("<br>\n")
  229|         self.write("</div>\n")
  230| 
  231|     def visitModule(self, module):
  232|         self.write(span("keyword", module.type()) + " " + self.label(module.name()) + "\n")
  233|         if len(module.comments()): self.write("\n" + desc(module.comments()) + "\n")            
  234|         self.write("<div class=\"module\">\n")
  235|         self.scope().append(module.name()[-1])
  236|         for declaration in module.declarations():
  237|             declaration.accept(self)
  238|             self.write("<br>\n")
  239|         self.scope().pop()
  240|         self.write("</div>\n")
  241| 
  242|     def visitClass(self, clas):
  243|         self.write(span("keyword", clas.type()) + " " + self.label(clas.name()) + "\n")
  244|         if len(clas.parents()):
  245|             self.write("<div class=\"parents\">\n")
  246|             self.write(entity("b", "parents:") + "\n")
  247|             self.write("<div class=\"inheritance\">\n")
  248|             for parent in clas.parents():
  249|                 parent.accept(self)
  250|                 self.write("<br>\n")
  251|             self.write("</div>\n")
  252|             self.write("</div>\n")
  253|         self.scope().append(clas.name()[-1])
  254|         if len(clas.comments()): self.write("\n" + desc(clas.comments()) + "\n")            
  255|         if len(clas.declarations()):
  256|             self.write("<div class=\"declarations\">\n")
  257|             for d in clas.declarations():
  258|                 d.accept(self)
  259|                 self.write("<br>\n")
  260|             self.write("</div>\n")
  261|         self.scope().pop()
  262| 
  263|     def visitInheritance(self, inheritance):
  264|         for attribute in inheritance.attributes(): self.write(span("keywords", attribute) + " ")
  265|         inheritance.parent().accept(self)
  266|         self.write(self.reference(self.__type_ref, self.__type_label))
  267| 
  268|     def visitParameter(self, parameter):
  269|         for m in parameter.premodifier(): self.write(span("keyword", m) + " ")
  270|         parameter.type().accept(self)
  271|         self.write(self.reference(self.__type_ref, self.__type_label) + " ")
  272|         for m in parameter.postmodifier(): self.write(span("keyword", m) + " ")
  273|         if len(parameter.identifier()) != 0:
  274|             self.write(span("variable", parameter.identifier()))
  275|             if len(parameter.value()) != 0:
  276|                 self.write(" = " + span("value", parameter.value()))
  277| 
  278|     def visitFunction(self, function):
  279|         for modifier in operation.premodifier(): self.write(span("keyword", modifier) + " ")
  280|         operation.returnType().accept(self)
  281|         self.write(self.reference(self.__type_ref, self.__type_label) + " ")
  282|         self.write(self.label(function.realname()) + "(")
  283|         parameters = function.parameters()
  284|         if len(parameters): parameters[0].accept(self)
  285|         for parameter in parameters[1:]:
  286|             self.write(", ")
  287|             parameter.accept(self)
  288|         self.write(")")
  289|         for modifier in operation.postmodifier(): self.write(span("keyword", modifier) + " ")
  290|         self.write("\n")
  291|         if len(operation.exceptions()):
  292|             self.write(span("keyword", "raises") + "\n")
  293|             exceptions = []
  294|             for exception in operation.exceptions():
  295|                 exceptions.append(self.reference(Util.ccolonName(exception.name()), Util.ccolonName(exception.name(), self.scope())))
  296|             self.write("(" + span("raises", string.join(exceptions, ", ")) + ")")
  297|         self.write("\n")
  298|         if len(function.comments()): self.write("\n" + desc(function.comments()) + "\n")            
  299| 
  300|     def visitOperation(self, operation):
  301|         for modifier in operation.premodifier(): self.write(span("keyword", modifier) + " ")
  302|         if operation.returnType():
  303|             operation.returnType().accept(self)
  304|             self.write(self.reference(self.__type_ref, self.__type_label) + " ")
  305|         if operation.language() == "IDL" and operation.type() == "attribute":
  306|             self.write(span("keyword", "attribute") + " ")
  307|         self.write(self.label(operation.realname()) + "(")
  308|         parameters = operation.parameters()
  309|         if len(parameters): parameters[0].accept(self)
  310|         for parameter in parameters[1:]:
  311|             self.write(", ")
  312|             parameter.accept(self)
  313|         self.write(")")
  314|         for modifier in operation.postmodifier(): self.write(span("keyword", modifier) + " ")
  315|         self.write("\n")
  316|         if len(operation.exceptions()):
  317|             self.write(span("keyword", "raises") + "\n")
  318|             exceptions = []
  319|             for exception in operation.exceptions():
  320|                 exceptions.append(self.reference(Util.ccolonName(exception.name()), Util.ccolonName(exception.name(), self.scope())))
  321|             self.write("(" + span("raises", string.join(exceptions, ", ")) + ")")
  322|         self.write("\n")
  323|         if len(operation.comments()): self.write("\n" + desc(operation.comments()) + "\n")            
  324| 
  325|     def visitEnumerator(self, enumerator):
  326|         self.write(self.label(enumerator.name()))
  327|         if len(enumerator.value()):
  328|             self.write(" = " + span("value", enumerator.value()))
  329| 
  330|     def visitEnum(self, enum):
  331|         self.write(span("keyword", "enum ") + self.label(enum.name()) + "\n")
  332|         self.write("<div class=\"enum\">\n")
  333|         for enumerator in enum.enumerators():
  334|             enumerator.accept(self)
  335|             self.write("<br>\n")
  336|         self.write("</div>\n")
  337|         if len(enum.comments()): self.write("\n" + desc(enum.comments()) + "\n")            
  338| 
  339| def usage():
  340|     """Print usage to stdout"""
  341|     print \
  342| """
  343|   -o <filename>                        Output filename
  344|   -s <filename>                        Filename of stylesheet"""
  345| 
  346| def __parseArgs(args):
  347|     global output, stylesheet, verbose
  348|     output = sys.stdout
  349|     stylesheet = ""
  350|     try:
  351|         opts,remainder = getopt.getopt(args, "o:s:v")
  352|     except getopt.error, e:
  353|         sys.stderr.write("Error in arguments: " + str(e) + "\n")
  354|         sys.exit(1)
  355| 
  356|     for opt in opts:
  357|         o,a = opt
  358|         if o == "-o": output = open(a, "w")
  359|         elif o == "-s": stylesheet = a
  360|         elif o == "-v": verbose = 1
  361| 
  362| def format(args, ast, config_obj):
  363|     global output, stylesheet
  364|     __parseArgs(args)
  365|     output.write("<html>\n")
  366|     output.write("<head>\n")
  367|     if len(stylesheet):
  368|         output.write("<link  rel=\"stylesheet\" href=\"" + stylesheet + "\">\n")
  369|     output.write("</head>\n")
  370|     output.write("<body>\n")
  371|     toc = TableOfContents()
  372|     for d in ast.declarations():
  373|         d.accept(toc)
  374|     output.write(entity("h1", "Reference") + "\n")
  375|     formatter = HTMLFormatter(output, toc)
  376|     for d in ast.declarations():
  377|         d.accept(formatter)
  378|     output.write(entity("h1", "Index") + "\n")
  379|     toc.write(output)
  380|     output.write("</body>\n")
  381|     output.write("</html>\n")