#ifndef __TOKENIZE__TOKENIZER_H #define __TOKENIZE__TOKENIZER_H #include #include #include #include #include #include #include #include #include namespace tokenize { template struct Tag { }; //////////////////////////////////////////////////////////////////////////////////////////////////// // // Tokenizer // //////////////////////////////////////////////////////////////////////////////////////////////////// template class Tokenizer : public Stream, public TokenizerPolicy { template friend class Tokenizer; public: explicit Tokenizer() noexcept; explicit Tokenizer(std::string streamName, std::istream &istream); template Tokenizer(OtherTokenizer &&other) noexcept : Stream(std::forward(other)) { } void removeComments(const std::string &startSequence, const std::string &endSequence, bool removeEnd); template Type get(); template bool testAndReturn(const Type &expectedValue); template bool testAndSkip(const Type &expectedValue); template void expect(const Type &expectedValue); // TODO: refactor std::string getIdentifier(); bool testIdentifierAndSkip(const std::string &identifier); bool testIdentifierAndReturn(const std::string &identifier); // TODO: remove bool probeNumber(); std::string getLine(); void skipWhiteSpace(); void skipBlankSpace(); void skipLine(); private: std::string getImpl(Tag); char getImpl(Tag); uint64_t getImpl(Tag); int64_t getImpl(Tag); uint32_t getImpl(Tag); int32_t getImpl(Tag); bool getImpl(Tag); bool testImpl(const std::string &expectedValue); bool testImpl(char expectedValue); bool testImpl(uint64_t expectedValue); bool testImpl(int64_t expectedValue); bool testImpl(uint32_t expectedValue); bool testImpl(int32_t expectedValue); bool testImpl(bool expectedValue); uint64_t getIntegerBody(); }; //////////////////////////////////////////////////////////////////////////////////////////////////// template Tokenizer::Tokenizer() noexcept { } //////////////////////////////////////////////////////////////////////////////////////////////////// template Tokenizer::Tokenizer(std::string streamName, std::istream &istream) : Stream(streamName, istream) { } //////////////////////////////////////////////////////////////////////////////////////////////////// template void Tokenizer::skipWhiteSpace() { while (!atEnd() && TokenizerPolicy::isWhiteSpaceCharacter(currentCharacter())) advance(); } //////////////////////////////////////////////////////////////////////////////////////////////////// template void Tokenizer::skipBlankSpace() { while (!atEnd() && TokenizerPolicy::isBlankCharacter(currentCharacter())) advance(); } //////////////////////////////////////////////////////////////////////////////////////////////////// template void Tokenizer::skipLine() { while (!atEnd() && currentCharacter() != '\n') advance(); advance(); } //////////////////////////////////////////////////////////////////////////////////////////////////// template template Type Tokenizer::get() { return getImpl(Tag()); } //////////////////////////////////////////////////////////////////////////////////////////////////// template template bool Tokenizer::testAndReturn(const Type &expectedValue) { const auto previousPosition = position(); const auto result = testImpl(expectedValue); seek(previousPosition); return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// template template bool Tokenizer::testAndSkip(const Type &expectedValue) { const auto previousPosition = position(); const auto result = testImpl(expectedValue); if (result == false) seek(previousPosition); return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// template template void Tokenizer::expect(const Type &expectedValue) { if (testAndSkip(expectedValue)) return; std::stringstream message; message << "unexpected value, expected “" << expectedValue << "”"; throw TokenizerException(location(), message.str()); } //////////////////////////////////////////////////////////////////////////////////////////////////// template std::string Tokenizer::getIdentifier() { skipWhiteSpace(); std::string value; while (true) { const auto character = currentCharacter(); if (!TokenizerPolicy::isIdentifierCharacter(character)) { if (value.empty()) throw TokenizerException(location(), "could not parse identifier"); return value; } value.push_back(character); advance(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testIdentifierAndReturn(const std::string &expectedValue) { const auto previousPosition = position(); const auto result = testIdentifierAndSkip(expectedValue); seek(previousPosition); return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testIdentifierAndSkip(const std::string &expectedValue) { return testAndSkip(expectedValue) && !TokenizerPolicy::isIdentifierCharacter(currentCharacter()); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::probeNumber() { const auto previousPosition = position(); skipWhiteSpace(); while (!TokenizerPolicy::isWhiteSpaceCharacter(currentCharacter())) if (!std::isdigit(currentCharacter())) { seek(previousPosition); return false; } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// template std::string Tokenizer::getLine() { std::string value; while (true) { const auto character = currentCharacter(); advance(); if (character == '\n') break; else if (character == '\r') continue; value.push_back(character); } return value; } //////////////////////////////////////////////////////////////////////////////////////////////////// template void Tokenizer::removeComments(const std::string &startSequence, const std::string &endSequence, bool removeEnd) { // TODO: move to appropriate place for (auto &character : m_content) character = TokenizerPolicy::transformCharacter(character); const auto removeRange = [&](const auto &start, const auto &end) { const auto previousPosition = position(); assert(start < m_content.size()); seek(start); while (position() < end) { if (atEnd()) return; m_content[position()] = ' '; advanceUnchecked(); } seek(previousPosition); }; seek(0); // TODO: refactor while (!atEnd()) { bool startSequenceFound = false; while (!atEnd()) { if ((startSequenceFound = testAndSkip(startSequence))) break; advanceUnchecked(); } if (!startSequenceFound && atEnd()) break; const auto startPosition = position() - startSequence.size(); bool endSequenceFound = false; while (!atEnd()) { if ((endSequenceFound = testAndSkip(endSequence))) break; advanceUnchecked(); } // If the end sequence is to be removed or could not be found, remove entire range const auto endPosition = (removeEnd || !endSequenceFound) ? position() : position() - endSequence.size(); removeRange(startPosition, endPosition); seek(endPosition + 1); } seek(0); } //////////////////////////////////////////////////////////////////////////////////////////////////// template std::string Tokenizer::getImpl(Tag) { skipWhiteSpace(); const auto startPosition = position(); while (!TokenizerPolicy::isWhiteSpaceCharacter(currentCharacter())) advance(); const auto endPosition = position(); const auto length = static_cast(endPosition - startPosition); std::string value; value.reserve(length); seek(startPosition); for (size_t i = 0; i < length; i++) { value.push_back(currentCharacter()); advance(); } return value; } //////////////////////////////////////////////////////////////////////////////////////////////////// template char Tokenizer::getImpl(Tag) { const auto value = currentCharacter(); advance(); return value; } //////////////////////////////////////////////////////////////////////////////////////////////////// template uint64_t Tokenizer::getIntegerBody() { check(); if (!std::isdigit(currentCharacter())) throw TokenizerException(location(), "could not read integer value"); uint64_t value = 0; while (!atEnd()) { const auto character = currentCharacter(); if (!std::isdigit(character)) break; value *= 10; value += character - '0'; advanceUnchecked(); } return value; } //////////////////////////////////////////////////////////////////////////////////////////////////// template int64_t Tokenizer::getImpl(Tag) { skipWhiteSpace(); bool positive = testAndSkip('+') || !testAndSkip('-'); const auto value = getIntegerBody(); return (positive ? value : -value); } //////////////////////////////////////////////////////////////////////////////////////////////////// template uint64_t Tokenizer::getImpl(Tag) { skipWhiteSpace(); if (currentCharacter() == '-') throw TokenizerException(location(), "expected unsigned integer, got signed one"); return getIntegerBody(); } //////////////////////////////////////////////////////////////////////////////////////////////////// template int32_t Tokenizer::getImpl(Tag) { return static_cast(getImpl(Tag())); } //////////////////////////////////////////////////////////////////////////////////////////////////// template uint32_t Tokenizer::getImpl(Tag) { return static_cast(getImpl(Tag())); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::getImpl(Tag) { skipWhiteSpace(); if (testAndSkip('0')) return false; if (testAndSkip('1')) return true; throw TokenizerException(location(), "could not read Boolean value"); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(const std::string &expectedValue) { if (!TokenizerPolicy::isWhiteSpaceCharacter(expectedValue.front())) skipWhiteSpace(); for (size_t i = 0; i < expectedValue.size(); i++) { if (atEnd()) return false; const auto character = currentCharacter(); if (character != expectedValue[i]) return false; advance(); } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(char expectedValue) { const auto result = (currentCharacter() == expectedValue); advance(); return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(int64_t expectedValue) { const auto value = getImpl(Tag()); return (value == expectedValue); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(uint64_t expectedValue) { const auto value = getImpl(Tag()); return (value == expectedValue); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(int32_t expectedValue) { return testImpl(static_cast(expectedValue)); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(uint32_t expectedValue) { return testImpl(static_cast(expectedValue)); } //////////////////////////////////////////////////////////////////////////////////////////////////// template bool Tokenizer::testImpl(bool expectedValue) { const auto value = getImpl(Tag()); return (value == expectedValue); } //////////////////////////////////////////////////////////////////////////////////////////////////// } #endif