X-Git-Url: http://rtime.felk.cvut.cz/gitweb/boost-statechart-viewer.git/blobdiff_plain/147b691963a696716839a86144225d4ec89e43be..3602fe3023edf36a17aab8bbdf9e773f0eab7097:/src/visualizer.cpp diff --git a/src/visualizer.cpp b/src/visualizer.cpp index a32f03c..721ef1a 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -1,97 +1,172 @@ -#include -#include -#include -#include +/** @file */ +//////////////////////////////////////////////////////////////////////////////////////// +// +// This file is part of Boost Statechart Viewer. +// +// Boost Statechart Viewer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Boost Statechart Viewer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Boost Statechart Viewer. If not, see . +// +//////////////////////////////////////////////////////////////////////////////////////// +//standard header files +#include +//LLVM Header files #include "llvm/Support/raw_ostream.h" -#include "llvm/System/Host.h" +#include "llvm/Support/Host.h" #include "llvm/Config/config.h" -#include "clang/Frontend/DiagnosticOptions.h" +//clang header files #include "clang/Frontend/TextDiagnosticPrinter.h" - -#include "clang/Basic/LangOptions.h" - -#include "clang/Index/TranslationUnit.h" -#include "clang/Basic/SourceManager.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Basic/FileManager.h" - -#include "clang/Frontend/HeaderSearchOptions.h" #include "clang/Frontend/Utils.h" - -#include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" - #include "clang/Lex/Preprocessor.h" -#include "clang/Frontend/PreprocessorOptions.h" -#include "clang/Frontend/FrontendOptions.h" - -#include "clang/Frontend/CompilerInvocation.h" - -#include "clang/Basic/IdentifierTable.h" -#include "clang/Basic/Builtins.h" - -#include "clang/AST/ASTContext.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/AST/ASTConsumer.h" -#include "clang/Sema/Sema.h" -#include "clang/AST/DeclBase.h" -#include "clang/AST/Type.h" -#include "clang/AST/Decl.h" #include "clang/Sema/Lookup.h" -#include "clang/Sema/Ownership.h" -#include "clang/AST/DeclGroup.h" - -#include "clang/Parse/Parser.h" - #include "clang/Parse/ParseAST.h" #include "clang/Basic/Version.h" - -#include "llvm/Support/CommandLine.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Compilation.h" //my own header files -#include "stringoper.h" -#include "commandlineopt.h" +#include "iooper.h" using namespace clang; +using namespace clang::driver; +using namespace std; - +/** + * This class provides Simple diagnostic Client. It uses implementation in library for printing diagnostci information. + * Also it counts number of warnings, errors, ... When an error occurs the program is stopped. + */ class MyDiagnosticClient : public TextDiagnosticPrinter { + int nwarnings; /** Save number of Warnings occured during diagnostic */ + int nnotes; + int nignored; + int nerrors; public: - MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false){} + /** + * Initialize number of warnings, errors, ... + */ + MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false) + { + nwarnings=0; + nnotes=0; + nignored=0; + nerrors = 0; + } + /** + * This method prints diagnostic and counts diagnostic types. + */ virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info) { - TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); - if(DiagLevel == 3) exit(1); + TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information using library implementation + switch (DiagLevel) // count number of all diagnostic information + { + case 0 : nignored+=1; break; + case 1 : nnotes+=1; break; + case 2 : nwarnings+=1; break; + default : nerrors+=1; + print_stats(); + exit(1); + } + } + /** + * Print statistics about diagnostic. + */ + void print_stats() + { + cout<<"\n--Diagnostic Info--\n"; + cout<<"Number of ignored: "< transitions; - std::list states; - std::string name_of_machine; - std::string name_of_start; - FullSourceLoc *fSloc; + list transitions; + list cReactions; /** list of custom reactions. After all files are traversed this list should be empty. */ + list events; + list states; + string name_of_machine; + string name_of_start; + FullSourceLoc *fsloc; /** Full Source Location instance for holding Source Manager. */ public: - virtual void Initialize(ASTContext &ctx)//run after the AST is constructed + list getStates() /** Return list of states. */ + { + return states; + } + + list getTransitions() /** Return list of transitions. */ + { + return transitions; + } + + list getEvents() /** Return list of events. */ + { + return events; + } + + string getStateMachine() /** Return name of the state machine. */ + { + return name_of_machine; + } + + string getNameOfFirstState() /** Return name of start state. */ + { + return name_of_start; + } + + virtual void Initialize(ASTContext &ctx)/** Run after the AST is constructed before the consumer starts to work. So this function works like constructor. */ { - SourceLocation loc; + fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager()); name_of_start = ""; name_of_machine = ""; - SourceManager &sman = ctx.getSourceManager(); - fSloc = new FullSourceLoc(loc, sman); } - virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations +/** +* Traverse global decls using DeclGroupRef for handling all global decls. But only interesting decls are processed. Interesting decls are Struct, Class, C++ methods and Namespace. +* When Namespace is found it recursively traverse all decls inside this Namespace using method recursive_visit. +*/ + virtual void HandleTopLevelDecl(DeclGroupRef DGR) { - const SourceManager &sman = fSloc->getManager(); SourceLocation loc; - std::string line; - std::string super_class, output; + string line, output, event; llvm::raw_string_ostream x(output); for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) { @@ -99,371 +174,323 @@ class FindStates : public ASTConsumer loc = decl->getLocation(); if(loc.isValid()) { - const NamedDecl *namedDecl = dyn_cast(decl); - //std::cout<getDeclKindName()<<"\n"; + if(decl->getKind()==35) + { + method_decl(decl); + } if (const TagDecl *tagDecl = dyn_cast(decl)) { - if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class + if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class { - const CXXRecordDecl *cRecDecl = dyn_cast(decl); - decl->print(x); - //decl->dump(); - line = cut_commentary(clean_spaces(get_line_of_code(x.str()))); - output = ""; - if(is_derived(line)) - { - if(name_of_machine == "") - { - find_name_of_machine(cRecDecl, line); - } - else - { - if(find_states(cRecDecl, line)) - { - const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl); - std::cout << "New state: " << namedDecl->getNameAsString() << "\n"; - find_transitions(namedDecl->getNameAsString(), declCont); - } - } - } + struct_class(decl); } } if(const NamespaceDecl *namespaceDecl = dyn_cast(decl)) { + DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl); - //declCont->dumpDeclContext(); recursive_visit(declCont); } } + output = ""; } } - void recursive_visit(const DeclContext *declCont) //recursively visit all decls hidden inside namespaces + +/** +* It is used to recursive traverse decls in Namespaces. This method do the same as HandleTopLevelDecl. +*/ + void recursive_visit(const DeclContext *declCont) { - const SourceManager &sman = fSloc->getManager(); - std::string line, output; - SourceLocation loc; + string line, output, event; llvm::raw_string_ostream x(output); + SourceLocation loc; for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) { const Decl *decl = *i; - const NamedDecl *namedDecl = dyn_cast(decl); - - //std::cout<<"a "<getDeclKindName()<<"\n"; loc = decl->getLocation(); if(loc.isValid()) - { - if (const TagDecl *tagDecl = dyn_cast(decl)) + { + if(decl->getKind()==35) + { + method_decl(decl); + } + else if (const TagDecl *tagDecl = dyn_cast(decl)) { if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class { - const CXXRecordDecl *cRecDecl = dyn_cast(decl); - decl->print(x); - line = cut_commentary(clean_spaces(get_line_of_code(x.str()))); - output = ""; - if(is_derived(line)) - { - if(name_of_machine == "") - { - find_name_of_machine(cRecDecl, line); - } - else - { - if(find_states(cRecDecl, line)) - { - const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl); - //states.push_back(namedDecl->getNameAsString()); - std::cout << "New state: " << namedDecl->getNameAsString() << "\n"; - find_transitions(namedDecl->getNameAsString(), declCont); - } - } - } + struct_class(decl); } } - if(const NamespaceDecl *namespaceDecl = dyn_cast(decl)) + else if(const NamespaceDecl *namespaceDecl = dyn_cast(decl)) { - DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl); - //declCont->dumpDeclContext(); + DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl); recursive_visit(declCont); } } + output = ""; } } - bool find_states(const CXXRecordDecl *cRecDecl, std::string line) // test if the struct/class is the state (must be derived from simple_state) - { - std::string super_class = get_super_class(line), base; - if(cRecDecl->getNumBases()>1) + +/** +* This function works with class or struct. It splits the decl into 3 interesting parts. +* The state machine decl, state decl and event decl. +*/ + void struct_class(const Decl *decl) + { + string output, line, ret, trans, event; + llvm::raw_string_ostream x(output); + decl->print(x); + line = get_line_of_code(x.str()); + output = ""; + int pos; + const NamedDecl *namedDecl = dyn_cast(decl); + if(is_derived(line)) { - for(unsigned i = 0; igetNumBases();i++ ) - { - if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class); - else base = super_class; - if(is_state(super_class)) - { - //std::cout<(decl); + + if(find_events(cRecDecl, line)) { - //std::cout<getNameAsString()); } - else return false; - } - } - - void find_name_of_machine(const CXXRecordDecl *cRecDecl, std::string line) // find name of the state machine and the start state - { - std::string super_class = get_super_class(line), base, params; - - int pos = 0; - if(cRecDecl->getNumBases()>1) - { - for(unsigned i = 0; igetNumBases();i++ ) + else if(name_of_machine == "") { - if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class); - else base = super_class; - if(is_machine(base)) - { - params = get_params(base); - pos = params.find(","); - name_of_machine = params.substr(0,pos); - name_of_start = params.substr(pos); - std::cout<<"Name of the state machine: "<getNameAsString()); + } } } } - void find_transitions (const std::string name_of_state,const DeclContext *declCont) // traverse all methods for finding declarations of transitions - { - std::string output, line, dest, params, base; +/** +* This function provides traversing all methods and other context indide class. If +* typedef or classic method decl is found. If typedef is found then it is being testted for transitions and custom reactions. +*/ + void methods_in_class(const Decl *decl, const string state) + { + string output, line, ret, trans, event; llvm::raw_string_ostream x(output); - int num; + int pos, num; + const TagDecl *tagDecl = dyn_cast(decl); + const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl); + output=""; for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) { - const Decl *decl = *i; - if (const TypedefDecl *typedDecl = dyn_cast(decl)) + if (i->getKind()==26) // typedefs { - decl->print(x); - output = x.str(); - line = clean_spaces(cut_typedef(output)); - num = count(output,'<'); - if(num>1) - { - num-=1; - if(is_list(line)) - { - line = get_inner_part(line); - } - } - for(int j = 0;jprint(x); + output = x.str(); + line = clean_spaces(cut_type(output)); + ret = find_transitions(state,line); + if(!ret.empty()) + { + num = count(ret,';')+1; + for(int i = 0;igetKind()==35) method_decl(decl);// C++ method + } } - - void save_to_file(std::string output) + + /** + * Traverse method declaration using classes with main class Stmt. + */ + void method_decl(const Decl *decl) { - std::string state, str, context, ctx; - int pos1, pos2, cnt, subs; - std::ofstream filestr(output.c_str()); - std::cout<::iterator i = states.begin();i!=states.end();i++) // write all states in the context of the automaton + string output, line, event; + llvm::raw_string_ostream x(output); + if(decl->hasBody()) { - state = *i; - cnt = count(state,','); - if(cnt==1) + decl->print(x); + line = get_return(x.str()); + if(test_model(line,"result")) { - pos1 = state.find(","); - ctx = cut_namespaces(state.substr(pos1+1)); - //std::cout<(decl); + const ParmVarDecl *pvd = fDecl->getParamDecl(0); + QualType qt = pvd->getOriginalType(); + event = qt.getAsString(); + if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2); + event = event.substr(event.rfind(" ")+1); + line = dyn_cast(decl)->getQualifiedNameAsString(); + line = cut_namespaces(line.substr(0,line.rfind("::"))); + line.append(","); + line.append(event); + find_return_stmt(decl->getBody(),line); + for(list::iterator i = cReactions.begin();i!=cReactions.end();i++) // erase info about it from list of custom reactions { - filestr<getStmtClass() == 99) test_stmt(dyn_cast(statemt)->getSubStmt(), event); + else { - state = states.front(); - filestr<<"subgraph cluster"<::iterator i = states.begin();i!=states.end();i++) + for (Stmt::child_range range = statemt->children(); range; ++range) { - state = *i; - cnt = count(state,','); - //std::cout<::iterator i = transitions.begin();i!=transitions.end();i++) // write all transitions - { - state = *i; - pos1 = state.find(","); - filestr<"; - pos2 = state.rfind(","); - filestr<getManager(); + int type; + string line, param; + type = stmt->getStmtClass(); + switch(type) + { + case 8 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // do + break; + case 86 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // for + break; + case 88 : find_return_stmt(dyn_cast(stmt)->getThen(), event); //if then + find_return_stmt(dyn_cast(stmt)->getElse(), event); //if else + break; + case 90 : find_return_stmt(dyn_cast(stmt)->getSubStmt(), event); //label + break; + case 98 : line = sman.getCharacterData(dyn_cast(stmt)->getReturnLoc()); + line = get_line_of_code(line).substr(6); + line = line.substr(0,line.find("(")); + if(test_model(line,"transit")) + { + param = get_params(line); + transitions.push_back(event.append(",").append(param)); + } + break; + case 99 : find_return_stmt(stmt, event); + break; + case 101 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // switch + break; + case 102 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // while + break; + } } -}; -int main(int argc, char *argv[]) -{ - llvm::cl::ParseCommandLineOptions(argc, argv); - std::cout<<"Input file: "< dis(new DiagnosticIDs()); + Diagnostic diag(dis,mdc); + FileManager fm( * new FileSystemOptions()); + SourceManager sm (diag, fm); HeaderSearch *headers = new HeaderSearch(fm); - CompilerInvocation::setLangDefaults(lang, IK_ObjCXX); + + Driver TheDriver(LLVM_BINDIR, llvm::sys::getHostTriple(), "", false, false, diag); + TheDriver.setCheckInputsExist(true); + TheDriver.CCCIsCXX = 1; + TheDriver.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING; - HeaderSearchOptions hsopts; - hsopts.ResourceDir=LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING; - for(unsigned i = 0; i Args(argv, argv + argc); + llvm::OwningPtr C(TheDriver.BuildCompilation(Args.size(), + Args.data())); + const driver::JobList &Jobs = C->getJobs(); + const driver::Command *Cmd = cast(*Jobs.begin()); + const driver::ArgStringList &CCArgs = Cmd->getArguments(); + for(unsigned i = 0; i2) + { + string str = Args[i]; + outputFilename = str.substr(2); + } + else outputFilename = Args[i+1]; + break; + } } - TargetOptions to; - to.Triple = llvm::sys::getHostTriple(); - TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, to); - clang::ApplyHeaderSearchOptions( - *headers, - hsopts, - lang, - ti->getTriple()); + + CompilerInvocation::CreateFromArgs(compInv, + const_cast(CCArgs.data()), + const_cast(CCArgs.data())+CCArgs.size(), + diag); + + HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts(); + LangOptions lang = compInv.getLangOpts(); + CompilerInvocation::setLangDefaults(lang, IK_ObjCXX); + TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts()); + FrontendOptions f = compInv.getFrontendOpts(); + inputFilename = f.Inputs[0].second; + + cout<<"Input filename: "<BeginSourceFile(lang, &pp); + ASTContext ctx(lang, sm, *ti, tab, * new SelectorTable(), builtins,0); + mdc->BeginSourceFile(lang, &pp);//start using diagnostic ParseAST(pp, &c, ctx, false, false); - mdc->EndSourceFile(); - c.save_to_file(outputFile); + mdc->EndSourceFile(); //end using diagnostic + IO_operations *io = new IO_operations(outputFilename, c.getStateMachine(), c.getNameOfFirstState(), c.getTransitions(), c.getStates(), c.getEvents()); + io->save_to_file(); + io->print_stats(); + mdc->print_stats(); return 0; - }