///////////////////////////////////////////////////////////////////////// // CppSemiExp.cpp - declares semi-expression which collects tokens // // ver 1.2 from input stream via a CToker instance // // // // Language: Visual C++, ver 7.0 // // Platform: Dell Dimension 8300, Windows XP Prof, SP 1 // // Application: CSE687 prototype for project #1, Spring 2004 // // Author: Jim Fawcett, CST 2-187, Syracuse University // // (315) 443-3948, jfawcett@twcny.rr.com // ///////////////////////////////////////////////////////////////////////// #include "CppSemiExp.h" #include "CppToker.h" #include #include #include using namespace Scanner; //----< is this a C language comment? >---------------------------------- bool CCCommentFilter::present() { if(_pToker->curr() == "/*") return true; return false; } //----< discard C language comment >------------------------------------- bool CCCommentFilter::extract(std::string &token) { std::string temp; while((temp = _pToker->getTok()) != "*/") if(temp == "") break; // discard token return false; // no token returned } //----< is this a C++ language comment? >-------------------------------- bool CCppCommentFilter::present() { std::string temp = _pToker->curr(); if(temp.length() >= 2 && temp[0] == '/' && temp[1] == '/') return true; return false; } //----< discard C++ language comment >----------------------------------- bool CCppCommentFilter::extract(std::string &token) { std::string temp; while((temp = _pToker->getTok()) != "\n") if(temp == "") break; // discard token token = "\n"; // substitute return true; // token returned } // //----< is this a quoted string? >--------------------------------------- bool CDoubleQuoteFilter::present() { std::string temp = _pToker->curr(); if(temp.length() > 0 && temp[0] == '\"') return true; return false; } //----< collect quoted string as a token >------------------------------- bool CDoubleQuoteFilter::extract(std::string &token) { std::string temp = _pToker->curr(); _pToker->getTok(); while(_pToker->curr() != "\"" || _pToker->prev() == "\\") { temp += _pToker->curr(); _pToker->getTok(); } token = (temp += _pToker->curr()); return true; } //----< is this a quoted character? >------------------------------------ bool CSingleQuoteFilter::present() { std::string temp = _pToker->curr(); if(temp.length() > 0 && temp[0] == '\'') return true; return false; } //----< collect quoted character as a token >---------------------------- bool CSingleQuoteFilter::extract(std::string &token) { std::string temp = _pToker->curr(); _pToker->getTok(); while(_pToker->curr() != "\'" || _pToker->prev() == "\\") { temp += _pToker->curr(); _pToker->getTok(); } token = (temp += _pToker->curr()); return true; } // //----< CSemiExp constructor >------------------------------------------- CSemiExp::CSemiExp(CToker *pToker) : _pToker(pToker) { } //----< CSemiExp destructor >-------------------------------------------- CSemiExp::~CSemiExp(void) { // - user created CToker so don't destroy // - user creates filters or uses // static default filters so don't destroy } //----< register type derived from ITokenFilter >------------------------ void CSemiExp::registerFilter(ITokenFilter *pITokenFilter) { _filter.push_back(pITokenFilter); } //----< register default filters >--------------------------------------- void CSemiExp::registerDefaultFilters() { static CCppCommentFilter CppCommFilter(_pToker); static CCCommentFilter CCommFilter(_pToker); static CDoubleQuoteFilter CDoubleQF(_pToker); static CSingleQuoteFilter CSingleQF(_pToker); if(_filter.size() > 0) _filter.clear(); CppCommFilter.setToker(_pToker); CCommFilter.setToker(_pToker); CDoubleQF.setToker(_pToker); CSingleQF.setToker(_pToker); registerFilter(&CppCommFilter); registerFilter(&CCommFilter); registerFilter(&CDoubleQF); registerFilter(&CSingleQF); // std::cout << "\n " << _filter.size() << " filters registered\n\n"; } //----< return number of tokens in semi-expression >--------------------- size_t CSemiExp::length() const { return _tok.size(); } //----< does this character terminat a semi-expression? >---------------- bool CSemiExp::isTerminator(const std::string &token) const { if(token=="{" || token=="}" || token==";") return true; if(length() == 0) return false; if(token=="\n" && _tok[0]=="#") return true; return false; } //----< remove all tokens >---------------------------------------------- void CSemiExp::clear() { _tok.clear(); } // //----< remove or transform groups of tokens >--------------------------- bool CSemiExp::filter(std::string &token) { std::string dummy; std::list::iterator iter; for(iter=_filter.begin(); iter!=_filter.end(); iter++) { if((*iter)->present()) { if((*iter)->extract(dummy=token)) { token = dummy; // return token via side effect return true; } else { return false; // don't return token } } } return true; } //----< extract semi-expressions from filtered stream >------------------ // // returns silently if no toker provided, e.g., created with default ctor // bool CSemiExp::get() { if(!_pToker) return false; clear(); std::string token; do { token = _pToker->getTok(); if(token == "") return false; if(filter(token)) { if(token != "\n") _tok.push_back(token); } } while(!isTerminator(token)); return true; } // //----< check for valid index >------------------------------------------ void CSemiExp::checkIndex(unsigned int i) { if(i<0 || _tok.size() <= i) throw std::exception("index out of domain in CSemiExp::insert"); } //----< insert token preceding element i >------------------------------- void CSemiExp::insert(unsigned int i, const std::string &token) { checkIndex(i); std::vector::iterator iter = _tok.begin(); _tok.insert(iter+i,token); } //----< append token at end of semi-expression >------------------------- void CSemiExp::push_back(const std::string &token) { std::vector::iterator iter = _tok.begin(); _tok.push_back(token); } //----< remove token from semi-expression >------------------------------ void CSemiExp::erase(unsigned int i) { checkIndex(i); std::vector::iterator iter = _tok.begin(); _tok.erase(iter+i); } //----< find token in semi-expression >---------------------------------- // // returns length() if not found // size_t CSemiExp::find(const std::string& token, size_t start) { size_t len = length(); for(size_t i=start; i------------------------- std::string& CSemiExp::operator [](unsigned int n) { if(n<0 || length()<= n) { std::string msg = "index out of domain in CSemiExp::operator[](int)"; throw std::exception(msg.c_str()); } return _tok[n]; } //----< return readonly reference to nth token >------------------------- std::string CSemiExp::operator [](unsigned int n) const { if(n<0 || length()<= n) { std::string msg = "index out of domain in CSemiExp::operator[](int)"; throw std::exception(msg.c_str()); } return _tok[n]; } //----< display semi-expression on cout with little formatting >--------- void CSemiExp::show(bool verbose) const { if(!verbose) return; std::cout << " "; for(unsigned int i=0; i<_tok.size(); i++) if(_tok[i] != "\n") std::cout << _tok[i] << " "; std::cout << std::endl; } // ///////////////////////////////////////////////////////////////////////// // Test Stub #ifdef TEST_CPPSEMIEXP #include #include class testFunctor { public: virtual bool operator()(bool verbose)=0; virtual ~testFunctor() {}; }; class tester { public: tester(const std::string& msg); ~tester(); bool execute(); void registerTest(testFunctor *tf); void show(unsigned index); private: std::vector _test; std::vector _verbose; unsigned failed; }; tester::tester(const std::string& msg) { std::cout << "\n " << msg; std::cout << "\n " << std::string(msg.length(),'=') << "\n"; } tester::~tester() { std::cout << "\n" << std::setw(4) << (unsigned)_test.size() << "tests executed"; std::cout << "\n" << std::setw(4) << failed << " tests failed\n\n"; } void tester::registerTest(testFunctor *tf) { _test.push_back(tf); _verbose.push_back(false); } void tester::show(unsigned int index) { if(index < 0 || _verbose.size() <= index) return; _verbose[index] = true; } bool tester::execute() { failed = 0; for(int i=0; i<(int)_test.size(); i++) { if(!(*_test[i])(_verbose[i])) failed++; } return (failed == 0); } class nestedScope : public testFunctor { bool operator()(bool verbose) { std::cout << " Testing Nested Scope Fix\n"; std::istringstream inStr("int x; double y; char z;"); CToker tokA(&inStr); CSemiExp testA(&tokA); testA.registerDefaultFilters(); testA.get(); testA.show(verbose); { CSemiExp testB(&tokA); testB.registerDefaultFilters(); testB = testA; testB.get(); testB.show(verbose); } testA.get(); testA.show(verbose); return true; // if we get here test passed } }; class commentAtEndOfStream : public testFunctor { public: bool operator()(bool verbose) { std::cout << " Testing Comment at end of Stream Fix\n"; std::istringstream inStr1("/* a comment */"); CToker tok1(&inStr1); CSemiExp test1(&tok1); test1.registerDefaultFilters(); test1.get(); test1.show(verbose); std::istringstream inStr2("// a comment "); CToker tok2(&inStr2); CSemiExp test2(&tok2); test2.registerDefaultFilters(); test2.get(); test2.show(verbose); return true; // if we get here test passed } }; class filterSourceCode : public testFunctor { public: bool findComment(const CSemiExp &semi) { for(int i=0; i<(int)semi.length(); i++) { if(semi[i].find("/*") < semi.length()) return true; if(semi[i].find("//") < semi.length()) return true; } return false; } bool operator()(bool verbose) { std::cout << " Testing comment stripping\n"; std::string file = "..\\CppSemiExp.h"; std::ifstream in(file.c_str()); if(in.good()) { try { CToker toker(&in); CSemiExp semi(&toker); semi.registerDefaultFilters(); while(semi.get()) { semi.show(verbose); if(findComment(semi)) return false; } semi.show(verbose); return true; } catch(exception e) { return false; } } return false; } }; class construction : public testFunctor { bool operator()(bool verbose) { std::cout << " Testing manual construction of semi-expression\n"; CSemiExp test1; // default construction test1.push_back("one"); test1.show(verbose); test1.push_back("two"); test1.show(verbose); test1.insert(1,"three"); test1.show(verbose); test1.push_back(";"); test1.show(verbose); int i = static_cast(test1.find("three")); if(verbose) std::cout << " The index of token \"three\" is " << i << std::endl; test1.get(); // does nothing since it has no CToker instance test1.show(verbose); return true; // test is visual } }; class copy : public testFunctor { public: bool operator()(bool verbose) { std::cout << " Testing copy constructor\n"; CSemiExp test1; // default construction test1.push_back("one"); test1.push_back("two"); test1.insert(1,"three"); test1.push_back(";"); test1.show(verbose); CSemiExp test2 = test1; // compiler generated copy construction test2.show(verbose); for(int i=0; i<(int)test1.length(); i++) if(test1[i] != test2[i]) return false; return true; } }; class assign : public testFunctor { public: bool operator()(bool verbose) { std::cout << " Testing assignment\n"; CSemiExp test1; // default construction test1.push_back("one"); test1.push_back("two"); test1.insert(1,"three"); test1.push_back(";"); test1.show(verbose); CSemiExp test2; test2 = test1; // compiler generated assignment test2.show(verbose); for(int i=0; i<(int)test1.length(); i++) if(test1[i] != test2[i]) return false; return true; } }; int main() { tester t("Testing Fixes for CSemiExp version 1.1"); t.registerTest(new nestedScope); t.registerTest(new commentAtEndOfStream); t.registerTest(new filterSourceCode); t.registerTest(new construction); t.registerTest(new copy); t.registerTest(new assign); t.show(3); t.execute(); } #endif