mirror of
				https://github.com/Laupetin/OpenAssetTools.git
				synced 2025-10-31 10:36:58 +00:00 
			
		
		
		
	Extract commonly used Parser code to new Parser component
This commit is contained in:
		
							
								
								
									
										111
									
								
								src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| #include "AbstractDirectiveStreamProxy.h" | ||||
|  | ||||
| TokenPos AbstractDirectiveStreamProxy::CreatePos(const ParserLine& line, const unsigned position) | ||||
| { | ||||
|     return TokenPos(line.m_filename.get(), line.m_line_number, static_cast<int>(position + 1)); | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::SkipWhitespace(const ParserLine& line, unsigned& position) | ||||
| { | ||||
|     while(true) | ||||
|     { | ||||
|         if (position >= line.m_line.size()) | ||||
|             return false; | ||||
|  | ||||
|         if (isspace(line.m_line[position])) | ||||
|             position++; | ||||
|         else | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::ExtractInteger(const ParserLine& line, unsigned& position, int& value) | ||||
| { | ||||
|     if (position >= line.m_line.size()) | ||||
|         return false; | ||||
|  | ||||
|     const auto* startPosition = &line.m_line[position]; | ||||
|     char* endPosition; | ||||
|     value = strtol(startPosition, &endPosition, 0); | ||||
|     const auto len = endPosition - startPosition; | ||||
|  | ||||
|     if(len > 0) | ||||
|     { | ||||
|         position += len; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::ExtractIdentifier(const ParserLine& line, unsigned& position) | ||||
| { | ||||
|     auto firstChar = true; | ||||
|     while (true) | ||||
|     { | ||||
|         if (position >= line.m_line.size()) | ||||
|             return !firstChar; | ||||
|  | ||||
|         const auto c = line.m_line[position]; | ||||
|         if (isalpha(c)  | ||||
|             || c == '_'  | ||||
|             || !firstChar && isdigit(c)) | ||||
|         { | ||||
|             position++; | ||||
|         } | ||||
|         else | ||||
|             return !firstChar; | ||||
|  | ||||
|         firstChar = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::MatchCharacter(const ParserLine& line, unsigned& position, char c) | ||||
| { | ||||
|     if (position < line.m_line.size() && line.m_line[position] == c) | ||||
|     { | ||||
|         position++; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::MatchNextCharacter(const ParserLine& line, unsigned& position, char c) | ||||
| { | ||||
|     return SkipWhitespace(line, position) && MatchCharacter(line, position, c); | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::MatchString(const ParserLine& line, unsigned& position, const char* str, unsigned len) | ||||
| { | ||||
|     if (line.m_line.compare(position, len, str) == 0) | ||||
|     { | ||||
|         position += len; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::MatchNextString(const ParserLine& line, unsigned& position, const char* str, unsigned len) | ||||
| { | ||||
|     return SkipWhitespace(line, position) && MatchString(line, position, str, len); | ||||
| } | ||||
|  | ||||
| bool AbstractDirectiveStreamProxy::FindDirective(const ParserLine& line, unsigned& directivePosition) | ||||
| { | ||||
|     directivePosition = 0; | ||||
|     for (; directivePosition < line.m_line.size(); directivePosition++) | ||||
|     { | ||||
|         const auto c = line.m_line[directivePosition]; | ||||
|  | ||||
|         if (isspace(c)) | ||||
|             continue; | ||||
|  | ||||
|         return c == '#'; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "Parsing/IParserLineStream.h" | ||||
| #include "Parsing/TokenPos.h" | ||||
|  | ||||
| class AbstractDirectiveStreamProxy : public IParserLineStream | ||||
| { | ||||
| protected: | ||||
|     AbstractDirectiveStreamProxy() = default; | ||||
|  | ||||
|     static TokenPos CreatePos(const ParserLine& line, unsigned position); | ||||
|  | ||||
|     static bool SkipWhitespace(const ParserLine& line, unsigned& position); | ||||
|     static bool ExtractInteger(const ParserLine& line, unsigned& position, int& value); | ||||
|     static bool ExtractIdentifier(const ParserLine& line, unsigned& position); | ||||
|     static bool MatchCharacter(const ParserLine& line, unsigned& position, char c); | ||||
|     static bool MatchNextCharacter(const ParserLine& line, unsigned& position, char c); | ||||
|     static bool MatchString(const ParserLine& line, unsigned& position, const char* str, unsigned len); | ||||
|     static bool MatchNextString(const ParserLine& line, unsigned& position, const char* str, unsigned len); | ||||
|  | ||||
|     static bool FindDirective(const ParserLine& line, unsigned& directivePosition); | ||||
| }; | ||||
							
								
								
									
										348
									
								
								src/Parser/Parsing/Impl/AbstractLexer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								src/Parser/Parsing/Impl/AbstractLexer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,348 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cassert> | ||||
| #include <deque> | ||||
|  | ||||
| #include "Utils/ClassUtils.h" | ||||
| #include "Parsing/ILexer.h" | ||||
| #include "Parsing/IParserLineStream.h" | ||||
| #include "Parsing/ParsingException.h" | ||||
|  | ||||
| template <typename TokenType> | ||||
| class AbstractLexer : public ILexer<TokenType> | ||||
| { | ||||
|     // TokenType must inherit IParserValue | ||||
|     static_assert(std::is_base_of<IParserValue, TokenType>::value); | ||||
|  | ||||
| protected: | ||||
|     std::deque<TokenType> m_token_cache; | ||||
|     std::deque<ParserLine> m_line_cache; | ||||
|     IParserLineStream* const m_stream; | ||||
|  | ||||
|     unsigned m_line_index; | ||||
|     unsigned m_current_line_offset; | ||||
|  | ||||
|     explicit AbstractLexer(IParserLineStream* stream) | ||||
|         : m_stream(stream), | ||||
|           m_line_index(0u), | ||||
|           m_current_line_offset(0u) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     virtual TokenType GetNextToken() = 0; | ||||
|  | ||||
|     int NextChar() | ||||
|     { | ||||
|         while (true) | ||||
|         { | ||||
|             while (m_line_index >= m_line_cache.size()) | ||||
|             { | ||||
|                 if (!m_line_cache.empty() && m_line_cache.back().IsEof()) | ||||
|                     return EOF; | ||||
|  | ||||
|                 m_line_cache.push_back(m_stream->NextLine()); | ||||
|             } | ||||
|  | ||||
|             if (m_current_line_offset >= m_line_cache[m_line_index].m_line.size()) | ||||
|             { | ||||
|                 m_line_index++; | ||||
|                 m_current_line_offset = 0; | ||||
|             } | ||||
|             else | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return m_line_cache[m_line_index].m_line[m_current_line_offset++]; | ||||
|     } | ||||
|  | ||||
|     int PeekChar() | ||||
|     { | ||||
|         auto peekLineOffset = m_current_line_offset; | ||||
|         auto peekLine = m_line_index; | ||||
|  | ||||
|         while (true) | ||||
|         { | ||||
|             while (peekLine >= m_line_cache.size()) | ||||
|             { | ||||
|                 if (m_line_cache.back().IsEof()) | ||||
|                     return EOF; | ||||
|  | ||||
|                 m_line_cache.push_back(m_stream->NextLine()); | ||||
|             } | ||||
|  | ||||
|             if (peekLineOffset >= m_line_cache[peekLine].m_line.size()) | ||||
|             { | ||||
|                 peekLine++; | ||||
|                 peekLineOffset = 0; | ||||
|             } | ||||
|             else | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return m_line_cache[peekLine].m_line[peekLineOffset]; | ||||
|     } | ||||
|  | ||||
|     _NODISCARD const ParserLine& CurrentLine() const | ||||
|     { | ||||
|         return m_line_cache[m_line_index]; | ||||
|     } | ||||
|  | ||||
|     _NODISCARD bool IsLineEnd() const | ||||
|     { | ||||
|         return m_current_line_offset >= CurrentLine().m_line.size(); | ||||
|     } | ||||
|  | ||||
|     _NODISCARD bool NextCharInLineIs(const char c) | ||||
|     { | ||||
|         return !IsLineEnd() && PeekChar() == c; | ||||
|     } | ||||
|  | ||||
|     _NODISCARD TokenPos GetPreviousCharacterPos() const | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset); | ||||
|     } | ||||
|  | ||||
|     _NODISCARD TokenPos GetNextCharacterPos() | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         if (m_current_line_offset + 1 >= currentLine.m_line.size()) | ||||
|         { | ||||
|             PeekChar(); | ||||
|             return TokenPos(m_line_cache.back().m_filename, m_line_cache.back().m_line_number, 1); | ||||
|         } | ||||
|  | ||||
|         return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset + 1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * \brief Reads an identifier from the current position | ||||
|      * \return The value of the read identifier | ||||
|      */ | ||||
|     std::string ReadIdentifier() | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         assert(m_current_line_offset >= 1); | ||||
|         assert(isalpha(currentLine.m_line[m_current_line_offset - 1]) || currentLine.m_line[m_current_line_offset - 1] == '_'); | ||||
|  | ||||
|         const auto startPos = m_current_line_offset - 1; | ||||
|         const auto lineSize = currentLine.m_line.size(); | ||||
|         while (m_current_line_offset < lineSize) | ||||
|         { | ||||
|             const auto c = currentLine.m_line[m_current_line_offset]; | ||||
|  | ||||
|             if (!isalnum(c) && c != '_') | ||||
|                 break; | ||||
|  | ||||
|             m_current_line_offset++; | ||||
|         } | ||||
|  | ||||
|         return std::string(currentLine.m_line, startPos, m_current_line_offset - startPos); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * \brief Reads an identifier from the current position | ||||
|      * \return The value of the read identifier | ||||
|      */ | ||||
|     std::string ReadString() | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         assert(m_current_line_offset >= 1); | ||||
|         assert(currentLine.m_line[m_current_line_offset - 1] == '"'); | ||||
|  | ||||
|         const auto startPos = m_current_line_offset; | ||||
|         const auto lineSize = currentLine.m_line.size(); | ||||
|         while (true) | ||||
|         { | ||||
|             if (m_current_line_offset >= lineSize) | ||||
|                 throw ParsingException(TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset), "Unclosed string"); | ||||
|  | ||||
|             if (currentLine.m_line[m_current_line_offset] == '\"') | ||||
|                 break; | ||||
|  | ||||
|             m_current_line_offset++; | ||||
|         } | ||||
|  | ||||
|         return std::string(currentLine.m_line, startPos, m_current_line_offset++ - startPos); | ||||
|     } | ||||
|  | ||||
|     void ReadHexNumber(int& integerValue) | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         const auto* start = ¤tLine.m_line.c_str()[m_current_line_offset - 1]; | ||||
|         char* end; | ||||
|  | ||||
|         integerValue = static_cast<int>(std::strtoul(start, &end, 16)); | ||||
|         const auto numberLength = static_cast<unsigned>(end - start); | ||||
|         if (numberLength == 0 || isalnum(*end) || *end == '_') | ||||
|             throw ParsingException(GetPreviousCharacterPos(), "Invalid hex number"); | ||||
|  | ||||
|         m_current_line_offset += numberLength - 1; | ||||
|     } | ||||
|  | ||||
|     _NODISCARD bool IsIntegerNumber() const | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         const auto* currentCharacter = ¤tLine.m_line.c_str()[m_current_line_offset - 1]; | ||||
|         auto isInteger = true; | ||||
|         auto dot = false; | ||||
|         auto exponent = false; | ||||
|  | ||||
|         if (*currentCharacter == '-' || *currentCharacter == '+') | ||||
|             currentCharacter++; | ||||
|  | ||||
|         while (*currentCharacter) | ||||
|         { | ||||
|             if (isdigit(*currentCharacter)) | ||||
|             { | ||||
|             } | ||||
|             else if (*currentCharacter == '.') | ||||
|             { | ||||
|                 if (dot) | ||||
|                     throw ParsingException(GetPreviousCharacterPos(), "Invalid number"); | ||||
|                 dot = true; | ||||
|                 isInteger = false; | ||||
|             } | ||||
|             else if (*currentCharacter == 'e' || *currentCharacter == 'E') | ||||
|             { | ||||
|                 if (exponent) | ||||
|                     throw ParsingException(GetPreviousCharacterPos(), "Invalid number"); | ||||
|                 if (currentCharacter[1] == '-') | ||||
|                     currentCharacter++; | ||||
|                 exponent = true; | ||||
|                 isInteger = false; | ||||
|             } | ||||
|             else if (isalpha(*currentCharacter)) | ||||
|             { | ||||
|                 throw ParsingException(GetPreviousCharacterPos(), "Invalid number"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             currentCharacter++; | ||||
|         } | ||||
|  | ||||
|         return isInteger; | ||||
|     } | ||||
|  | ||||
|     int ReadInteger() | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         const auto* start = ¤tLine.m_line.c_str()[m_current_line_offset - 1]; | ||||
|         char* end; | ||||
|         const auto integerValue = std::strtol(start, &end, 10); | ||||
|         const auto numberLength = static_cast<unsigned>(end - start); | ||||
|  | ||||
|         if (numberLength == 0) | ||||
|             throw ParsingException(GetPreviousCharacterPos(), "Invalid number"); | ||||
|  | ||||
|         m_current_line_offset += numberLength - 1; | ||||
|  | ||||
|         return integerValue; | ||||
|     } | ||||
|  | ||||
|     double ReadFloatingPoint() | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         const auto* start = ¤tLine.m_line.c_str()[m_current_line_offset - 1]; | ||||
|         char* end; | ||||
|         const auto floatingPointValue = std::strtod(start, &end); | ||||
|         const auto numberLength = static_cast<unsigned>(end - start); | ||||
|  | ||||
|         if (numberLength == 0) | ||||
|             throw ParsingException(GetPreviousCharacterPos(), "Invalid number"); | ||||
|  | ||||
|         m_current_line_offset += numberLength - 1; | ||||
|  | ||||
|         return floatingPointValue; | ||||
|     } | ||||
|  | ||||
|     void ReadNumber(bool& isFloatingPoint, double& floatingPointValue, int& integerValue) | ||||
|     { | ||||
|         const auto& currentLine = CurrentLine(); | ||||
|         assert(m_current_line_offset >= 1); | ||||
|         assert(isdigit(currentLine.m_line[m_current_line_offset - 1])); | ||||
|  | ||||
|         const auto lineLength = currentLine.m_line.size(); | ||||
|         if (lineLength - m_current_line_offset >= 1 | ||||
|             && currentLine.m_line[m_current_line_offset - 1] == '0' | ||||
|             && currentLine.m_line[m_current_line_offset] == 'x') | ||||
|         { | ||||
|             isFloatingPoint = false; | ||||
|             ReadHexNumber(integerValue); | ||||
|         } | ||||
|         else if (IsIntegerNumber()) | ||||
|         { | ||||
|             isFloatingPoint = false; | ||||
|             integerValue = ReadInteger(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             isFloatingPoint = true; | ||||
|             floatingPointValue = ReadFloatingPoint(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     const TokenType& GetToken(unsigned index) override | ||||
|     { | ||||
|         assert(index >= 0); | ||||
|         while (index >= m_token_cache.size()) | ||||
|             m_token_cache.emplace_back(GetNextToken()); | ||||
|  | ||||
|         return m_token_cache[index]; | ||||
|     } | ||||
|  | ||||
|     void PopTokens(int amount) override | ||||
|     { | ||||
|         if (amount <= 0 || m_token_cache.empty()) | ||||
|             return; | ||||
|  | ||||
|         if (static_cast<int>(m_token_cache.size()) <= amount) | ||||
|         { | ||||
|             const auto& lastToken = m_token_cache.back(); | ||||
|             while (m_line_cache.front().m_line_number != lastToken.GetPos().m_line | ||||
|                 || m_line_cache.front().m_filename.get() != lastToken.GetPos().m_filename.get()) | ||||
|             { | ||||
|                 m_line_cache.pop_front(); | ||||
|                 m_line_index--; | ||||
|             } | ||||
|             m_token_cache.clear(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_token_cache.erase(m_token_cache.begin(), m_token_cache.begin() + amount); | ||||
|             const auto& firstToken = m_token_cache.front(); | ||||
|             while (m_line_cache.front().m_line_number != firstToken.GetPos().m_line | ||||
|                 || m_line_cache.front().m_filename.get() != firstToken.GetPos().m_filename.get()) | ||||
|             { | ||||
|                 m_line_cache.pop_front(); | ||||
|                 m_line_index--; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _NODISCARD bool IsEof() override | ||||
|     { | ||||
|         return GetToken(0).IsEof(); | ||||
|     } | ||||
|  | ||||
|     _NODISCARD const TokenPos& GetPos() override | ||||
|     { | ||||
|         return GetToken(0).GetPos(); | ||||
|     } | ||||
|  | ||||
|     _NODISCARD ParserLine GetLineForPos(const TokenPos& pos) const override | ||||
|     { | ||||
|         for(const auto& line : m_line_cache) | ||||
|         { | ||||
|             if (line.m_filename.get() == pos.m_filename.get() | ||||
|                 && line.m_line_number == pos.m_line) | ||||
|                 return line; | ||||
|         } | ||||
|  | ||||
|         return ParserLine(); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										97
									
								
								src/Parser/Parsing/Impl/AbstractParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/Parser/Parsing/Impl/AbstractParser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <iostream> | ||||
| #include <vector> | ||||
|  | ||||
| #include "Parsing/IParser.h" | ||||
| #include "Parsing/ILexer.h" | ||||
| #include "Parsing/Sequence/AbstractSequence.h" | ||||
| #include "Parsing/ParsingException.h" | ||||
|  | ||||
| template <typename TokenType, typename ParserState> | ||||
| class AbstractParser : public IParser | ||||
| { | ||||
|     // TokenType must inherit IParserValue | ||||
|     static_assert(std::is_base_of<IParserValue, TokenType>::value); | ||||
|  | ||||
| public: | ||||
|     typedef AbstractSequence<TokenType, ParserState> sequence_t; | ||||
|  | ||||
| protected: | ||||
|     ILexer<TokenType>* m_lexer; | ||||
|     std::unique_ptr<ParserState> m_state; | ||||
|  | ||||
|     explicit AbstractParser(ILexer<TokenType>* lexer, std::unique_ptr<ParserState> state) | ||||
|         : m_lexer(lexer), | ||||
|           m_state(std::move(state)) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     virtual const std::vector<sequence_t*>& GetTestsForState() = 0; | ||||
|  | ||||
| public: | ||||
|     virtual ~AbstractParser() override = default; | ||||
|     AbstractParser(const AbstractParser& other) = default; | ||||
|     AbstractParser(AbstractParser&& other) noexcept = default; | ||||
|     AbstractParser& operator=(const AbstractParser& other) = default; | ||||
|     AbstractParser& operator=(AbstractParser&& other) noexcept = default; | ||||
|  | ||||
|     bool Parse() override | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!m_lexer->IsEof()) | ||||
|             { | ||||
|                 auto testSuccessful = false; | ||||
|                 const auto& availableTests = GetTestsForState(); | ||||
|  | ||||
|                 for (const sequence_t* test : availableTests) | ||||
|                 { | ||||
|                     unsigned consumedTokenCount; | ||||
|  | ||||
|                     if (test->MatchSequence(m_lexer, m_state.get(), consumedTokenCount)) | ||||
|                     { | ||||
|                         m_lexer->PopTokens(consumedTokenCount); | ||||
|                         testSuccessful = true; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (!testSuccessful) | ||||
|                 { | ||||
|                     const TokenPos& pos = m_lexer->GetPos(); | ||||
|                     const ParserLine line = m_lexer->GetLineForPos(pos); | ||||
|  | ||||
|                     if (!line.IsEof()) | ||||
|                     { | ||||
|                         std::cout << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression:\n" | ||||
|                             << line.m_line.substr(pos.m_column - 1) << std::endl; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         std::cout << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression." << std::endl; | ||||
|                     } | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch (const ParsingException& e) | ||||
|         { | ||||
|             const auto pos = e.Position(); | ||||
|             const auto line = m_lexer->GetLineForPos(pos); | ||||
|  | ||||
|             if (!line.IsEof()) | ||||
|             { | ||||
|                 std::cout << "Error: " << e.FullMessage() << "\n" << line.m_line.substr(pos.m_column - 1) << std::endl; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 std::cout << "Error: " << e.FullMessage() << std::endl; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										79
									
								
								src/Parser/Parsing/Impl/CommentRemovingStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/Parser/Parsing/Impl/CommentRemovingStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| #include "CommentRemovingStreamProxy.h" | ||||
|  | ||||
| CommentRemovingStreamProxy::CommentRemovingStreamProxy(IParserLineStream* stream) | ||||
|     : m_stream(stream), | ||||
|       m_inside_multi_line_comment(false), | ||||
|       m_next_line_is_comment(false) | ||||
| { | ||||
| } | ||||
|  | ||||
| ParserLine CommentRemovingStreamProxy::NextLine() | ||||
| { | ||||
|     auto line = m_stream->NextLine(); | ||||
|  | ||||
|     if (m_next_line_is_comment) | ||||
|     { | ||||
|         m_next_line_is_comment = !line.m_line.empty() && line.m_line[line.m_line.size() - 1] == '\\'; | ||||
|         return ParserLine(line.m_filename, line.m_line_number, std::string()); | ||||
|     } | ||||
|  | ||||
|     unsigned multiLineCommentStart = 0; | ||||
|     for (auto i = 0u; i < line.m_line.size(); i++) | ||||
|     { | ||||
|         const auto c = line.m_line[i]; | ||||
|  | ||||
|         if (m_inside_multi_line_comment) | ||||
|         { | ||||
|             if (c == '*' && i + 1 < line.m_line.size() && line.m_line[i + 1] == '/') | ||||
|             { | ||||
|                 line.m_line.erase(multiLineCommentStart, i + 2 - multiLineCommentStart); | ||||
|                 multiLineCommentStart = 0; | ||||
|                 m_inside_multi_line_comment = false; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if(c == '/' && i + 1 < line.m_line.size()) | ||||
|             { | ||||
|                 const auto c1 = line.m_line[i + 1]; | ||||
|  | ||||
|                 if (c1 == '*') | ||||
|                 { | ||||
|                     multiLineCommentStart = i; | ||||
|                     m_inside_multi_line_comment = true; | ||||
|                 } | ||||
|                 else if(c1 == '/') | ||||
|                 { | ||||
|                     m_next_line_is_comment = line.m_line[line.m_line.size() - 1] == '\\'; | ||||
|                     line.m_line.erase(i); | ||||
|                     return line; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if(m_inside_multi_line_comment) | ||||
|         line.m_line.erase(multiLineCommentStart); | ||||
|  | ||||
|     return line; | ||||
| } | ||||
|  | ||||
| bool CommentRemovingStreamProxy::IncludeFile(const std::string& filename) | ||||
| { | ||||
|     return m_stream->IncludeFile(filename); | ||||
| } | ||||
|  | ||||
| void CommentRemovingStreamProxy::PopCurrentFile() | ||||
| { | ||||
|     m_stream->PopCurrentFile(); | ||||
| } | ||||
|  | ||||
| bool CommentRemovingStreamProxy::IsOpen() const | ||||
| { | ||||
|     return m_stream->IsOpen(); | ||||
| } | ||||
|  | ||||
| bool CommentRemovingStreamProxy::Eof() const | ||||
| { | ||||
|     return m_stream->Eof(); | ||||
| } | ||||
							
								
								
									
										19
									
								
								src/Parser/Parsing/Impl/CommentRemovingStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Parser/Parsing/Impl/CommentRemovingStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "Parsing/IParserLineStream.h" | ||||
|  | ||||
| class CommentRemovingStreamProxy final : public IParserLineStream | ||||
| { | ||||
|     IParserLineStream* const m_stream; | ||||
|     bool m_inside_multi_line_comment; | ||||
|     bool m_next_line_is_comment; | ||||
|  | ||||
| public: | ||||
|     explicit CommentRemovingStreamProxy(IParserLineStream* stream); | ||||
|  | ||||
|     ParserLine NextLine() override; | ||||
|     bool IncludeFile(const std::string& filename) override; | ||||
|     void PopCurrentFile() override; | ||||
|     _NODISCARD bool IsOpen() const override; | ||||
|     _NODISCARD bool Eof() const override; | ||||
| }; | ||||
							
								
								
									
										470
									
								
								src/Parser/Parsing/Impl/DefinesStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								src/Parser/Parsing/Impl/DefinesStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,470 @@ | ||||
| #include "DefinesStreamProxy.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <utility> | ||||
|  | ||||
| #include "Parsing/ParsingException.h" | ||||
|  | ||||
| DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition() | ||||
|     : m_parameter_index(0u), | ||||
|       m_parameter_position(0u) | ||||
| { | ||||
| } | ||||
|  | ||||
| DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition(const unsigned index, const unsigned position) | ||||
|     : m_parameter_index(index), | ||||
|       m_parameter_position(position) | ||||
| { | ||||
| } | ||||
|  | ||||
| DefinesStreamProxy::Define::Define() | ||||
| = default; | ||||
|  | ||||
| DefinesStreamProxy::Define::Define(std::string name, std::string value) | ||||
|     : m_name(std::move(name)), | ||||
|       m_value(std::move(value)) | ||||
| { | ||||
| } | ||||
|  | ||||
| std::string DefinesStreamProxy::Define::Render(std::vector<std::string>& parameterValues) | ||||
| { | ||||
|     if (parameterValues.empty() || m_parameter_positions.empty()) | ||||
|         return m_value; | ||||
|  | ||||
|     std::ostringstream str; | ||||
|     auto lastPos = 0u; | ||||
|     for (const auto& parameterPosition : m_parameter_positions) | ||||
|     { | ||||
|         if (lastPos < parameterPosition.m_parameter_position) | ||||
|             str << std::string(m_value, lastPos, parameterPosition.m_parameter_position - lastPos); | ||||
|  | ||||
|         if (parameterPosition.m_parameter_index < parameterValues.size()) | ||||
|         { | ||||
|             str << parameterValues[parameterPosition.m_parameter_index]; | ||||
|         } | ||||
|  | ||||
|         lastPos = parameterPosition.m_parameter_position; | ||||
|     } | ||||
|  | ||||
|     if (lastPos < m_value.size()) | ||||
|         str << std::string(m_value, lastPos, m_value.size() - lastPos); | ||||
|  | ||||
|     return str.str(); | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::Define::IdentifyParameters(std::vector<std::string>& parameterNames) | ||||
| { | ||||
|     if (parameterNames.empty()) | ||||
|         return; | ||||
|  | ||||
|     auto inWord = false; | ||||
|     auto wordStart = 0u; | ||||
|     for (auto i = 0u; i < m_value.size(); i++) | ||||
|     { | ||||
|         const auto c = m_value[i]; | ||||
|         if (!isalnum(c)) | ||||
|         { | ||||
|             if (inWord) | ||||
|             { | ||||
|                 const std::string word(m_value, wordStart, i - wordStart); | ||||
|  | ||||
|                 auto parameterIndex = 0u; | ||||
|                 for (; parameterIndex < parameterNames.size(); parameterIndex++) | ||||
|                 { | ||||
|                     if (word == parameterNames[parameterIndex]) | ||||
|                         break; | ||||
|                 } | ||||
|  | ||||
|                 if (parameterIndex < parameterNames.size()) | ||||
|                 { | ||||
|                     m_value.erase(wordStart, i - wordStart); | ||||
|                     m_parameter_positions.emplace_back(parameterIndex, wordStart); | ||||
|                     i = wordStart; | ||||
|                 } | ||||
|  | ||||
|                 inWord = false; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (!inWord && (isalpha(c) || c == '_')) | ||||
|             { | ||||
|                 inWord = true; | ||||
|                 wordStart = i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (inWord) | ||||
|     { | ||||
|         const std::string word(m_value, wordStart, m_value.size() - wordStart); | ||||
|  | ||||
|         auto parameterIndex = 0u; | ||||
|         for (; parameterIndex < parameterNames.size(); parameterIndex++) | ||||
|         { | ||||
|             if (word == parameterNames[parameterIndex]) | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         if (parameterIndex < parameterNames.size()) | ||||
|         { | ||||
|             m_value.erase(wordStart, m_value.size() - wordStart); | ||||
|             m_parameter_positions.emplace_back(parameterIndex, wordStart); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| DefinesStreamProxy::DefinesStreamProxy(IParserLineStream* stream) | ||||
|     : m_stream(stream), | ||||
|       m_ignore_depth(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| std::vector<std::string> DefinesStreamProxy::MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition) | ||||
| { | ||||
|     if (line.m_line[parameterPosition] != '(') | ||||
|         return std::vector<std::string>(); | ||||
|  | ||||
|     parameterPosition++; | ||||
|     std::vector<std::string> parameters; | ||||
|  | ||||
|     while (true) | ||||
|     { | ||||
|         if (!SkipWhitespace(line, parameterPosition) || parameterPosition >= line.m_line.size()) | ||||
|             throw ParsingException(CreatePos(line, parameterPosition), "Invalid define parameter list"); | ||||
|  | ||||
|         const auto nameStartPos = parameterPosition; | ||||
|         if (!ExtractIdentifier(line, parameterPosition)) | ||||
|             throw ParsingException(CreatePos(line, parameterPosition), "Cannot extract name of parameter of define"); | ||||
|  | ||||
|         parameters.emplace_back(std::string(line.m_line, nameStartPos, parameterPosition - nameStartPos)); | ||||
|  | ||||
|         if (parameterPosition >= line.m_line.size()) | ||||
|             throw ParsingException(CreatePos(line, parameterPosition), "Unclosed define parameters"); | ||||
|  | ||||
|         if (line.m_line[parameterPosition] == ')') | ||||
|         { | ||||
|             parameterPosition++; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (line.m_line[parameterPosition] != ',') | ||||
|             throw ParsingException(CreatePos(line, parameterPosition), "Unknown symbol in define parameter list"); | ||||
|  | ||||
|         parameterPosition++; | ||||
|     } | ||||
|  | ||||
|     return parameters; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     if (!MatchString(line, directivePosition, DEFINE_DIRECTIVE, std::char_traits<char>::length(DEFINE_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     const auto nameStartPos = directivePosition; | ||||
|     if (!ExtractIdentifier(line, directivePosition)) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name."); | ||||
|  | ||||
|     const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos); | ||||
|  | ||||
|     auto parameters = MatchDefineParameters(line, directivePosition); | ||||
|  | ||||
|     std::string value; | ||||
|     if (directivePosition < line.m_line.size()) | ||||
|         value = line.m_line.substr(directivePosition + 1); | ||||
|  | ||||
|     Define define(name, value); | ||||
|     define.IdentifyParameters(parameters); | ||||
|     AddDefine(std::move(define)); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     if (!MatchString(line, directivePosition, UNDEF_DIRECTIVE, std::char_traits<char>::length(UNDEF_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     if (!SkipWhitespace(line, directivePosition)) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot undef without a name."); | ||||
|  | ||||
|     const auto nameStartPos = directivePosition; | ||||
|     if (!ExtractIdentifier(line, directivePosition)) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name."); | ||||
|  | ||||
|     const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos); | ||||
|     const auto entry = m_defines.find(name); | ||||
|  | ||||
|     if (entry != m_defines.end()) | ||||
|         m_defines.erase(entry); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     auto reverse = false; | ||||
|     if (!MatchString(line, directivePosition, IFDEF_DIRECTIVE, std::char_traits<char>::length(IFDEF_DIRECTIVE))) | ||||
|     { | ||||
|         if (!MatchString(line, directivePosition, IFNDEF_DIRECTIVE, std::char_traits<char>::length(IFNDEF_DIRECTIVE))) | ||||
|             return false; | ||||
|  | ||||
|         reverse = true; | ||||
|     } | ||||
|  | ||||
|     if (!m_modes.empty() && !m_modes.top()) | ||||
|     { | ||||
|         m_ignore_depth++; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (!SkipWhitespace(line, directivePosition)) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name."); | ||||
|  | ||||
|     const auto nameStartPos = directivePosition; | ||||
|     if (!ExtractIdentifier(line, directivePosition)) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name."); | ||||
|  | ||||
|     const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos); | ||||
|     const auto entry = m_defines.find(name); | ||||
|  | ||||
|     if (entry != m_defines.end()) | ||||
|         m_modes.push(!reverse); | ||||
|     else | ||||
|         m_modes.push(reverse); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     if (!MatchString(line, directivePosition, ELSE_DIRECTIVE, std::char_traits<char>::length(ELSE_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     if (m_ignore_depth > 0) | ||||
|         return true; | ||||
|  | ||||
|     if (!m_modes.empty()) | ||||
|         m_modes.top() = !m_modes.top(); | ||||
|     else | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot use else without ifdef"); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     if (!MatchString(line, directivePosition, ENDIF_DIRECTIVE, std::char_traits<char>::length(ENDIF_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     if (m_ignore_depth > 0) | ||||
|     { | ||||
|         m_ignore_depth--; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (!m_modes.empty()) | ||||
|         m_modes.pop(); | ||||
|     else | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Cannot use endif without ifdef"); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::MatchDirectives(const ParserLine& line) | ||||
| { | ||||
|     unsigned directivePos; | ||||
|  | ||||
|     if (!FindDirective(line, directivePos)) | ||||
|         return false; | ||||
|  | ||||
|     directivePos++; | ||||
|  | ||||
|     if (m_modes.empty() || m_modes.top() == true) | ||||
|     { | ||||
|         if (MatchDefineDirective(line, directivePos) | ||||
|             || MatchUndefDirective(line, directivePos)) | ||||
|             return true; | ||||
|     } | ||||
|  | ||||
|     return MatchIfdefDirective(line, directivePos) | ||||
|         || MatchElseDirective(line, directivePos) | ||||
|         || MatchEndifDirective(line, directivePos); | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::FindDefineForWord(const ParserLine& line, const unsigned wordStart, const unsigned wordEnd, Define*& value) | ||||
| { | ||||
|     const std::string word(line.m_line, wordStart, wordEnd - wordStart); | ||||
|     const auto foundEntry = m_defines.find(word); | ||||
|     if (foundEntry != m_defines.end()) | ||||
|     { | ||||
|         value = &foundEntry->second; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::ExtractParametersFromDefineUsage(const ParserLine& line, const unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues) | ||||
| { | ||||
|     if (line.m_line[parameterStart] != '(') | ||||
|         return; | ||||
|  | ||||
|     std::ostringstream currentValue; | ||||
|     auto pos = parameterStart + 1; | ||||
|     auto valueHasStarted = false; | ||||
|     while (true) | ||||
|     { | ||||
|         if (pos >= line.m_line.size()) | ||||
|             throw ParsingException(CreatePos(line, pos), "Invalid use of define"); | ||||
|  | ||||
|         const auto c = line.m_line[pos]; | ||||
|  | ||||
|         if (c == ',') | ||||
|         { | ||||
|             parameterValues.emplace_back(currentValue.str()); | ||||
|             currentValue.clear(); | ||||
|             currentValue.str(std::string()); | ||||
|             valueHasStarted = false; | ||||
|         } | ||||
|         else if (c == ')') | ||||
|         { | ||||
|             parameterValues.emplace_back(currentValue.str()); | ||||
|             parameterEnd = pos + 1; | ||||
|             break; | ||||
|         } | ||||
|         else if (valueHasStarted || !isspace(c)) | ||||
|         { | ||||
|             valueHasStarted = true; | ||||
|             currentValue << c; | ||||
|         } | ||||
|  | ||||
|         pos++; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::ExpandDefines(ParserLine& line) | ||||
| { | ||||
|     auto wordStart = 0u; | ||||
|     auto lastWordEnd = 0u; | ||||
|     auto inWord = false; | ||||
|     Define* value; | ||||
|  | ||||
|     std::ostringstream str; | ||||
|     auto usesDefines = false; | ||||
|  | ||||
|     for (auto i = 0u; i < line.m_line.size(); i++) | ||||
|     { | ||||
|         const auto c = line.m_line[i]; | ||||
|         if (!inWord) | ||||
|         { | ||||
|             if (isalpha(c) || c == '_') | ||||
|             { | ||||
|                 wordStart = i; | ||||
|                 inWord = true; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (!isalnum(c) && c != '_') | ||||
|             { | ||||
|                 if (FindDefineForWord(line, wordStart, i, value)) | ||||
|                 { | ||||
|                     std::vector<std::string> parameterValues; | ||||
|                     ExtractParametersFromDefineUsage(line, i, i, parameterValues); | ||||
|                     const auto defineValue = value->Render(parameterValues); | ||||
|  | ||||
|                     if (!usesDefines) | ||||
|                     { | ||||
|                         str << std::string(line.m_line, 0, wordStart) << defineValue; | ||||
|                         usesDefines = true; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         str << std::string(line.m_line, lastWordEnd, wordStart - lastWordEnd) << defineValue; | ||||
|                     } | ||||
|                     lastWordEnd = i; | ||||
|                 } | ||||
|                 inWord = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (inWord) | ||||
|     { | ||||
|         if (FindDefineForWord(line, wordStart, line.m_line.size(), value)) | ||||
|         { | ||||
|             std::vector<std::string> parameterValues; | ||||
|             const auto defineValue = value->Render(parameterValues); | ||||
|  | ||||
|             if (!usesDefines) | ||||
|             { | ||||
|                 str << std::string(line.m_line, 0, wordStart) << defineValue; | ||||
|                 usesDefines = true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 str << std::string(line.m_line, lastWordEnd, wordStart - lastWordEnd) << defineValue; | ||||
|             } | ||||
|             lastWordEnd = line.m_line.size(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (usesDefines) | ||||
|     { | ||||
|         if (lastWordEnd < line.m_line.size()) | ||||
|             str << std::string(line.m_line, lastWordEnd, line.m_line.size() - lastWordEnd); | ||||
|         line.m_line = str.str(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::AddDefine(Define define) | ||||
| { | ||||
|     m_defines[define.m_name] = std::move(define); | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::Undefine(const std::string& name) | ||||
| { | ||||
|     const auto entry = m_defines.find(name); | ||||
|  | ||||
|     if (entry != m_defines.end()) | ||||
|         m_defines.erase(entry); | ||||
| } | ||||
|  | ||||
| ParserLine DefinesStreamProxy::NextLine() | ||||
| { | ||||
|     auto line = m_stream->NextLine(); | ||||
|  | ||||
|     if (MatchDirectives(line) | ||||
|         || !m_modes.empty() && !m_modes.top()) | ||||
|     { | ||||
|         line.m_line.clear(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         ExpandDefines(line); | ||||
|     } | ||||
|  | ||||
|     return line; | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::IncludeFile(const std::string& filename) | ||||
| { | ||||
|     return m_stream->IncludeFile(filename); | ||||
| } | ||||
|  | ||||
| void DefinesStreamProxy::PopCurrentFile() | ||||
| { | ||||
|     m_stream->PopCurrentFile(); | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::IsOpen() const | ||||
| { | ||||
|     return m_stream->IsOpen(); | ||||
| } | ||||
|  | ||||
| bool DefinesStreamProxy::Eof() const | ||||
| { | ||||
|     return m_stream->Eof(); | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/Parser/Parsing/Impl/DefinesStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/Parser/Parsing/Impl/DefinesStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <unordered_map> | ||||
| #include <stack> | ||||
|  | ||||
| #include "AbstractDirectiveStreamProxy.h" | ||||
| #include "Parsing/IParserLineStream.h" | ||||
|  | ||||
| class DefinesStreamProxy final : public AbstractDirectiveStreamProxy | ||||
| { | ||||
|     static constexpr const char* DEFINE_DIRECTIVE = "define "; | ||||
|     static constexpr const char* UNDEF_DIRECTIVE = "undef "; | ||||
|     static constexpr const char* IFDEF_DIRECTIVE = "ifdef "; | ||||
|     static constexpr const char* IFNDEF_DIRECTIVE = "ifndef "; | ||||
|     static constexpr const char* ELSE_DIRECTIVE = "else"; | ||||
|     static constexpr const char* ENDIF_DIRECTIVE = "endif"; | ||||
|  | ||||
| public: | ||||
|     class DefineParameterPosition | ||||
|     { | ||||
|     public: | ||||
|         unsigned m_parameter_index; | ||||
|         unsigned m_parameter_position; | ||||
|  | ||||
|         DefineParameterPosition(); | ||||
|         DefineParameterPosition(unsigned index, unsigned position); | ||||
|     }; | ||||
|  | ||||
|     class Define | ||||
|     { | ||||
|     public: | ||||
|         std::string m_name; | ||||
|         std::string m_value; | ||||
|         std::vector<DefineParameterPosition> m_parameter_positions; | ||||
|  | ||||
|         Define(); | ||||
|         Define(std::string name, std::string value); | ||||
|         void IdentifyParameters(std::vector<std::string>& parameterNames); | ||||
|         std::string Render(std::vector<std::string>& parameterValues); | ||||
|     }; | ||||
|  | ||||
| private: | ||||
|     IParserLineStream* const m_stream; | ||||
|     std::unordered_map<std::string, Define> m_defines; | ||||
|     std::stack<bool> m_modes; | ||||
|     unsigned m_ignore_depth; | ||||
|      | ||||
|     std::vector<std::string> MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition); | ||||
|     _NODISCARD bool MatchDefineDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchUndefDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchIfdefDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchElseDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchEndifDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchDirectives(const ParserLine& line); | ||||
|      | ||||
|     void ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues); | ||||
|     bool FindDefineForWord(const ParserLine& line, unsigned wordStart, unsigned wordEnd, Define*& value); | ||||
|     void ExpandDefines(ParserLine& line); | ||||
|  | ||||
| public: | ||||
|     explicit DefinesStreamProxy(IParserLineStream* stream); | ||||
|  | ||||
|     void AddDefine(Define define); | ||||
|     void Undefine(const std::string& name); | ||||
|  | ||||
|     ParserLine NextLine() override; | ||||
|     bool IncludeFile(const std::string& filename) override; | ||||
|     void PopCurrentFile() override; | ||||
|     _NODISCARD bool IsOpen() const override; | ||||
|     _NODISCARD bool Eof() const override; | ||||
| }; | ||||
							
								
								
									
										142
									
								
								src/Parser/Parsing/Impl/IncludingStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/Parser/Parsing/Impl/IncludingStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| #include "IncludingStreamProxy.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <filesystem> | ||||
|  | ||||
| #include "Parsing/ParsingException.h" | ||||
|  | ||||
| namespace fs = std::filesystem; | ||||
|  | ||||
| IncludingStreamProxy::IncludingStreamProxy(IParserLineStream* stream) | ||||
|     : m_stream(stream) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition) | ||||
| { | ||||
|     auto currentPos = includeDirectivePosition; | ||||
|     bool isDoubleQuotes; | ||||
|  | ||||
|     while (isspace(line.m_line[currentPos])) | ||||
|     { | ||||
|         if (currentPos++ >= line.m_line.size()) | ||||
|             throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR); | ||||
|     } | ||||
|  | ||||
|     if (line.m_line[currentPos] == '"') | ||||
|         isDoubleQuotes = true; | ||||
|     else if (line.m_line[currentPos] == '<') | ||||
|         isDoubleQuotes = false; | ||||
|     else | ||||
|         throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR); | ||||
|  | ||||
|     filenameStartPosition = ++currentPos; | ||||
|     filenameEndPosition = 0; | ||||
|  | ||||
|     for (; currentPos < line.m_line.size(); currentPos++) | ||||
|     { | ||||
|         const auto c = line.m_line[currentPos]; | ||||
|  | ||||
|         if (c == '"') | ||||
|         { | ||||
|             if (!isDoubleQuotes) | ||||
|                 throw ParsingException(CreatePos(line, currentPos - 1), ""); | ||||
|             filenameEndPosition = currentPos; | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (c == '>') | ||||
|         { | ||||
|             if (isDoubleQuotes) | ||||
|                 throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR); | ||||
|             filenameEndPosition = currentPos; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, unsigned directivePosition) const | ||||
| { | ||||
|     if (!MatchString(line, directivePosition, INCLUDE_DIRECTIVE, std::char_traits<char>::length(INCLUDE_DIRECTIVE))) | ||||
|         return false; | ||||
|      | ||||
|     unsigned filenameStart, filenameEnd; | ||||
|  | ||||
|     if (!ExtractIncludeFilename(line, directivePosition, filenameStart, filenameEnd)) | ||||
|         throw ParsingException(TokenPos(line.m_filename, line.m_line_number, directivePosition), INCLUDE_QUOTES_ERROR); | ||||
|  | ||||
|     if (filenameEnd <= filenameStart) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "No filename specified"); | ||||
|  | ||||
|     const auto filename = line.m_line.substr(filenameStart, filenameEnd - filenameStart); | ||||
|  | ||||
|     if (!m_stream->IncludeFile(filename)) | ||||
|     { | ||||
|         std::ostringstream errorStr; | ||||
|         errorStr << "Could not include file \"" << filename << "\""; | ||||
|         throw ParsingException(CreatePos(line, directivePosition), errorStr.str()); | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     if(!MatchString(line, directivePosition, PRAGMA_ONCE_DIRECTIVE, std::char_traits<char>::length(PRAGMA_ONCE_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     const auto absolutePath = absolute(fs::path(line.m_filename.get())); | ||||
|     const auto absolutePathStr = absolutePath.string(); | ||||
|  | ||||
|     const auto existingPath = m_included_files.find(absolutePathStr); | ||||
|     if (existingPath != m_included_files.end()) | ||||
|         m_stream->PopCurrentFile(); | ||||
|     else | ||||
|         m_included_files.emplace(absolutePathStr); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::MatchDirectives(const ParserLine& line) | ||||
| { | ||||
|     unsigned directivePos; | ||||
|  | ||||
|     if (!FindDirective(line, directivePos)) | ||||
|         return false; | ||||
|  | ||||
|     directivePos++; | ||||
|     return MatchIncludeDirective(line, directivePos) | ||||
|         || MatchPragmaOnceDirective(line, directivePos); | ||||
| } | ||||
|  | ||||
| ParserLine IncludingStreamProxy::NextLine() | ||||
| { | ||||
|     auto line = m_stream->NextLine(); | ||||
|  | ||||
|     while(MatchDirectives(line)) | ||||
|         line = m_stream->NextLine(); | ||||
|  | ||||
|     return line; | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::IncludeFile(const std::string& filename) | ||||
| { | ||||
|     return m_stream->IncludeFile(filename); | ||||
| } | ||||
|  | ||||
| void IncludingStreamProxy::PopCurrentFile() | ||||
| { | ||||
|     m_stream->PopCurrentFile(); | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::IsOpen() const | ||||
| { | ||||
|     return m_stream->IsOpen(); | ||||
| } | ||||
|  | ||||
| bool IncludingStreamProxy::Eof() const | ||||
| { | ||||
|     return m_stream->Eof(); | ||||
| } | ||||
							
								
								
									
										30
									
								
								src/Parser/Parsing/Impl/IncludingStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Parser/Parsing/Impl/IncludingStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <set> | ||||
|  | ||||
| #include "AbstractDirectiveStreamProxy.h" | ||||
| #include "Parsing/IParserLineStream.h" | ||||
|  | ||||
| class IncludingStreamProxy final : public AbstractDirectiveStreamProxy | ||||
| { | ||||
|     static constexpr const char* INCLUDE_QUOTES_ERROR = "Invalid include directive. Expected \"\" or <>"; | ||||
|     static constexpr const char* INCLUDE_DIRECTIVE = "include "; | ||||
|     static constexpr const char* PRAGMA_ONCE_DIRECTIVE = "pragma once"; | ||||
|  | ||||
|     IParserLineStream* const m_stream; | ||||
|     std::set<std::string> m_included_files; | ||||
|      | ||||
|     _NODISCARD static bool ExtractIncludeFilename(const ParserLine& line, unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition); | ||||
|     _NODISCARD bool MatchIncludeDirective(const ParserLine& line, unsigned directivePosition) const; | ||||
|     _NODISCARD bool MatchPragmaOnceDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchDirectives(const ParserLine& line); | ||||
|  | ||||
| public: | ||||
|     explicit IncludingStreamProxy(IParserLineStream* stream); | ||||
|  | ||||
|     ParserLine NextLine() override; | ||||
|     bool IncludeFile(const std::string& filename) override; | ||||
|     void PopCurrentFile() override; | ||||
|     _NODISCARD bool IsOpen() const override; | ||||
|     _NODISCARD bool Eof() const override; | ||||
| }; | ||||
							
								
								
									
										92
									
								
								src/Parser/Parsing/Impl/PackDefinitionStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/Parser/Parsing/Impl/PackDefinitionStreamProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| #include "PackDefinitionStreamProxy.h" | ||||
|  | ||||
| #include "Parsing/ParsingException.h" | ||||
|  | ||||
| PackDefinitionStreamProxy::PackDefinitionStreamProxy(IParserLineStream* stream) | ||||
|     : m_stream(stream) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool PackDefinitionStreamProxy::MatchPackDirective(const ParserLine& line, unsigned directivePosition) | ||||
| { | ||||
|     auto packValue = 0; | ||||
|  | ||||
|     if (!MatchString(line, directivePosition, PRAGMA_PACK_DIRECTIVE, std::char_traits<char>::length(PRAGMA_PACK_DIRECTIVE))) | ||||
|         return false; | ||||
|  | ||||
|     if (!MatchNextCharacter(line, directivePosition, '(')) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive."); | ||||
|  | ||||
|     bool isPush; | ||||
|     if (MatchNextString(line, directivePosition, PUSH_KEYWORD, std::char_traits<char>::length(PUSH_KEYWORD))) | ||||
|         isPush = true; | ||||
|     else if (MatchNextString(line, directivePosition, POP_KEYWORD, std::char_traits<char>::length(POP_KEYWORD))) | ||||
|         isPush = false; | ||||
|     else | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Unknown pack directive command."); | ||||
|  | ||||
|     if(isPush) | ||||
|     { | ||||
|         if (!MatchNextCharacter(line, directivePosition, ',')) | ||||
|             throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive."); | ||||
|  | ||||
|         if (!ExtractInteger(line, directivePosition, packValue)) | ||||
|             throw ParsingException(CreatePos(line, directivePosition), "Invalid pack value."); | ||||
|     } | ||||
|  | ||||
|     if (!MatchNextCharacter(line, directivePosition, ')')) | ||||
|         throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive."); | ||||
|  | ||||
|     if(isPush) | ||||
|         m_current_pack.push(packValue); | ||||
|     else if (!m_current_pack.empty()) | ||||
|         m_current_pack.pop(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool PackDefinitionStreamProxy::MatchDirectives(const ParserLine& line) | ||||
| { | ||||
|     unsigned directivePos; | ||||
|  | ||||
|     if (!FindDirective(line, directivePos)) | ||||
|         return false; | ||||
|  | ||||
|     directivePos++; | ||||
|     return MatchPackDirective(line, directivePos); | ||||
| } | ||||
|  | ||||
| ParserLine PackDefinitionStreamProxy::NextLine() | ||||
| { | ||||
|     auto line = m_stream->NextLine(); | ||||
|  | ||||
|     while (MatchDirectives(line)) | ||||
|         line = m_stream->NextLine(); | ||||
|  | ||||
|     return line; | ||||
| } | ||||
|  | ||||
| bool PackDefinitionStreamProxy::IncludeFile(const std::string& filename) | ||||
| { | ||||
|     return m_stream->IncludeFile(filename); | ||||
| } | ||||
|  | ||||
| void PackDefinitionStreamProxy::PopCurrentFile() | ||||
| { | ||||
|     m_stream->PopCurrentFile(); | ||||
| } | ||||
|  | ||||
| bool PackDefinitionStreamProxy::IsOpen() const | ||||
| { | ||||
|     return m_stream->IsOpen(); | ||||
| } | ||||
|  | ||||
| bool PackDefinitionStreamProxy::Eof() const | ||||
| { | ||||
|     return m_stream->Eof(); | ||||
| } | ||||
|  | ||||
| int PackDefinitionStreamProxy::GetCurrentPack() const | ||||
| { | ||||
|     return m_current_pack.empty() ? DEFAULT_PACK : m_current_pack.top(); | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/Parser/Parsing/Impl/PackDefinitionStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/Parser/Parsing/Impl/PackDefinitionStreamProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stack> | ||||
|  | ||||
| #include "AbstractDirectiveStreamProxy.h" | ||||
| #include "Parsing/IPackValueSupplier.h" | ||||
| #include "Parsing/IParserLineStream.h" | ||||
|  | ||||
| class PackDefinitionStreamProxy final : public AbstractDirectiveStreamProxy, public IPackValueSupplier | ||||
| { | ||||
| public: | ||||
|     static constexpr int DEFAULT_PACK = 8; | ||||
|  | ||||
| private: | ||||
|     static constexpr const char* PRAGMA_PACK_DIRECTIVE = "pragma pack"; | ||||
|     static constexpr const char* PUSH_KEYWORD = "push"; | ||||
|     static constexpr const char* POP_KEYWORD = "pop"; | ||||
|  | ||||
|     IParserLineStream* const m_stream; | ||||
|     std::stack<int> m_current_pack; | ||||
|  | ||||
|     _NODISCARD bool MatchPackDirective(const ParserLine& line, unsigned directivePosition); | ||||
|     _NODISCARD bool MatchDirectives(const ParserLine& line); | ||||
|  | ||||
| public: | ||||
|     explicit PackDefinitionStreamProxy(IParserLineStream* stream); | ||||
|  | ||||
|     ParserLine NextLine() override; | ||||
|     bool IncludeFile(const std::string& filename) override; | ||||
|     void PopCurrentFile() override; | ||||
|     _NODISCARD bool IsOpen() const override; | ||||
|     _NODISCARD bool Eof() const override; | ||||
|  | ||||
|     int GetCurrentPack() const override; | ||||
| }; | ||||
							
								
								
									
										100
									
								
								src/Parser/Parsing/Impl/ParserFilesystemStream.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/Parser/Parsing/Impl/ParserFilesystemStream.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| #include "ParserFilesystemStream.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <filesystem> | ||||
|  | ||||
| namespace fs = std::filesystem; | ||||
|  | ||||
| ParserFilesystemStream::FileInfo::FileInfo(std::string filePath) | ||||
|     : m_file_path(std::move(filePath)), | ||||
|       m_stream(m_file_path), | ||||
|       m_line_number(1) | ||||
| { | ||||
| } | ||||
|  | ||||
| ParserFilesystemStream::ParserFilesystemStream(std::string path) | ||||
| { | ||||
|     const auto absolutePath = absolute(fs::path(path)); | ||||
|     m_files.emplace(FileInfo(absolutePath.string())); | ||||
| } | ||||
|  | ||||
| bool ParserFilesystemStream::IsOpen() const | ||||
| { | ||||
|     return !m_files.empty() | ||||
|         && m_files.top().m_stream.is_open(); | ||||
| } | ||||
|  | ||||
| ParserLine ParserFilesystemStream::NextLine() | ||||
| { | ||||
|     std::ostringstream str; | ||||
|     auto hasLength = false; | ||||
|  | ||||
|     if (m_files.empty()) | ||||
|         return ParserLine(); | ||||
|  | ||||
|     while(!m_files.empty()) | ||||
|     { | ||||
|         auto& fileInfo = m_files.top(); | ||||
|  | ||||
|         auto c = fileInfo.m_stream.get(); | ||||
|         while (c != EOF) | ||||
|         { | ||||
|             switch (c) | ||||
|             { | ||||
|             case '\r': | ||||
|                 c = fileInfo.m_stream.get(); | ||||
|                 if (c == '\n') | ||||
|                     return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number++, str.str()); | ||||
|                 str << '\r'; | ||||
|                 hasLength = true; | ||||
|                 continue; | ||||
|  | ||||
|             case '\n': | ||||
|                 return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number++, str.str()); | ||||
|  | ||||
|             default: | ||||
|                 str << static_cast<char>(c); | ||||
|                 hasLength = true; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             c = fileInfo.m_stream.get(); | ||||
|         } | ||||
|  | ||||
|         if(hasLength) | ||||
|             return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number, str.str()); | ||||
|         m_files.pop(); | ||||
|     } | ||||
|  | ||||
|     return ParserLine(); | ||||
| } | ||||
|  | ||||
| bool ParserFilesystemStream::IncludeFile(const std::string& filename) | ||||
| { | ||||
|     if (m_files.empty()) | ||||
|         return false; | ||||
|      | ||||
|     auto newFilePath = fs::path(m_files.top().m_file_path); | ||||
|     newFilePath.remove_filename().concat(filename); | ||||
|     newFilePath = absolute(newFilePath); | ||||
|  | ||||
|     FileInfo fileInfo(newFilePath.string()); | ||||
|  | ||||
|     if (!fileInfo.m_stream.is_open()) | ||||
|         return false; | ||||
|  | ||||
|     m_files.emplace(std::move(fileInfo)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void ParserFilesystemStream::PopCurrentFile() | ||||
| { | ||||
|     if(!m_files.empty()) | ||||
|         m_files.pop(); | ||||
| } | ||||
|  | ||||
| bool ParserFilesystemStream::Eof() const | ||||
| { | ||||
|     return m_files.empty() | ||||
|         || m_files.top().m_stream.eof(); | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/Parser/Parsing/Impl/ParserFilesystemStream.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/Parser/Parsing/Impl/ParserFilesystemStream.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stack> | ||||
| #include <fstream> | ||||
|  | ||||
| #include "Parsing/IParserLineStream.h" | ||||
|  | ||||
| class ParserFilesystemStream final : public IParserLineStream | ||||
| { | ||||
|     class FileInfo | ||||
|     { | ||||
|     public: | ||||
|         std::string m_file_path; | ||||
|         std::ifstream m_stream; | ||||
|         int m_line_number; | ||||
|  | ||||
|         explicit FileInfo(std::string filePath); | ||||
|     }; | ||||
|     std::stack<FileInfo> m_files; | ||||
|  | ||||
| public: | ||||
|     explicit ParserFilesystemStream(std::string path); | ||||
|  | ||||
|     ParserLine NextLine() override; | ||||
|     bool IncludeFile(const std::string& filename) override; | ||||
|     void PopCurrentFile() override; | ||||
|     _NODISCARD bool IsOpen() const override; | ||||
|     _NODISCARD bool Eof() const override; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user