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|
90| const char* FS = " ";
91|
92|
93| const char* RS = "\n";
94|
95| const char* context_names[] =
96| {
97| "REF",
98| "DEF",
99| "SPAN",
100| "IMPL",
101| "UDIR",
102| "UDEC",
103| "CALL"
104| };
105|
106|
107|
108| void makedirs(const char* path)
109| {
110| static char buf[1024];
111| strcpy(buf, path);
112| struct stat st;
113| int error;
114| char* ptr = 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|
128| continue;
129| *sep = 0;
130| // Try to stat this dir
131| if ((error = stat(buf, &st)) == -1 && errno == ENOENT)
132| mkdir(buf, 0755);
133| else if (error)
134| {
135| perror(buf);
136| return;
137| }
138|
139| *sep = '/';
140| // Move past /
141| ptr++;
142| }
143| }
144|
145| }
146|
147| struct LinkStore::Private
148| {
149|
150| const char* buffer_start;
151|
152|
153| FileFilter* filter;
154|
155|
156| Parser* parser;
157|
158|
159| SWalker* walker;
160|
161|
162| struct Streams {
163| std::ofstream* syntax;
164| std::ofstream* xref;
165| Streams() : syntax(NULL), xref(NULL) {}
166| Streams(const Streams& o) : syntax(o.syntax), xref(o.xref) {}
167| Streams& operator =(const Streams& o)
168| {
169| syntax = o.syntax;
170| xref = o.xref;
171| return *this;
172| }
173| };
174|
175|
176| typedef std::map<AST::SourceFile*,Streams> StreamsMap;
177|
178|
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::string* main;
199| const std::vector<std::string>* extra;
200| filter->get_all_filenames(main, extra);
201|
202|
203|
204| AST::SourceFile* file = filter->get_sourcefile(main->c_str());
205| get_syntax_stream(file);
206| get_xref_stream(file);
207|
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|
221| Private::StreamsMap::iterator iter = m->streams.begin();
222| while (iter != m->streams.end())
223| {
224| Private::Streams& streams = (iter++)->second;
225| if (streams.syntax) delete streams.syntax;
226| if (streams.xref) delete streams.xref;
227| }
228| delete m;
229| }
230|
231| SWalker* LinkStore::swalker()
232| {
233| return m->walker;
234| }
235|
236| int LinkStore::find_col(int line, const char* ptr)
237| {
238| const char* pos = 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(line, col);
244| }
245|
246| void LinkStore::link(Ptree* node, Context context, const ScopedName& name, const std::string& desc, const AST::Declaration* decl)
247| {
248| AST::SourceFile* file = 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(file, decl, file->filename(), line, context);
258|
259| // Get info for storing a syntax record
260| int col = find_col(line, node->LeftMost());
261| if (col < 0)
262| return; // inside macro
263| int len = node->RightMost() - node->LeftMost();
264|
265| store_syntax_record(file, line, col, len, context, name, desc);
266| }
267|
268|
269|
270| class TypeStorer : public Types::Visitor
271| {
272|
273| LinkStore* links;
274| Ptree* node;
275| LinkStore::Context context;
276| public:
277|
278| TypeStorer(LinkStore* ls, Ptree* n, LinkStore::Context c)
279| : links(ls), node(n), context(c)
280| { }
281|
282|
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|
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(node, context, named->name(), describe(named));
306| }
307| void visit_declared(Types::Declared* declared)
308| {
309| // All other nameds get stored
310| links->link(node, context, declared->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|
362| };
363|
364|
365| class LinkStore::encode
366| {
367| public:
368| const char* str;
369| encode(const char* s) : str(s)
370| { }
371| encode(const std::string& s) : str(s.c_str())
372| { }
373| };
374|
375|
376| std::ostream& operator <<(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 char* s = 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|
394| class LinkStore::encode_name
395| {
396| public:
397| const ScopedName& name;
398| encode_name(const ScopedName& N) : name(N)
399| { }
400| };
401|
402|
403| std::ostream& operator <<(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::SourceFile* file = 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::SourceFile* file = m->walker->current_file();
427| if (!decl || !m->filter->should_link(file))
428| return;
429| link(node, Definition, decl->name(), decl->type(), decl);
430| }
431|
432| void LinkStore::span(int line, int col, int len, const char* desc)
433| {
434| AST::SourceFile* file = m->walker->current_file();
435| if (!m->filter->should_link(file))
436| return;
437| std::ostream& out = 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::SourceFile* file = m->walker->current_file();
447| if (!m->filter->should_link(file))
448| return;
449| int col = find_col(line, node->LeftMost());
450| if (col < 0)
451| return; // inside macro
452| int len = node->RightMost() - node->LeftMost();
453|
454| span(line, col, len, desc);
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::SourceFile* file = m->walker->current_file();
462| if (!m->filter->should_link(file))
463| return;
464| int left_col = find_col(left_line, node->LeftMost());
465| if (left_col < 0)
466| return; // inside macro
467| int len = node->RightMost() - node->LeftMost();
468|
469| // Find right edge
470| char* fname;
471| int fname_len;
472| int right_line = m->parser->LineNumber(node->RightMost(), fname, fname_len);
473|
474| if (right_line == left_line)
475| // Same line, so normal output
476| span(left_line, left_col, len, desc);
477| else
478| {
479| // Must output one for each line
480| int right_col = find_col(right_line, node->RightMost());
481| for (int line = left_line; line < right_line; line++, left_col = 0)
482| span(line, left_col, -1, desc);
483| // Last line is a bit different
484| span(right_line, 0, right_col, desc);
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::ostream& out = 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|
501| std::vector<AST::Scope*> scopes;
502| Types::Named* vtype;
503| ScopedName short_name;
504| if (m->walker->builder()->mapName(name, scopes, vtype))
505| {
506| for (size_t i = 0; i < scopes.size(); i++)
507| {
508| if (AST::Namespace* ns = 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::ostream& out = get_xref_stream(file);
538|
539| AST::Scope* container = 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::ostream& LinkStore::get_syntax_stream(AST::SourceFile* file)
549| {
550|
551| Private::Streams& streams = 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::ostream& LinkStore::get_xref_stream(AST::SourceFile* file)
563| {
564|
565| Private::Streams& streams = 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|