]> rtime.felk.cvut.cz Git - boost-statechart-viewer.git/blob - src/visualizer.cpp
Better visualization of submachines. All submachines are now hidden in clusters label...
[boost-statechart-viewer.git] / src / visualizer.cpp
1 #include <iostream>
2 #include <string>
3 #include <fstream>
4 #include <list>
5
6
7 #include "llvm/Support/raw_ostream.h"
8 #include "llvm/System/Host.h"
9 #include "llvm/Config/config.h"
10
11 #include "clang/Frontend/DiagnosticOptions.h"
12 #include "clang/Frontend/TextDiagnosticPrinter.h"
13
14 #include "clang/Basic/LangOptions.h"
15 #include "clang/Basic/FileSystemOptions.h"
16
17 #include "clang/Index/TranslationUnit.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Lex/HeaderSearch.h"
20 #include "clang/Basic/FileManager.h"
21
22 #include "clang/Frontend/HeaderSearchOptions.h"
23 #include "clang/Frontend/Utils.h"
24
25 #include "clang/Basic/TargetOptions.h"
26 #include "clang/Basic/TargetInfo.h"
27
28 #include "clang/Lex/Preprocessor.h"
29 #include "clang/Frontend/PreprocessorOptions.h"
30 #include "clang/Frontend/FrontendOptions.h"
31
32 #include "clang/Frontend/CompilerInvocation.h"
33
34 #include "clang/Basic/IdentifierTable.h"
35 #include "clang/Basic/Builtins.h"
36
37 #include "clang/AST/ASTContext.h"
38 #include "clang/AST/ASTConsumer.h"
39 #include "clang/Sema/Sema.h"
40 #include "clang/AST/DeclBase.h"
41 #include "clang/AST/Type.h"
42 #include "clang/AST/Decl.h"
43 #include "clang/Sema/Lookup.h"
44 #include "clang/Sema/Ownership.h"
45 #include "clang/AST/DeclGroup.h"
46
47 #include "clang/Parse/Parser.h"
48
49 #include "clang/Parse/ParseAST.h"
50 #include "clang/Basic/Version.h"
51
52 #include "llvm/Support/CommandLine.h"
53
54 //my own header files
55 #include "stringoper.h"
56 #include "commandlineopt.h"
57
58 using namespace clang;
59
60
61 class FindStates : public ASTConsumer
62 {
63         std::list<string> transitions;
64         std::list<string> events;
65         std::list<string> states;
66         std::string name_of_machine;
67         std::string name_of_start;
68         public:
69
70         virtual void Initialize(ASTContext &ctx)//run after the AST is constructed
71         {       
72                 name_of_start = "";
73                 name_of_machine = "";
74         }
75
76         virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations
77         {
78                 SourceLocation loc;
79                 std::string line;
80                 std::string super_class, output;
81                 llvm::raw_string_ostream x(output);
82                 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) 
83                 {
84                         const Decl *decl = *i;
85                         loc = decl->getLocation();
86                         if(loc.isValid())
87                         {
88                                 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
89                                 //std::cout<<decl->getDeclKindName()<<"\n";
90                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
91                                 {
92                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
93                                         {
94                                                 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
95                                                 decl->print(x);
96                                                 //decl->dump();                                                 
97                                                 line = cut_commentary(clean_spaces(get_line_of_code(x.str())));
98                                                 output = "";
99                                                 if(is_derived(line))
100                                                 {
101                                                         if(name_of_machine == "")
102                                                         {
103                                                                 find_name_of_machine(cRecDecl, line);
104                                                         }
105                                                         else
106                                                         {
107                                                                 if(find_states(cRecDecl, line))
108                                                                 {                               
109                                                                         const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);                                      
110                                                                         std::cout << "New state: " << namedDecl->getNameAsString() << "\n";
111                                                                         find_transitions(namedDecl->getNameAsString(), declCont);
112                                                                 }
113                                                         }
114                                                 }
115                                         }
116                                 }       
117                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
118                                 {
119                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
120                                         //declCont->dumpDeclContext();                          
121                                         recursive_visit(declCont);
122                                 
123                                 }
124                         }
125                 }
126         }
127         void recursive_visit(const DeclContext *declCont) //recursively visit all decls inside namespace
128         {
129                 std::string line, output;
130                 SourceLocation loc;
131                 llvm::raw_string_ostream x(output);
132                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
133                 {
134                         const Decl *decl = *i;
135                         const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
136                         
137                         //std::cout<<"a "<<decl->getDeclKindName()<<"\n";
138                         loc = decl->getLocation();
139                         if(loc.isValid())
140                         {                       
141                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
142                                 {
143                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
144                                         {
145                                                 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
146                                                 decl->print(x);
147                                                 line = cut_commentary(clean_spaces(get_line_of_code(x.str())));
148                                                 output = "";
149                                                 if(is_derived(line))
150                                                 {
151                                                         if(name_of_machine == "")
152                                                         {
153                                                                 find_name_of_machine(cRecDecl, line);
154                                                         }
155                                                         else
156                                                         {
157                                                                 if(find_states(cRecDecl, line))
158                                                                 {                               
159                                                                         const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);                                      
160                                                                         //states.push_back(namedDecl->getNameAsString());
161                                                                         std::cout << "New state: " << namedDecl->getNameAsString() << "\n";
162                                                                         find_transitions(namedDecl->getNameAsString(), declCont);
163                                                                 }
164                                                         }
165                                                 }
166                                         }       
167                                 }
168                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
169                                 {
170                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
171                                         //declCont->dumpDeclContext();                          
172                                         recursive_visit(declCont);
173                                 }
174                         }
175                 } 
176         }
177         bool find_states(const CXXRecordDecl *cRecDecl, std::string line)
178         {       
179                 std::string super_class = get_super_class(line), base;          
180                 if(cRecDecl->getNumBases()>1)
181                 {
182                         for(int i = 0; i<cRecDecl->getNumBases();i++ )
183                         {
184                                 if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class);
185                                 else base = super_class;
186                                 if(is_state(super_class)) 
187                                 {
188                                         //std::cout<<get_params(super_class);
189                                         states.push_back(get_params(super_class));
190                                         return true;
191                                 }
192                                 else
193                                 {
194                                         super_class = get_next_base(super_class);
195                                 }
196                         }
197                         return false;
198                 }
199                 else
200                 { 
201                         if(is_state(super_class)) 
202                         {
203                                 //std::cout<<get_params(super_class);
204                                 states.push_back(get_params(super_class));
205                                 return true;
206                         }
207                         else return false;
208                 }
209         }
210                 
211         void find_name_of_machine(const CXXRecordDecl *cRecDecl, std::string line)
212         {       
213                 std::string super_class = get_super_class(line), base, params;
214                 
215                 int pos = 0;
216                 if(cRecDecl->getNumBases()>1)
217                 {
218                         for(int i = 0; i<cRecDecl->getNumBases();i++ )
219                         {
220                                 if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class);
221                                 else base = super_class;
222                                 if(is_machine(base))
223                                 {
224                                         params = get_params(base);
225                                         pos = params.find(",");
226                                         name_of_machine = params.substr(0,pos);
227                                         name_of_start = params.substr(pos);
228                                         std::cout<<"Name of the state machine: "<<name_of_machine<<"\n";
229                                         std::cout<<"Name of the first state: "<<name_of_start<<"\n";
230                                 }
231                                 else
232                                 {
233                                         super_class = get_next_base(super_class);
234                                 }
235                         }
236                 }
237                 else
238                 { 
239                         if(is_machine(super_class))
240                         {
241                                 //std::cout<<super_class;
242                                 params = get_params(super_class);
243                                 //std::cout<<params;
244                                 pos = params.find(",");
245                                 name_of_machine = cut_namespaces(params.substr(0,pos));
246                                 name_of_start = cut_namespaces(params.substr(pos+1));
247                                 std::cout<<"Name of the state machine:"<<name_of_machine<<"\n";
248                                 std::cout<<"Name of the first state:"<<name_of_start<<"\n";
249                         }
250                 }
251         }
252
253         void find_transitions (const std::string name_of_state,const DeclContext *declCont) // traverse all methods for finding declarations of transitions
254         {       
255                 std::string output, line, dest, params, base;   
256                 llvm::raw_string_ostream x(output);
257                 int pos;
258                 int num;                
259                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) 
260                 {
261                         const Decl *decl = *i;
262                         
263                         if (const TypedefDecl *typeDecl = dyn_cast<TypedefDecl>(decl)) 
264                         {
265                                         decl->print(x);
266                                         output = x.str();
267                                         line = clean_spaces(cut_typedef(output));
268                                         num = count(output,'<');                                        
269                                         if(num>1)
270                                         {
271                                                 num-=1;
272                                                 if(is_list(line))
273                                                 {
274                                                         line = get_inner_part(line);
275                                                 }
276                                         }
277                                         for(int j = 0;j<num;j++)
278                                         {
279                                                 if(j!=num-1) base = get_first_base(line);                       
280                                                 else base = line;
281                                                 if(is_transition(base))
282                                                 {
283                                                         dest = name_of_state;
284                                                         params = get_params(base);
285                                                         dest.append(",");                                                       
286                                                         dest.append(params);
287                                                         transitions.push_back(dest);
288                                                         line = get_next_base(line);
289                                                 }
290                                                 else
291                                                 {
292                                                         line = get_next_base(line);
293                                                 }
294                                         }
295                         }
296                 }       
297         }
298         void save_to_file(std::string output)
299         {
300                 std::string state, str, context, ctx;
301                 int pos1, pos2, cnt, subs;
302                 std::ofstream filestr(output.c_str());
303                 std::cout<<output<<"\n";
304                 filestr<<"digraph "<< name_of_machine<< " {\n";
305                 context = name_of_machine;
306                 for(list<string>::iterator i = states.begin();i!=states.end();i++)
307                 {
308                         state = *i;
309                         cnt = count(state,',');
310                         if(cnt==1)
311                         {
312                                 pos1 = state.find(",");
313                                 ctx = cut_namespaces(state.substr(pos1+1));
314                                 //std::cout<<name_of_machine.length();                          
315                                 if(ctx.compare(0,context.length(),context)==0)
316                                 {
317                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
318                                         states.erase(i);
319                                         i--;
320                                 }
321                         }
322                         if(cnt==2)
323                         {
324                                 pos1 = state.find(",");
325                                 pos2 = state.rfind(",");
326                                 ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
327                                 if(ctx.compare(0,context.length(),context))
328                                 {                               
329                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
330                                 }
331                         }
332                 }
333                 filestr<<name_of_start<<" [peripheries=2] ;\n";
334                 subs = 0;
335                 while(!states.empty())
336                 {
337                         state = states.front();
338                         filestr<<"subgraph cluster"<<subs<<" {\n";                      
339                         pos1 = state.find(",");
340                         pos2 = state.rfind(",");
341                         context = cut_namespaces(state.substr(0,pos1));
342                         filestr<<"label=\""<<context<<"\";\n";
343                         filestr<<cut_namespaces(state.substr(pos2+1))<<" [peripheries=2] ;\n";  
344                         states.pop_front();     
345                         //std::cout<<states.size();     
346                         for(list<string>::iterator i = states.begin();i!=states.end();i++)
347                         {
348                                 state = *i;
349                                 cnt = count(state,',');
350                                 //std::cout<<state<<"\n";
351                                 if(cnt==1)
352                                 {
353                                         pos1 = state.find(",");
354                                         ctx = cut_namespaces(state.substr(pos1+1));
355                                         if(ctx.compare(0,context.length(),context)==0)
356                                         {
357                                                 filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
358                                         }
359                                         states.erase(i);
360                                         i--;
361                                 }
362                                 if(cnt==2)
363                                 {
364                                         pos1 = state.find(",");
365                                         pos2 = state.rfind(",");
366                                         ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
367                                         if(ctx.compare(0,context.length(),context))
368                                         {                               
369                                                 filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
370                                         }
371                                 }
372                         }
373                         filestr<<"}\n";
374                         subs+=1;        
375                 }               
376                 for(list<string>::iterator i = transitions.begin();i!=transitions.end();i++)
377                 {
378                         state = *i;
379                         pos1 = state.find(",");
380                         filestr<<cut_namespaces(state.substr(0,pos1))<<"->";
381                         pos2 = state.rfind(",");
382                         filestr<<cut_namespaces(state.substr(pos2+1));
383                         filestr<<"[label=\""<<cut_namespaces(state.substr(pos1+1,pos2-pos1-1))<<"\"];\n";
384                 }               
385                 filestr<<"}";
386                 filestr.close();
387         }
388 };
389
390 int main(int argc, char *argv[])
391 {
392         llvm::cl::ParseCommandLineOptions(argc, argv);  
393         std::cout<<"Input file: "<<inputFilename<<"\n"; 
394         DiagnosticOptions diagnosticOptions;
395         TextDiagnosticPrinter *tdp = new TextDiagnosticPrinter(llvm::nulls(), diagnosticOptions);
396         llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());
397         Diagnostic diag(dis,tdp);
398         FileSystemOptions fileSysOpt;     
399         LangOptions lang;
400         lang.BCPLComment=1;
401         lang.CPlusPlus=1; 
402         FileManager fm (fileSysOpt);
403
404         SourceManager sm ( diag, fm);
405         HeaderSearch *headers = new HeaderSearch(fm);
406         CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
407
408         HeaderSearchOptions hsopts;
409         hsopts.ResourceDir=LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
410         for(int i = 0; i<includeFiles.size();i++)
411         {       
412                 hsopts.AddPath(includeFiles[i],
413                                 clang::frontend::Angled,
414                                 false,
415                                 false,
416                                 true);
417         }
418         TargetOptions to;
419         to.Triple = llvm::sys::getHostTriple();
420         TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, to);
421         clang::ApplyHeaderSearchOptions(
422         *headers,
423         hsopts,
424         lang,
425         ti->getTriple());
426         Preprocessor pp(diag, lang, *ti, sm, *headers);
427         FrontendOptions f;
428         PreprocessorOptions ppio;
429         InitializePreprocessor(pp, ppio,hsopts,f);
430         const FileEntry *file = fm.getFile(inputFilename);
431         sm.createMainFileID(file);
432         IdentifierTable tab(lang);
433         SelectorTable sel;
434         Builtin::Context builtins(*ti);
435         FindStates c;
436         ASTContext ctx(lang, sm, *ti, tab, sel, builtins,0);
437         tdp->BeginSourceFile(lang, &pp);
438         ParseAST(pp, &c, ctx, false, false);
439         tdp->EndSourceFile();
440         
441         c.save_to_file(outputFile);
442         return 0;
443
444 }