]> rtime.felk.cvut.cz Git - boost-statechart-viewer.git/blob - src/visualizer.cpp
Add simple error dumper. Works only if the error is in transition. The state shouldn...
[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         FullSourceLoc *fSloc;
69         public:
70
71         virtual void Initialize(ASTContext &ctx)//run after the AST is constructed
72         {       
73                 SourceLocation loc;
74                 name_of_start = "";
75                 name_of_machine = "";
76                 SourceManager &sman = ctx.getSourceManager();
77                 fSloc = new FullSourceLoc(loc, sman);
78         }
79
80         virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations
81         {
82                 SourceLocation loc;
83                 std::string line;
84                 std::string super_class, output;
85                 llvm::raw_string_ostream x(output);
86                 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) 
87                 {
88                         const Decl *decl = *i;
89                         loc = decl->getLocation();
90                         if(loc.isValid())
91                         {
92                                 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
93                                 //std::cout<<decl->getDeclKindName()<<"\n";
94                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
95                                 {
96                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
97                                         {
98                                                 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
99                                                 decl->print(x);
100                                                 //decl->dump();                                                 
101                                                 line = cut_commentary(clean_spaces(get_line_of_code(x.str())));
102                                                 output = "";
103                                                 if(is_derived(line))
104                                                 {
105                                                         if(name_of_machine == "")
106                                                         {
107                                                                 find_name_of_machine(cRecDecl, line);
108                                                         }
109                                                         else
110                                                         {
111                                                                 if(find_states(cRecDecl, line))
112                                                                 {                               
113                                                                         const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);                                      
114                                                                         std::cout << "New state: " << namedDecl->getNameAsString() << "\n";
115                                                                         find_transitions(namedDecl->getNameAsString(), declCont);
116                                                                 }
117                                                         }
118                                                 }
119                                         }
120                                 }       
121                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
122                                 {
123                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
124                                         //declCont->dumpDeclContext();                          
125                                         recursive_visit(declCont);
126                                 
127                                 }
128                         }
129                 }
130         }
131         void recursive_visit(const DeclContext *declCont) //recursively visit all decls hidden inside namespaces
132         {
133                 std::string line, output;
134                 SourceLocation loc;
135                 llvm::raw_string_ostream x(output);
136                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
137                 {
138                         const Decl *decl = *i;
139                         const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
140                         
141                         //std::cout<<"a "<<decl->getDeclKindName()<<"\n";
142                         loc = decl->getLocation();
143                         if(loc.isValid())
144                         {                       
145                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
146                                 {
147                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
148                                         {
149                                                 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
150                                                 decl->print(x);
151                                                 line = cut_commentary(clean_spaces(get_line_of_code(x.str())));
152                                                 output = "";
153                                                 if(is_derived(line))
154                                                 {
155                                                         if(name_of_machine == "")
156                                                         {
157                                                                 find_name_of_machine(cRecDecl, line);
158                                                         }
159                                                         else
160                                                         {
161                                                                 if(find_states(cRecDecl, line))
162                                                                 {                               
163                                                                         const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);              
164                                                                         //states.push_back(namedDecl->getNameAsString());
165                                                                         std::cout << "New state: " << namedDecl->getNameAsString() << "\n";
166                                                                         find_transitions(namedDecl->getNameAsString(), declCont);
167                                                                 }
168                                                         }
169                                                 }
170                                         }       
171                                 }
172                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
173                                 {
174                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
175                                         //declCont->dumpDeclContext();                          
176                                         recursive_visit(declCont);
177                                 }
178                         }
179                 } 
180         }
181         bool find_states(const CXXRecordDecl *cRecDecl, std::string line) // test if the struct/class is the state (must be derived from simple_state)
182         {       
183                 std::string super_class = get_super_class(line), base;          
184                 if(cRecDecl->getNumBases()>1)
185                 {
186                         for(unsigned i = 0; i<cRecDecl->getNumBases();i++ )
187                         {
188                                 if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class);
189                                 else base = super_class;
190                                 if(is_state(super_class)) 
191                                 {
192                                         //std::cout<<get_params(super_class);
193                                         states.push_back(get_params(super_class));
194                                         return true;
195                                 }
196                                 else
197                                 {
198                                         super_class = get_next_base(super_class);
199                                 }
200                         }
201                         return false;
202                 }
203                 else
204                 { 
205                         if(is_state(super_class)) 
206                         {
207                                 //std::cout<<get_params(super_class);
208                                 states.push_back(get_params(super_class));
209                                 return true;
210                         }
211                         else return false;
212                 }
213         }
214                 
215         void find_name_of_machine(const CXXRecordDecl *cRecDecl, std::string line) // find name of the state machine and the start state
216         {       
217                 std::string super_class = get_super_class(line), base, params;
218                 
219                 int pos = 0;
220                 if(cRecDecl->getNumBases()>1)
221                 {
222                         for(unsigned i = 0; i<cRecDecl->getNumBases();i++ )
223                         {
224                                 if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class);
225                                 else base = super_class;
226                                 if(is_machine(base))
227                                 {
228                                         params = get_params(base);
229                                         pos = params.find(",");
230                                         name_of_machine = params.substr(0,pos);
231                                         name_of_start = params.substr(pos);
232                                         std::cout<<"Name of the state machine: "<<name_of_machine<<"\n";
233                                         std::cout<<"Name of the first state: "<<name_of_start<<"\n";
234                                 }
235                                 else
236                                 {
237                                         super_class = get_next_base(super_class);
238                                 }
239                         }
240                 }
241                 else
242                 { 
243                         if(is_machine(super_class))
244                         {
245                                 //std::cout<<super_class;
246                                 params = get_params(super_class);
247                                 //std::cout<<params;
248                                 pos = params.find(",");
249                                 name_of_machine = cut_namespaces(params.substr(0,pos));
250                                 name_of_start = cut_namespaces(params.substr(pos+1));
251                                 std::cout<<"Name of the state machine:"<<name_of_machine<<"\n";
252                                 std::cout<<"Name of the first state:"<<name_of_start<<"\n";
253                         }
254                 }
255         }
256
257         void find_transitions (const std::string name_of_state,const DeclContext *declCont) // traverse all methods for finding declarations of transitions
258         {       
259                 std::string output, line, dest, params, base;   
260                 llvm::raw_string_ostream x(output);
261                 int num;                
262                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) 
263                 {
264                         const Decl *decl = *i;
265                         if (const TypedefDecl *typedDecl = dyn_cast<TypedefDecl>(decl)) 
266                         {
267                                         decl->print(x);
268                                         output = x.str();
269                                         line = clean_spaces(cut_typedef(output));
270                                         num = count(output,'<');
271                                         if(line.compare(0,12,"intreactions")==0) 
272                                         {
273                                                 std::cout<<"Error during finding transitions. At least one of the transitions in this state("<<name_of_state<<") is not valid.\n";
274                                                 dump_error(decl);
275                                                 exit(1);                                        
276                                         }                       
277                                         if(num>1)
278                                         {
279                                                 num-=1;
280                                                 if(is_list(line))
281                                                 {
282                                                         line = get_inner_part(line);
283                                                 }
284                                         }
285                                         for(int j = 0;j<num;j++)
286                                         {
287                                                 if(j!=num-1) base = get_first_base(line);                       
288                                                 else base = line;
289                                                 if(is_transition(base))
290                                                 {
291                                                         dest = name_of_state;
292                                                         params = get_params(base);
293                                                         dest.append(",");                                                       
294                                                         dest.append(params);
295                                                         transitions.push_back(dest);
296                                                         line = get_next_base(line);
297                                                 }
298                                                 else
299                                                 {
300                                                         line = get_next_base(line);
301                                                 }
302                                         }
303                                         output = "";
304                         }
305                 }       
306         }
307         void dump_error (const Decl *decl)
308         {
309                 std::string output;
310                 int pos1, pos2;         
311                 const SourceManager &sman = fSloc->getManager();
312                 const SourceLocation loc = decl->getLocation();
313                 llvm::raw_string_ostream x(output);             
314                 loc.print(x,sman);
315                 output = x.str();
316                 pos1 = output.find(":");
317                 pos2 = output.rfind(":");
318                 std::cout<<"In file: "<<output.substr(0,pos1)<<" at line: "<<output.substr(pos1+1, pos2-pos1-1) <<"\n";
319         }
320         void save_to_file(std::string output)
321         {
322                 std::string state, str, context, ctx;
323                 int pos1, pos2, cnt, subs;
324                 std::ofstream filestr(output.c_str());
325                 std::cout<<output<<"\n";
326                 filestr<<"digraph "<< name_of_machine<< " {\n";
327                 context = name_of_machine;
328                 for(list<string>::iterator i = states.begin();i!=states.end();i++) // write all states in the context of the automaton
329                 {
330                         state = *i;
331                         cnt = count(state,',');
332                         if(cnt==1)
333                         {
334                                 pos1 = state.find(",");
335                                 ctx = cut_namespaces(state.substr(pos1+1));
336                                 //std::cout<<name_of_machine.length();                          
337                                 if(ctx.compare(0,context.length(),context)==0)
338                                 {
339                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
340                                         states.erase(i);
341                                         i--;
342                                 }
343                         }
344                         if(cnt==2)
345                         {
346                                 pos1 = state.find(",");
347                                 pos2 = state.rfind(",");
348                                 ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
349                                 //std::cout<<ctx<<" "<<context<<"\n";
350                                 if(ctx.compare(0,context.length(),context)==0)
351                                 {                               
352                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
353                                 }
354                         }
355                 }
356                 filestr<<name_of_start<<" [peripheries=2] ;\n";
357                 subs = 0;
358                 while(!states.empty()) // substates ?
359                 {
360                         state = states.front();
361                         filestr<<"subgraph cluster"<<subs<<" {\n";                      
362                         pos1 = state.find(",");
363                         pos2 = state.rfind(",");
364                         context = cut_namespaces(state.substr(0,pos1));
365                         filestr<<"label=\""<<context<<"\";\n";
366                         filestr<<cut_namespaces(state.substr(pos2+1))<<" [peripheries=2] ;\n";  
367                         states.pop_front();     
368                         //std::cout<<states.size();     
369                         for(list<string>::iterator i = states.begin();i!=states.end();i++)
370                         {
371                                 state = *i;
372                                 cnt = count(state,',');
373                                 //std::cout<<state<<" \n";
374                                 if(cnt==1)
375                                 {
376                                         pos1 = state.find(",");
377                                         ctx = cut_namespaces(state.substr(pos1+1));
378                                         
379                                         //std::cout<<ctx<<" "<<context<<"\n";
380                                         if(ctx.compare(0,context.length(),context)==0)
381                                         {
382                                                 filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
383                                                 states.erase(i);
384                                                 i--;
385                                         }
386                                 }
387                                 if(cnt==2)
388                                 {
389                                         pos1 = state.find(",");
390                                         pos2 = state.rfind(",");
391                                         ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
392                                         if(ctx.compare(0,context.length(),context)==0)
393                                         {                               
394                                                 filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
395                                                 //std::cout<<ctx<<"\n";
396                                         }
397                                 }
398                         }
399                         filestr<<"}\n";
400                         subs+=1;        
401                 }               
402                 for(list<string>::iterator i = transitions.begin();i!=transitions.end();i++) // write all transitions
403                 {
404                         state = *i;
405                         pos1 = state.find(",");
406                         filestr<<cut_namespaces(state.substr(0,pos1))<<"->";
407                         pos2 = state.rfind(",");
408                         filestr<<cut_namespaces(state.substr(pos2+1));
409                         filestr<<"[label=\""<<cut_namespaces(state.substr(pos1+1,pos2-pos1-1))<<"\"];\n";
410                 }               
411                 filestr<<"}";
412                 filestr.close();
413         }
414 };
415
416 int main(int argc, char *argv[])
417 {
418         llvm::cl::ParseCommandLineOptions(argc, argv);  
419         std::cout<<"Input file: "<<inputFilename<<"\n"; 
420         DiagnosticOptions diagnosticOptions;
421         TextDiagnosticPrinter *tdp = new TextDiagnosticPrinter(llvm::nulls(), diagnosticOptions);
422         llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());
423         Diagnostic diag(dis,tdp);
424         FileSystemOptions fileSysOpt;     
425         LangOptions lang;
426         lang.BCPLComment=1;
427         lang.CPlusPlus=1; 
428         FileManager fm (fileSysOpt);
429
430         SourceManager sm ( diag, fm);
431         HeaderSearch *headers = new HeaderSearch(fm);
432         CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
433
434         HeaderSearchOptions hsopts;
435         hsopts.ResourceDir=LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
436         for(unsigned i = 0; i<includeFiles.size();i++)
437         {
438                 hsopts.AddPath(includeFiles[i],
439                                 clang::frontend::Angled,
440                                 false,
441                                 false,
442                                 true);
443         }
444         TargetOptions to;
445         to.Triple = llvm::sys::getHostTriple();
446         TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, to);
447         clang::ApplyHeaderSearchOptions(
448                 *headers,
449                 hsopts,
450                 lang,
451                 ti->getTriple());
452         Preprocessor pp(diag, lang, *ti, sm, *headers);
453         FrontendOptions f;
454         PreprocessorOptions ppio;
455         InitializePreprocessor(pp, ppio,hsopts,f);
456         const FileEntry *file = fm.getFile(inputFilename);
457         sm.createMainFileID(file);
458         IdentifierTable tab(lang);
459         SelectorTable sel;
460         Builtin::Context builtins(*ti);
461         FindStates c;
462         ASTContext ctx(lang, sm, *ti, tab, sel, builtins,0);
463         tdp->BeginSourceFile(lang, &pp);
464         ParseAST(pp, &c, ctx, false, false);
465         tdp->EndSourceFile();
466         c.save_to_file(outputFile);
467         return 0;
468
469 }