#include "externals.hh" #include "common.hh" #include "c-project.hh" #include "c-shaders.hh" namespace { const std::map< std::string , E_ShaderInput > InputTypes_( ([] { std::map< std::string , E_ShaderInput > t; t.emplace( "chunk" , E_ShaderInput::CHUNK ); t.emplace( "library" , E_ShaderInput::LIBRARY ); t.emplace( "lib" , E_ShaderInput::LIBRARY ); t.emplace( "vertex" , E_ShaderInput::VERTEX ); t.emplace( "fragment" , E_ShaderInput::FRAGMENT ); t.emplace( "compute" , E_ShaderInput::COMPUTE ); t.emplace( "geo" , E_ShaderInput::GEOMETRY ); t.emplace( "geometry" , E_ShaderInput::GEOMETRY ); return t; })()); const std::regex PreprocDirective_( "^\\s*//!\\s*([a-z]+(\\s+([^\\s]+))*)\\s*$" ); } // namespace /*============================================================================*/ namespace { // Input reader state and functions, used when loading a source file struct T_InputReader_ { using T_Tokens_ = std::vector< std::string >; const T_FSPath path; FILE* const file; T_ShaderInput& input; uint32_t line{ 0 }; char* buffer{ nullptr }; ssize_t readCount; std::string accumulator{ }; uint32_t accumLines{ 0 }; T_InputReader_( T_FSPath const& path , FILE* const file , T_ShaderInput& input ) : path( path ) , file( file ) , input( input ) { } ~T_InputReader_( ); void read( ); void handleDirective( T_Tokens_ const& tokens ); void error( T_String const& err ); void nl( ); void addAccumulated( ); }; /*----------------------------------------------------------------------------*/ T_InputReader_::~T_InputReader_( ) { if ( buffer ) { free( buffer ); } fclose( file ); } void T_InputReader_::read( ) { size_t bsz( 0 ); while ( ( readCount = getline( &buffer , &bsz , file ) ) != -1 ) { line ++; std::cmatch match; if ( std::regex_match( buffer , match , PreprocDirective_ ) ) { const T_Tokens_ tokens( ([](std::string const& a) { using stri = std::istream_iterator< std::string >; std::istringstream iss( a ); return T_Tokens_{ stri( iss ) , stri( ) }; })( match[ 1 ].str( ) ) ); assert( tokens.size( ) >= 1 ); handleDirective( tokens ); } else { accumulator += buffer; accumLines ++; } } addAccumulated( ); } /*----------------------------------------------------------------------------*/ void T_InputReader_::handleDirective( T_Tokens_ const& tokens ) { auto const& directive( tokens[ 0 ] ); if ( directive == "include" ) { if ( tokens.size( ) != 2 ) { nl( ); error( "invalid arguments" ); return; } T_FSPath inclPath{ tokens[ 1 ].c_str( ) }; if ( !inclPath.isValid( ) ) { nl( ); error( "invalid path" ); return; } if ( inclPath.isAbsolute( ) ) { inclPath = inclPath.makeRelative( inclPath.root( ) ); inclPath = ( Common::Project( ).basePath( ) + inclPath ).canonical( ); } else { inclPath = ( path.parent( ) + inclPath ).canonical( ); } if ( !inclPath.isUnder( Common::Project( ).basePath( ) ) ) { nl( ); error( "path is outside of project" ); return; } addAccumulated( ); auto& ck( input.chunks ); ck.addNew( inclPath , 1 ); } else if ( directive == "type" ) { nl( ); if ( tokens.size( ) != 2 ) { error( "invalid arguments" ); return; } auto pos( InputTypes_.find( tokens[ 1 ] ) ); if ( pos == InputTypes_.end( ) ) { error( "unknown type" ); } else { input.type = pos->second; } } else { nl( ); error( "unknown directive" ); } } /*----------------------------------------------------------------------------*/ void T_InputReader_::error( T_String const& err ) { input.errors.addNew( line , err ); } void T_InputReader_::nl( ) { accumLines ++; accumulator += '\n'; } void T_InputReader_::addAccumulated( ) { if ( accumLines ) { auto& ck( input.chunks ); ck.addNew( accumulator.c_str( ) , accumLines ); accumulator = {}; accumLines = 0; } } } // namespace /*= T_ShaderInput ============================================================*/ bool T_ShaderInput::load( T_FSPath const& path ) { type = E_ShaderInput::CHUNK; chunks.clear( ); errors.clear( ); const auto osPath{ path.toString( ).toOSString( ) }; FILE* const file{ fopen( (char const*) osPath.data( ) , "r" ) }; if ( !file ) { return false; } T_InputReader_ reader{ path , file , *this }; reader.read( ); return true; }