///////////////////////////////////////////////////////////////////// // Tokenizer.cpp - read words from a std::stream // // ver 3.2 // // Language: C++, Visual Studio 2013 // // Application: Parser component, CSE687 - Object Oriented Design // // Author: Jim Fawcett, Syracuse University, CST 4-187 // // jfawcett@twcny.rr.com // ///////////////////////////////////////////////////////////////////// /* Helper code that does not attempt to handle all tokenizing special cases like escaped characters. */ #include "Tokenizer.h" #include #include #include class ConsumeState { public: ConsumeState(); virtual ~ConsumeState(); void attach(std::istream* pIn) { _pIn = pIn; } virtual void eatChars() = 0; void consumeChars() { _pState->eatChars(); _pState = nextState(); } bool canRead() { return _pIn->good(); } std::string getTok() { return token; } bool hasTok() { return token.size() > 0; } ConsumeState* nextState(); protected: static std::string token; static std::istream* _pIn; static int prevChar; static int currChar; static ConsumeState* _pState; static ConsumeState* _pEatCppComment; static ConsumeState* _pEatCComment; static ConsumeState* _pEatWhitespace; static ConsumeState* _pEatPunctuator; static ConsumeState* _pEatAlphanum; static ConsumeState* _pEatNewline; }; std::string ConsumeState::token; std::istream* ConsumeState::_pIn; int ConsumeState::prevChar; int ConsumeState::currChar; ConsumeState* ConsumeState::_pState = nullptr; ConsumeState* ConsumeState::_pEatCppComment = nullptr; ConsumeState* ConsumeState::_pEatCComment = nullptr; ConsumeState* ConsumeState::_pEatWhitespace = nullptr; ConsumeState* ConsumeState::_pEatPunctuator = nullptr; ConsumeState* ConsumeState::_pEatAlphanum = nullptr; ConsumeState* ConsumeState::_pEatNewline; void testLog(const std::string& msg); ConsumeState* ConsumeState::nextState() { int chNext = _pIn->peek(); if (std::isspace(currChar) && currChar != '\n') { testLog("state: eatWhitespace"); return _pEatWhitespace; } if (currChar == '/' && chNext == '/') { testLog("state: eatCppComment"); return _pEatCppComment; } if (currChar == '/' && chNext == '*') { testLog("state: eatCComment"); return _pEatCComment; } if (currChar == '\n') { testLog("state: eatNewLine"); return _pEatNewline; } if (std::isalnum(currChar)) { testLog("state: eatAlphanum"); return _pEatAlphanum; } if (ispunct(currChar)) { testLog("state: eatPunctuator"); return _pEatPunctuator; } if (!_pIn->good()) return _pEatWhitespace; throw(std::logic_error("invalid type")); } class EatWhitespace : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating whitespace"; do { if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } while (std::isspace(currChar) && currChar != '\n'); } }; class EatCppComment : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating C++ comment"; do { if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } while (currChar != '\n'); } }; class EatCComment : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating C comment"; do { if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } while (currChar != '*' || _pIn->peek() != '/'); _pIn->get(); currChar = _pIn->get(); } }; class EatPunctuator : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating punctuator"; do { token += currChar; if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } while (ispunct(currChar)); } }; class EatAlphanum : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating alphanum"; do { token += currChar; if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } while (isalnum(currChar)); } }; class EatNewline : public ConsumeState { public: virtual void eatChars() { token.clear(); //std::cout << "\n eating alphanum"; token += currChar; if (!_pIn->good()) // end of stream return; currChar = _pIn->get(); } }; ConsumeState::ConsumeState() { static bool first = true; if (first) { first = false; _pEatAlphanum = new EatAlphanum(); _pEatCComment = new EatCComment(); _pEatCppComment = new EatCppComment(); _pEatPunctuator = new EatPunctuator(); _pEatWhitespace = new EatWhitespace(); _pEatNewline = new EatNewline(); _pState = _pEatWhitespace; } } ConsumeState::~ConsumeState() { static bool first = true; if (first) { first = false; delete _pEatAlphanum; delete _pEatCComment; delete _pEatCppComment; delete _pEatPunctuator; delete _pEatWhitespace; delete _pEatNewline; } } Toker::Toker() : pConsumer(new EatWhitespace()) {} Toker::~Toker() { delete pConsumer; } bool Toker::attach(std::istream* pIn) { if (pIn != nullptr && pIn->good()) { pConsumer->attach(pIn); return true; } return false; } std::string Toker::getTok() { while(true) { pConsumer->consumeChars(); if (pConsumer->hasTok()) break; if (!pConsumer->canRead()) return ""; } return pConsumer->getTok(); } bool Toker::canRead() { return pConsumer->canRead(); } void testLog(const std::string& msg) { #ifdef TEST_LOG std::cout << "\n " << msg; #endif } //----< test stub >-------------------------------------------------- #ifdef TEST_TOKENIZER #include int main() { std::string fileSpec = "../Tokenizer/Tokenizer.cpp"; //std::string fileSpec = "../Tokenizer/Tokenizer.h"; //std::string fileSpec = "../Tokenizer/Test.txt"; std::ifstream in(fileSpec); if (!in.good()) { std::cout << "\n can't open " << fileSpec << "\n\n"; return 1; } Toker toker; toker.attach(&in); while (in.good()) { std::string tok = toker.getTok(); if (tok == "\n") tok = "newline"; std::cout << "\n -- " << tok; } std::cout << "\n\n"; return 0; } #endif