/////////////////////////////////////////////////////////////// // grammar.cpp - detect and format semi-expressions with // // ver 2.3 selected syntactic forms // // // // Language: Visual C++, ver 6.0 // // Platform: Micron Dual Pentium Pro 200, Win NT 4.0 // // Application: CSE784 Project #1, Fall 1999 // // Author: Jim Fawcett, CST 2-187, Syracuse Univ. // // (315) 443-3948, fawcett@ecs.syr.edu // /////////////////////////////////////////////////////////////// #pragma warning(disable : 4786) #include #include #include #include #include #include "grammar.h" #include "cmdline.h" using namespace std; std::string moduleName::moduleName_ = ""; //----< define static members of the detector class >---------- vector detector::limitsExceeded; vector detector::globals; vector detector::publicData; vector detector::pointersReturned; bool detector::strip = false; bool grammar::_nestedComments = true; int detector::totalFuncLines = 0; int detector::totalCyclo = 0; int grammar ::totalLines = 0; int detector::maxFuncLines = 0; int detector::maxCyclo = 0; string functionDef::funcName = ""; //----< analyze file >----------------------------------------- void grammar::processFile(istream &in) { semiExp se(in); se.nestedComments(_nestedComments); while(se.get()) { doAnalyze(se); } totalLines += se.lines(); // if(se.lines() > maxFuncLines) // maxFuncLines = se.lines(); } //----< acquire semiexpression to analyze >-------------------- void grammar::analyze(detector &d) { detect.push_back(&d); } //----< has this grammatical type been detected? >------------- void grammar::doAnalyze(semiExp &se) { vector::iterator it; string debug = se[0]; for(it=detect.begin(); it!=detect.end(); it++) { if((*it)->operator()(se)) { cout << ((*it)->format(se)) << '\n'; break; } } } // //----< display nicely formatted semi-expression >------------- string detector::format(semiExp &se) { if(se[0] == "public" ) se.remove(0); if(se[0] == "protected") se.remove(0); if(se[0] == "private" ) se.remove(0); if(se[0] == ":" ) se.remove(0); packedTokens pt; return pt.format(se); } //----< show summary statistics >------------------------------ void detector::showSummary(ostream &out) { vector::iterator it; out << "\n =================================================" << "\n summary statistics - all files:" << "\n =================================================\n"; out << "\n[" << setw(5) << totalCyclo << "," << setw(5) << totalFuncLines << "] - total cyclo, total function lines"; double complexityPerLine; if(totalFuncLines != 0) complexityPerLine = double(totalCyclo)/double(totalFuncLines); else complexityPerLine = 0; out << "\n[" << setw(5) << maxCyclo << "," << setw(5) << maxFuncLines << "] - max cyclo, max function lines (excludes mains)\n" << setw(12) << complexityPerLine << " - complexity per line\n" << setw(12) << grammar::getTotalLines() << " - total lines" << '\n'; if(limitsExceeded.size() > 0) { out << "\n functions exceeding cc or line limits: " << "\n ----------------------------------------"; for(it=limitsExceeded.begin(); it!=limitsExceeded.end(); it++) out << "\n" << (*it); out << '\n'; } if(globals.size() > 0) { out << "\n global data declarations: " << "\n ---------------------------"; for(it=globals.begin(); it!=globals.end(); it++) out << "\n" << (*it); out << '\n'; } if(publicData.size() > 0) { out << "\n public data declarations: " << "\n --------------------------"; for(it=publicData.begin(); it!=publicData.end(); it++) out << "\n " << (*it); out << '\n'; } if(pointersReturned.size() > 0) { out << "\n pointers returned from public member functions: " << "\n -------------------------------------------------"; for(it=pointersReturned.begin(); it!=pointersReturned.end(); it++) out << "\n " << (*it); out << '\n'; } } // //----< constructor >------------------------------------------ functionDef::functionDef() : detector() { } //----< search for key word >---------------------------------- bool functionDef::key(string &s) { const int numKeys = 6; static const string keys[numKeys] = { "if", "while", "for", "foreach", "switch" }; int i; for(i=0; i------------------------ bool functionDef::operator()(semiExp &se) { if(se.length() == 0) return false; if(se[se.length()-1] == string("{")) { size_t i, j, k; for(i=0; i0 && !key(se[i-1])) { funcName = stripSignature(se); if(funcName[0] == '>') { for(k = i-4; k >= 0; k--) { funcName = se[k] + funcName; if(funcName.find('<') < funcName.size()) { funcName = se[k-1] + funcName; break; } } } // look for ] after identifier - implies this is an attribute size_t pos = se.find(")"); for(j = pos+1; j---- int functionDef::cyclo(semiExp &se) { int cc = 0; const int numKeys = 7; static string key[numKeys] = { "if", "for", "foreach", "while", "break", "continue", "goto" }; size_t i,j; for(i=0; i------------ // // returns function name, being careful to collect // all parts of an operator's name, e.g., operator+= // string functionDef::stripSignature(semiExp &se) { size_t posParen = se.find("("); size_t posOper = se.find("operator"); string temp; if(posOper < se.length()) { if(se.length() > posParen+2 && se[posParen+2] == "(") posParen += 2; size_t i; for(i=posOper; i 2 && se[posParen-2] == "::") { temp = se[posParen-3] + se[posParen-2] + se[posParen-1]; return temp; } return se[posParen-1]; } //----< collect line count and cyclomatic complexity >--------- // // - cc and line count are returned as side affects through // reference arguments // - return value is a string containing formatted announce- // ments of static data declarations inside function // string functionDef::collectMetrics(semiExp &se, int &cc, int &lines) { cc = 1; size_t firstLineCount = se.linesAtFirstToken(); size_t scopeLev = se.scopeLevel(); staticDeclar sd; string staticFormat; while(se.scopeLevel() >= scopeLev) { if(!se.get()) break; if(sd(se)) { staticFormat += '\n'; staticFormat += sd.format(se); } cc += cyclo(se); } lines = se.lines() - firstLineCount + 1; totalFuncLines += lines; totalCyclo += cc; if(maxCyclo < cc && functionDef::funcName != "main") maxCyclo = cc; if(maxFuncLines < lines && functionDef::funcName != "main") maxFuncLines = lines; return staticFormat; } // //----< is this a template function definition? >-------------- bool functionDef::isTemplate(semiExp& se) { if(se.find(">::")) return true; return false; } //----< collect template signature >--------------------------- string functionDef::collectSig(semiExp& se) { string sig; size_t pos = se.find("("); if(se[pos-1] == "operator" && se[pos] == "(" && se[pos+1] == ")") pos += 2; for(size_t i = pos; i--------------------------- // // assumes input semi-expression contains a function signature // string functionDef::format(semiExp &se) { se.remove(se.length()-1); string outStr, temp = funcName; if(!strip) temp += collectSig(se); int cc; int lines; string staticFormat = collectMetrics(se,cc,lines); ostrstream formatStr; formatStr << setw(6) << cc << setw(6) << lines << " " << temp.c_str() << ends; outStr = formatStr.str(); if(cc > 10 || lines >50) { ostrstream newFormatStr; newFormatStr << setw(6) << cc << setw(6) << lines << " " << moduleName::get().c_str() << " : " << temp.c_str() << ends; string templim = newFormatStr.str(); limitsExceeded.push_back(templim); } outStr += staticFormat; return outStr; } //----< detection operation >----------------------------------- bool globalDeclar::operator()(semiExp &se) { if(se.scopeLevel() != 0) // not global level return false; if(se[0] == "#" || se[0] == "using") // using namespace declar return false; if(se[se.length()-1] != string(";")) // not data declaration return false; if(se.length() < 3) // no type return false; if(se.find("::") < se.length()) // static member of class return false; if(se.find("(") < se.length()) // global function declar return false; if(se.find("typedef") < se.length()) // typedef return false; return true; } //----< strip declaration down to identifier >----------------- string globalDeclar::stripDeclar(semiExp &se) { int pos1 = se.find("="); int pos2 = se.find("["); int pos3 = se.length()-1; int pos = std::min(pos1,std::min(pos2,pos3)); return se[pos-1]; } //----< display last function signature >---------------------- string globalDeclar::format(semiExp &se) { string temp1 = detector::format(se); string temp2; if(se.textFind("::") < se.length()) temp2 = " - class static data declaration:\n "; else temp2 = " - global data declaration:\n "; temp2 += temp1; temp1 = string(" ") + temp1; if(se.textFind("::") == se.length()) globals.push_back(temp1); return temp2; } //----< detection operation >----------------------------------- bool classDeclar::operator()(semiExp &se) { if(se.find("class" ) < se.length() && se[se.length()-1] == "{") return true; if(se.find("interface" ) < se.length() && se[se.length()-1] == "{") return true; if(se.find("struct") < se.length() && se[se.length()-1] == "{") return true; if(se.find("typedef") < se.length()) return true; return false; } //----< look for public data and returned pointers >----------- string classDeclar::lookForErrors(semiExp &se) { bool inPublic = false; if(se.find("typedef") < se.length()) return ""; string formatErrors; size_t publicScopeLev; size_t scopeLev = se.scopeLevel(); localDataDeclar dd; pointerDeclar pd; classDeclar cd; functionDef fd; // cout << "looking for errors: "; se.display(); while(se.scopeLevel() >= scopeLev) { if(!se.get()) break; if(fd(se)) { // ----------------- formatErrors += '\n'; // added these lines formatErrors += fd.format(se); // 29 Feb 00 } // ----------------- if(cd(se)) cout << cd.format(se) << '\n'; if(se.find("public") < se.length()) { publicScopeLev = se.scopeLevel(); if(se[se.length()-1] == "{") --publicScopeLev; inPublic = true; } if(se.find("protected") < se.length()) inPublic = false; if(se.find("private") < se.length()) inPublic = false; if(inPublic) { if(dd(se) && (publicScopeLev == se.scopeLevel())) { cout << dd.format(se) << '\n'; } else if(pd(se)) { cout << pd.format(se) << '\n'; } } } return formatErrors; } //----< display last function signature >---------------------- string classDeclar::format(semiExp &se) { se.remove(se.length()-1); string temp = " - type: "; temp += detector::format(se); temp += lookForErrors(se); return temp; } //----< detection operation >----------------------------------- bool staticDeclar::operator()(semiExp &se) { if(se.scopeLevel() == 0) return false; if(se.find("static") < se.length() && se[se.length()-1] != "{") { int posParen = se.find("("); int posEqual = se.find("="); if(posParen < posEqual) return false; else return true; } return false; } //----< strip declaration down to identifier >----------------- string staticDeclar::stripDeclar(semiExp &se) { int pos1 = se.find("="); int pos2 = se.find("["); int pos3 = se.length()-1; int pos = std::min(pos1,std::min(pos2,pos3)); return se[pos-1]; } //----< display last function signature >---------------------- string staticDeclar::format(semiExp &se) { string temp = " - static: "; if(strip) temp += stripDeclar(se); else temp += detector::format(se); return temp; } //----< detection operation >----------------------------------- bool localDataDeclar::operator()(semiExp &se) { if(se.scopeLevel() == 0) return false; if(se[se.length()-1] != "{" && se[se.length()-1] != "}") { if(se.find("operator") < se.length()) return false; if(se.length() < 3) return false; int posParen = se.find("("); int posEqual = se.find("="); if(posParen < posEqual) return false; return true; } return false; } //----< strip declaration down to identifier >----------------- string localDataDeclar::stripDeclar(semiExp &se) { int pos1 = se.find("="); int pos2 = se.find("["); int pos3 = se.length()-1; int pos = std::min(pos1,std::min(pos2,pos3)); return se[pos-1]; } //----< display last function signature >---------------------- string localDataDeclar::format(semiExp &se) { string temp1 = detector::format(se); publicData.push_back(temp1); string temp2 = " - public data declaration:\n "; temp2 += temp1; return temp2; } //----< detection operation >----------------------------------- bool pointerDeclar::operator()(semiExp &se) { if(se[se.length()-1] != "{") { size_t posStar = se.find("*"); size_t posParen = se.find("("); size_t posEqual = se.find("="); if(posEqual < se.length() && posParen < posEqual) { return false; } else { if(posStar < posEqual && posStar < posParen) if(se.find("const") > posStar) if(posStar > 0 && se[posStar-1] != "operator") // added 21 Apr 00 return true; } } return false; } //----< strip declaration down to identifier >----------------- string pointerDeclar::stripDeclar(semiExp &se) { int pos1 = se.find("="); int pos2 = se.find("["); int pos3 = se.length()-1; int pos = std::min(pos1,std::min(pos2,pos3)); return se[pos-1]; } //----< display last function signature >---------------------- string pointerDeclar::format(semiExp &se) { string temp1 = detector::format(se); pointersReturned.push_back(temp1); string temp2 = " - pointer returned from public member function:\n "; temp2 += temp1; return temp2; } #ifdef TEST_GRAMMAR //----< test stub >-------------------------------------------- #include void main(int argc, char *argv[]) { if(argc == 1) { cout << "\n please enter name of file to analyze\n\n"; return; } grammar gram; functionDef funcDef; globalDeclar globDec; classDeclar classDec; staticDeclar staticDec; gram.analyze(funcDef); gram.analyze(globDec); gram.analyze(classDec); gram.analyze(staticDec); commandLine cl(argc,argv); if(cl.isCommand("s")) funcDef.doStrip(); int TotalFuncLines = 0; int TotalLines = 0; int TotalCyclo = 0; int i; for(i=0; i