See More

#include "parser/impl/pr_parser_impl.hpp" #include "parser/api/pr_include_file.hpp" #include "parser/impl/pr_parser_context.hpp" #include "fs/api/fs_file.hpp" #include #include #include #include #include //------------------------------------------------------------------------------ namespace parser { //------------------------------------------------------------------------------ Parser::IncludeFiles ParserImpl::parseFile( const fs::File & _file ) const { IncludeFiles result; ParserContext context; while( !_file.eof() ) { const std::string line = _file.getLine(); context.setCurrentLine( line ); auto includeFileOpt = parseLine( context ); if( includeFileOpt ) { result.push_back( *includeFileOpt ); } context.increaseLineNumber(); } return result; } //------------------------------------------------------------------------------ ParserImpl::IncludeFileOpt ParserImpl::parseLine( ParserContext & _context ) { constexpr size_t minimumSize = std::string_view{ "/*" }.size() - 1; IncludeFileOpt resutl; const std::string & line = _context.getCurrentLine(); const size_t size = line.size(); if( size <= minimumSize ) { return resutl; } const size_t startPos = getStartPos( _context ); for( size_t i = startPos; i < size; ++i ) { const char currentChar = line[i]; switch( currentChar ) { case '/': { i = findComentEnd( _context, i ); break; } case '"': { i = findEndOfString( _context, i ); break; } case 'R': { i = findEndOfRawString( _context, i ); break; } case '#': { if( auto indexOpt = findInclude( line, i ); indexOpt ) { return parseInclude( _context, *indexOpt ); } // if found # and it's not include it doesn't need analyze return resutl; break; } default: break; } } return resutl; } //------------------------------------------------------------------------------ std::size_t ParserImpl::getStartPos( ParserContext & _context ) noexcept { const std::size_t index = 0; if( _context.isEnableMultilineComment() ) { return findComentEnd( _context, index ); } if( _context.isEnableMultilineString() ) { return findEndOfString( _context, index ); } if( _context.isEnableRawString() ) { return findEndOfRawString( _context, index ); } return 0; } //------------------------------------------------------------------------------ std::size_t ParserImpl::getStartPosInInclude( const ParserContext & _conext, std::size_t _index, char _char ) { const std::string & line = _conext.getCurrentLine(); const std::size_t count = line.size(); for( std::size_t i = _index; i < count; ++i ) { const char currentChar = line[i]; if( currentChar == _char ) { return i; } if( currentChar == '/' ) { const std::size_t nextPos = i + 1; if( nextPos >= count ) { continue; } const char nextChar = line[nextPos]; if( nextChar == '/' ) { return std::string::npos; } if( nextChar == '*' ) { const auto endPos = line.find( "*/", nextPos ); if( endPos == std::string::npos ) { return std::string::npos; } i = endPos + 1; } } } return std::string::npos; } //------------------------------------------------------------------------------ std::size_t ParserImpl::findComentEnd( ParserContext & _context, std::size_t _index ) noexcept { const size_t nextIndex = _context.isEnableMultilineComment() ? 0 : _index + 1; const std::string & line = _context.getCurrentLine(); const size_t size = line.size(); if( nextIndex >= size ) { return size; } const char nextChar = line[nextIndex]; if( !_context.isEnableMultilineComment() ) { if( nextChar == '/' ) { return size; } if( nextChar != '*' ) { return _index; } assert( nextChar == '*' ); } const auto pos = line.find( "*/", nextIndex ); _context.setMultilineComment( pos == std::string::npos ); return _context.isEnableMultilineComment() ? size : pos + 1; } //------------------------------------------------------------------------------ std::size_t ParserImpl::findEndOfString( ParserContext & _context, std::size_t _index ) noexcept { const std::string & line = _context.getCurrentLine(); const size_t size = line.size(); for( size_t i = _index + 1; i < size; ++i ) { const char currentChar = line[i]; switch( currentChar ) { case '\"': { _context.setMultilineString( false ); return i; } case '\\': { if( i == size - 1 ) { _context.setMultilineString( true ); return size; } const size_t nextPost = i + 1; assert( nextPost < size ); if( nextPost < size && line[nextPost] == '\"' ) { i = nextPost; } break; } default: break; } } return size; } //------------------------------------------------------------------------------ std::size_t ParserImpl::findEndOfRawString( ParserContext & _context, std::size_t _index ) noexcept { const std::string & line = _context.getCurrentLine(); if( !_context.isEnableRawString() ) { const auto pos = line.find( "\"(", _index + 1 ); if( pos == std::string::npos ) { return _index; } _index += 3; _context.seEnableRawString( true ); } const auto pos = line.find( ")\"", _index ); if( pos != std::string::npos ) { _context.seEnableRawString( false ); return pos + 2; } return line.size(); } //------------------------------------------------------------------------------ std::optional< std::size_t > ParserImpl::findInclude( std::string_view _line, std::size_t _index ) { static const std::string includePhase{ "include" }; static const size_t includePhaseSize = includePhase.size(); const size_t size = _line.size(); bool isStartedCheckPhase = false; size_t indexPhase = 0; for( size_t i = _index + 1; i < size; ++i ) { const char currentChar = _line[i]; if( currentChar == ' ' || currentChar == '\t' ) { if( isStartedCheckPhase ) { return std::nullopt; } } else { isStartedCheckPhase = true; const char includeChar = includePhase[indexPhase]; if( currentChar == includeChar ) { ++indexPhase; if( indexPhase >= includePhaseSize ) { return i; } } else { return std::nullopt; } } } return std::nullopt; } //------------------------------------------------------------------------------ ParserImpl::IncludeFileOpt ParserImpl::parseInclude( const ParserContext & _context, std::size_t _index ) { const std::string & line = _context.getCurrentLine(); const size_t startPosSystem = getStartPosInInclude( _context, _index, '<' ); const size_t startPosUser = getStartPosInInclude( _context, _index, '"' ); if( startPosSystem == std::string::npos && startPosUser == std::string::npos ) { // it's strange that after #include don't exist < or " return std::nullopt; } const size_t startPos = std::min( startPosSystem, startPosUser ); const bool isSystem = startPosSystem != std::string::npos && startPosSystem == startPos; const size_t startPosName = ( isSystem ? startPosSystem : startPosUser ) + 1; const char endChar = isSystem ? '>' : '"'; size_t endPosName = line.find( endChar, startPosName ); if( endPosName == std::string::npos ) { // it's strange, include isn't closed return std::nullopt; } const std::string name = line.substr( startPosName, endPosName - startPosName ); IncludeFileLocation location{ _context.getLineNumber(), startPosName + 1, endPosName + 1 }; return IncludeFile{ location, name, isSystem }; } //------------------------------------------------------------------------------ }