Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/Parser/C++/syn/linkstore.cc
    1| // Synopsis C++ Parser: linkstore.cc source file
    2| // Implementation of the LinkStore class
    3| 
    4| // $Id: linkstore.cc,v 1.22 2002/12/21 05:04:39 chalky Exp $
    5| //
    6| // This file is a part of Synopsis.
    7| // Copyright (C) 2000-2002 Stephen Davies
    8| // Copyright (C) 2000, 2001 Stefan Seefeld
    9| //
   10| // Synopsis is free software; you can redistribute it and/or modify it
   11| // under the terms of the GNU General Public License as published by
   12| // the Free Software Foundation; either version 2 of the License, or
   13| // (at your option) any later version.
   14| //
   15| // This program is distributed in the hope that it will be useful,
   16| // but WITHOUT ANY WARRANTY; without even the implied warranty of
   17| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   18| // General Public License for more details.
   19| //
   20| // You should have received a copy of the GNU General Public License
   21| // along with this program; if not, write to the Free Software
   22| // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   23| // 02111-1307, USA.
   24| //
   25| // $Log: linkstore.cc,v $
   26| // Revision 1.22  2002/12/21 05:04:39  chalky
   27| // Move constants since gcc 3.2 didn't like them.
   28| //
   29| // Revision 1.21  2002/12/12 17:25:34  chalky
   30| // Implemented Include support for C++ parser. A few other minor fixes.
   31| //
   32| // Revision 1.20  2002/12/10 07:19:50  chalky
   33| // Remove debugging stuff
   34| //
   35| // Revision 1.19  2002/12/09 12:14:10  chalky
   36| // Ensure all files are created
   37| //
   38| // Revision 1.18  2002/12/09 04:01:00  chalky
   39| // Added multiple file support to parsers, changed AST datastructure to handle
   40| // new information, added a demo to demo/C++. AST Declarations now have a
   41| // reference to a SourceFile (which includes a filename) instead of a filename.
   42| //
   43| // Revision 1.17  2002/11/17 12:11:43  chalky
   44| // Reformatted all files with astyle --style=ansi, renamed fakegc.hh
   45| //
   46| // Revision 1.16  2002/11/03 05:41:30  chalky
   47| // Fix crash in visit_parameterized
   48| //
   49| // Revision 1.15  2002/11/02 06:37:37  chalky
   50| // Allow non-frames output, some refactoring of page layout, new modules.
   51| //
   52| // Revision 1.14  2002/10/29 02:39:57  chalky
   53| // Changes to compile with g++-3.2
   54| //
   55| // Revision 1.13  2002/10/28 16:25:02  chalky
   56| // Fix crash if using xref output but not syntax output
   57| //
   58| // Revision 1.12  2002/10/20 15:38:10  chalky
   59| // Much improved template support, including Function Templates.
   60| //
   61| 
   62| #include <unistd.h>
   63| #include <sys/types.h>
   64| #include <sys/stat.h>
   65| #include <errno.h>
   66| #include <cstdio>
   67| 
   68| #include "swalker.hh"
   69| #include "linkstore.hh"
   70| #include "ast.hh"
   71| #include "type.hh"
   72| #include "link_map.hh"
   73| #include "dumper.hh"
   74| #include "builder.hh"
   75| #include "strace.hh"
   76| #include "filter.hh"
   77| 
   78| #include <sstream>
   79| #include <iomanip>
   80| #include <map>
   81| 
   82| #include <occ/ptree.h>
   83| #include <occ/parse.h>
   84| #include <occ/buffer.h>
   85| 
   86| namespace 
   87| {
   88| 
   89| //. The field separator
   90| const charFS = " ";
   91| 
   92| //. The record separator
   93| const charRS = "\n";
   94| 
   95| const charcontext_names[] =
   96| {
   97|     "REF",
   98|     "DEF",
   99|     "SPAN",
  100|     "IMPL",
  101|     "UDIR",
  102|     "UDEC",
  103|     "CALL"
  104| };
  105| 
  106| // Utility function to ensure that all the directories in the given path
  107| // exist, creating them if needed.
  108| void makedirs(const char* path)
  109| {
  110|     static char buf[1024];
  111|     strcpy(bufpath);
  112|     struct stat st;
  113|     int error;
  114|     charptr = buf, *sep = NULL;
  115|     // Skip first / if any
  116|     if (*ptr == '/')
  117|         ptr++;
  118|     while (1)
  119|     {
  120|         // Find next /
  121|         while (*ptr && *ptr != '/')
  122|             ptr++;
  123|         if (!*ptr)
  124|             return;
  125|         sep = ptr;
  126|         if (ptr == sep + 1)
  127|             // An empty path component, eg: blah/foo//fred
  128|             continue;
  129|         *sep = 0;
  130|         // Try to stat this dir
  131|         if ((error = stat(buf, &st)) == -1 && errno == ENOENT)
  132|             mkdir(buf0755);
  133|         else if (error)
  134|         {
  135|             perror(buf);
  136|             return;
  137|         }
  138|         // Restore / to build up path
  139|         *sep = '/';
  140|         // Move past /
  141|         ptr++;
  142|     }
  143| }
  144| 
  145| }
  146| 
  147| struct LinkStore::Private
  148| {
  149|     //. The start of the program buffer
  150|     const charbuffer_start;
  151| 
  152|     //. The filter
  153|     FileFilterfilter;
  154| 
  155|     //. The Parser object
  156|     Parserparser;
  157| 
  158|     //. The SWalker object
  159|     SWalkerwalker;
  160| 
  161|     //. Struct for storing a pair of ofstream pointers
  162|     struct Streams {
  163|         std::ofstreamsyntax;
  164|         std::ofstreamxref;
  165|         Streams() : syntax(NULL), xref(NULL) {}
  166|         Streams(const Streams& o) : syntax(o.syntax), xref(o.xref) {}
  167|         Streamsoperator =(const Streams& o)
  168|         {
  169|             syntax = o.syntax;
  170|             xref = o.xref;
  171|             return *this;
  172|         }
  173|     };
  174| 
  175|     //. The type of a map of streams
  176|     typedef std::map<AST::SourceFile*,Streams> StreamsMap;
  177| 
  178|     //. A map of streams for each file
  179|     StreamsMap streams;
  180| };
  181| 
  182| LinkStore::LinkStore(FileFilter* filter, SWalker* swalker)
  183| {
  184|     m = new Private;
  185|     m->filter = filter;
  186|     m->walker = swalker;
  187|     m->buffer_start = swalker->program()->Read(0);
  188|     m->parser = swalker->parser();
  189| 
  190|     // Check size of array here to prevent later segfaults
  191|     if (sizeof(context_names)/sizeof(context_names[0]) != NumContext)
  192|     {
  193|         std::cerr << "FATAL ERROR: Wrong number of context names in linkstore.cc" << std::endl;
  194|         exit(1);
  195|     }
  196| 
  197|     // Create all the output streams to ensure that the files exist
  198|     const std::stringmain;
  199|     const std::vector<std::string>* extra;
  200|     filter->get_all_filenames(mainextra);
  201|     // Do the main file in case it is not in extra (it should be unless extra
  202|     // is not being used, i.e.: there is just one file)
  203|     //std::cout << " Creating streams for " << *main << std::endl;
  204|     AST::SourceFilefile = filter->get_sourcefile(main->c_str());
  205|     get_syntax_stream(file);
  206|     get_xref_stream(file);
  207|     // Do the extra filenames
  208|     std::vector<std::string>::const_iterator iter;
  209|     for (iter = extra->begin(); iter != extra->end(); iter++)
  210|     {
  211|         //std::cout << " Creating streams for " << *iter << std::endl;
  212|         file = filter->get_sourcefile(iter->c_str());
  213|         get_syntax_stream(file);
  214|         get_xref_stream(file);
  215|     }
  216| }
  217| 
  218| LinkStore::~LinkStore()
  219| {
  220|     // Delete (which closes) all the opened file streams
  221|     Private::StreamsMap::iterator iter = m->streams.begin();
  222|     while (iter != m->streams.end())
  223|     {
  224|         Private::Streamsstreams = (iter++)->second;
  225|         if (streams.syntaxdelete streams.syntax;
  226|         if (streams.xrefdelete streams.xref;
  227|     }
  228|     delete m;
  229| }
  230| 
  231| SWalkerLinkStore::swalker()
  232| {
  233|     return m->walker;
  234| }
  235| 
  236| int LinkStore::find_col(int line, const char* ptr)
  237| {
  238|     const charpos = ptr;
  239|     while (pos > m->buffer_start && *--pos != '\n')
  240|         ; // do nothing inside loop
  241|     int col = ptr - pos;
  242|     // Resolve macro maps
  243|     return LinkMap::instance()->map(linecol);
  244| }
  245| 
  246| void LinkStore::link(Ptree* node, Context context, const ScopedName& name, const std::string& desc, const AST::Declaration* decl)
  247| {
  248|     AST::SourceFilefile = m->walker->current_file();
  249| 
  250|     // Dont store records for included files
  251|     if (!m->filter->should_link(file))
  252|         return;
  253| 
  254|     // Get info for storing an xref record
  255|     int line = m->walker->line_of_ptree(node);
  256|     if (decl != NULL)
  257|         store_xref_record(filedeclfile->filename(), linecontext);
  258| 
  259|     // Get info for storing a syntax record
  260|     int col = find_col(linenode->LeftMost());
  261|     if (col < 0)
  262|         return; // inside macro
  263|     int len = node->RightMost() - node->LeftMost();
  264| 
  265|     store_syntax_record(filelinecollencontextnamedesc);
  266| }
  267| 
  268| //. A class which acts as a Types Visitor to store the correct link to a given
  269| //. type
  270| class TypeStorer : public Types::Visitor
  271| {
  272|     // Variables to pass to link()
  273|     LinkStorelinks;
  274|     Ptreenode;
  275|     LinkStore::Context context;
  276| public:
  277|     //. Constructor
  278|     TypeStorer(LinkStore* ls, Ptree* n, LinkStore::Context c)
  279|             : links(ls), node(n), context(c)
  280|     { }
  281| 
  282|     //. Returns a suitable description for the given type
  283|     std::string describe(Types::Type* type)
  284|     {
  285|         std::string desc;
  286|         try
  287|         {
  288|             return Types::declared_cast<AST::Declaration>(type)->type();
  289|         }
  290|         catch (const Types::wrong_type_cast&)
  291|         {
  292|             return links->swalker()->type_formatter()->format(type);
  293|         }
  294|     }
  295| 
  296|     // Visitor methods
  297|     void visit_base(Types::Base* base)
  298|     {
  299|         // Not a link, but a keyword
  300|         links->span(node"file-keyword");
  301|     }
  302|     void visit_named(Types::Named* named)
  303|     {
  304|         // All other nameds get stored
  305|         links->link(nodecontextnamed->name(), describe(named));
  306|     }
  307|     void visit_declared(Types::Declared* declared)
  308|     {
  309|         // All other nameds get stored
  310|         links->link(nodecontextdeclared->name(), describe(declared), declared->declaration());
  311|     }
  312|     void visit_modifier(Types::Modifier* mod)
  313|     {
  314|         // We recurse on the mod's alias, but dont link the const bit
  315|         if (mod->pre().size() && mod->pre().front() == "const")
  316|             if (!node->IsLeaf() && node->First()->Eq("const"))
  317|             {
  318|                 links->span(node->First(), "file-keyword");
  319|                 node = node->Last()->First();
  320|             }
  321|         mod->alias()->accept(this);
  322|     }
  323|     void visit_parameterized(Types::Parameterized* param)
  324|     {
  325|         // Sometimes there's a typename at the front..
  326|         if (node->First()->IsLeaf() && node->First()->Eq("typename"))
  327|             node = node->Second();
  328|         // Some modifiers nest the identifier..
  329|         while (!node->First()->IsLeaf())
  330|             node = node->First();
  331|         // For qualified template names the ptree is:
  332|         //  [ std :: [ vector [ < ... , ... > ] ] ]
  333|         // If the name starts with :: (global scope), skip it
  334|         if (node->First() && node->First()->Eq("::"))
  335|             node = node->Rest();
  336|         // Skip the qualifieds (and just link the final name)
  337|         while (node->Second() && node->Second()->Eq("::"))
  338|             if (node->Third()->IsLeaf())
  339|                 node = node->Rest()->Rest();
  340|             else
  341|                 node = node->Third();
  342|         // Do template
  343|         links->link(node->First(), param->template_type());
  344|         // Do params
  345|         node = node->Second();
  346|         typedef Types::Type::vector::iterator iterator;
  347|         iterator iter = param->parameters().begin();
  348|         iterator end = param->parameters().end();
  349|         // Could be leaf if eg: [SomeId const] node is now "const"
  350|         while (node && !node->IsLeaf() && iter != end)
  351|         {
  352|             // Skip '<' or ','
  353|             if ( !(node = node->Rest()) )
  354|                 break;
  355|             if (node->Car() && node->Car()->Car() && !node->Car()->Car()->IsLeaf() && node->Car()->Car()->Car())
  356|                 links->link(node->Car()->Car()->Car(), *iter);
  357|             ++iter;
  358|             node = node->Rest();
  359|         }
  360|     }
  361|     // Other types ignored, for now
  362| };
  363| 
  364| // A class that allows easy encoding of strings
  365| class LinkStore::encode
  366| {
  367| public:
  368|     const charstr;
  369|     encode(const char* s) : str(s)
  370|     { }
  371|     encode(const std::string& s) : str(s.c_str())
  372|     { }
  373| };
  374| 
  375| // Insertion operator for encode class
  376| std::ostreamoperator <<(std::ostream& out, const LinkStore::encode& enc)
  377| {
  378| #ifdef DEBUG
  379|     // Encoding makes it very hard to read.. :)
  380|     return out << enc.str;
  381| #else
  382| 
  383|     for (const chars = enc.str; *s; ++s)
  384|         if (isalnum(*s) || *s == '`' || *s == ':')
  385|             out << *s;
  386|         else
  387|             out << '%' << std::hex << std::setw(2) << std::setfill('0') << int(*s) << std::dec;
  388|     return out;
  389| #endif
  390| }
  391| 
  392| 
  393| // A class that allows easy encoding of ScopedNames
  394| class LinkStore::encode_name
  395| {
  396| public:
  397|     const ScopedNamename;
  398|     encode_name(const ScopedName& N) : name(N)
  399|     { }
  400| };
  401| 
  402| // Insertion operator for encode class
  403| std::ostreamoperator <<(std::ostream& out, const LinkStore::encode_name& enc)
  404| {
  405| #ifdef DEBUG
  406|     // Encoding makes it very hard to read.. :)
  407|     return out << LinkStore::encode(join(enc.name, "::"));
  408| #else
  409| 
  410|     return out << LinkStore::encode(join(enc.name"\t"));
  411| #endif
  412| }
  413| 
  414| // Store if type is named
  415| void LinkStore::link(Ptree* node, Types::Type* type, Context context)
  416| {
  417|     AST::SourceFilefile = m->walker->current_file();
  418|     if (!type || !m->filter->should_link(file))
  419|         return;
  420|     TypeStorer storer(this, node, context);
  421|     type->accept(&storer);
  422| }
  423| 
  424| void LinkStore::link(Ptree* node, const AST::Declaration* decl)
  425| {
  426|     AST::SourceFilefile = m->walker->current_file();
  427|     if (!decl || !m->filter->should_link(file))
  428|         return;
  429|     link(nodeDefinitiondecl->name(), decl->type(), decl);
  430| }
  431| 
  432| void LinkStore::span(int line, int col, int len, const char* desc)
  433| {
  434|     AST::SourceFilefile = m->walker->current_file();
  435|     if (!m->filter->should_link(file))
  436|         return;
  437|     std::ostreamout = get_syntax_stream(file);
  438| 
  439|     out << line << FS << col << FS << len << FS;
  440|     out << context_names[Span] << FS << encode(desc) << RS;
  441| }
  442| 
  443| void LinkStore::span(Ptree* node, const char* desc)
  444| {
  445|     int line = m->walker->line_of_ptree(node);
  446|     AST::SourceFilefile = m->walker->current_file();
  447|     if (!m->filter->should_link(file))
  448|         return;
  449|     int col = find_col(linenode->LeftMost());
  450|     if (col < 0)
  451|         return; // inside macro
  452|     int len = node->RightMost() - node->LeftMost();
  453| 
  454|     span(linecollendesc);
  455| }
  456| 
  457| void LinkStore::long_span(Ptree* node, const char* desc)
  458| {
  459|     // Find left edge
  460|     int left_line = m->walker->line_of_ptree(node);
  461|     AST::SourceFilefile = m->walker->current_file();
  462|     if (!m->filter->should_link(file))
  463|         return;
  464|     int left_col = find_col(left_linenode->LeftMost());
  465|     if (left_col < 0)
  466|         return; // inside macro
  467|     int len = node->RightMost() - node->LeftMost();
  468| 
  469|     // Find right edge
  470|     charfname;
  471|     int fname_len;
  472|     int right_line = m->parser->LineNumber(node->RightMost(), fnamefname_len);
  473| 
  474|     if (right_line == left_line)
  475|         // Same line, so normal output
  476|         span(left_lineleft_collendesc);
  477|     else
  478|     {
  479|         // Must output one for each line
  480|         int right_col = find_col(right_linenode->RightMost());
  481|         for (int line = left_lineline < right_lineline++, left_col = 0)
  482|             span(lineleft_col, -1desc);
  483|         // Last line is a bit different
  484|         span(right_line0right_coldesc);
  485|     }
  486| }
  487| 
  488| // Store a link in the Syntax File
  489| void LinkStore::store_syntax_record(AST::SourceFile* file, int line, int col, int len, Context context, const ScopedName& name, const std::string& desc)
  490| {
  491|     std::ostreamout = get_syntax_stream(file);
  492| 
  493|     // Start the record
  494|     out << line << FS << col << FS << len << FS;
  495|     out << context_names[context] << FS;
  496|     // Tab is used since :: is ambiguous as a scope separator unless real
  497|     // parsing is done, and the syntax highlighter generates links into the docs
  498|     // using the scope info
  499|     out << encode_name(name) << FS;
  500|     // Add the name to the description (REVISIT: This looks too complex!)
  501|     std::vector<AST::Scope*> scopes;
  502|     Types::Namedvtype;
  503|     ScopedName short_name;
  504|     if (m->walker->builder()->mapName(namescopesvtype))
  505|     {
  506|         for (size_t i = 0i < scopes.size(); i++)
  507|         {
  508|             if (AST::Namespacens = dynamic_cast<AST::Namespace*>(scopes[i]))
  509|                 if (ns->type() == "function")
  510|                 {
  511|                     // Restart description at function scope
  512|                     short_name.clear();
  513|                     continue;
  514|                 }
  515|             // Add the name to the short name
  516|             short_name.push_back(scopes[i]->name().back());
  517|         }
  518|         // Add the final type name to the short name
  519|         short_name.push_back(vtype->name().back());
  520|     }
  521|     else
  522|     {
  523|         STrace trace("LinkStore::link");
  524|         LOG("WARNING: couldnt map name " << name);
  525|         short_name = name;
  526|     }
  527|     out << encode(desc + " " + join(short_name,"::")) << RS;
  528| }
  529| 
  530| 
  531| // Store a link in the CrossRef File
  532| void LinkStore::store_xref_record(
  533|         AST::SourceFile* file, 
  534|         const AST::Declaration* decl, 
  535|         const std::string& filename, int line, Context context)
  536| {
  537|     std::ostreamout = get_xref_stream(file);
  538| 
  539|     AST::Scopecontainer = m->walker->builder()->scope();
  540|     std::string container_str = join(container->name(), "\t");
  541|     if (!container_str.size())
  542|         container_str = "\t";
  543|     out << encode_name(decl->name()) << FS << filename << FS << line << FS;
  544|     out << encode(container_str) << FS << context_names[context] << RS;
  545| }
  546| 
  547| // Gets the ofstream for a syntax file
  548| std::ostreamLinkStore::get_syntax_stream(AST::SourceFile* file)
  549| {
  550|     // Find the stream to use
  551|     Private::Streamsstreams = m->streams[file];
  552|     if (!streams.syntax)
  553|     {
  554|         std::string filename = m->filter->get_syntax_filename(file);
  555|         makedirs(filename.c_str());
  556|         streams.syntax = new std::ofstream(filename.c_str());
  557|     }
  558|     return *streams.syntax;
  559| }
  560| 
  561| // Gets the ofstream for a xref file
  562| std::ostreamLinkStore::get_xref_stream(AST::SourceFile* file)
  563| {
  564|     // Find the stream to use
  565|     Private::Streamsstreams = m->streams[file];
  566|     if (!streams.xref)
  567|     {
  568|         std::string filename = m->filter->get_xref_filename(file);
  569|         makedirs(filename.c_str());
  570|         streams.xref = new std::ofstream(filename.c_str());
  571|     }
  572|     return *streams.xref;
  573| }
  574| 
  575| // vim: set ts=8 sts=4 sw=4 et: