#include "externals.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 >; 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_( FILE* const file , T_ShaderInput& input ) : 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; } addAccumulated( ); auto& ck( input.chunks ); ck.addNew( E_ShaderInputChunk::INCLUDE , tokens[ 1 ].c_str( ) , 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( E_ShaderInputChunk::CODE , accumulator.c_str( ) , accumLines ); accumulator = {}; accumLines = 0; } } } // namespace /*= T_ShaderInput ============================================================*/ bool T_ShaderInput::load( T_String const& path ) { type = E_ShaderInput::CHUNK; chunks.clear( ); errors.clear( ); FILE* const file{ fopen( (char const*) path.toOSString( ).data( ) , "r" ) }; if ( !file ) { return false; } T_InputReader_ reader( file , *this ); reader.read( ); return true; }