diff --git a/3rdparty/ebcl b/3rdparty/ebcl index 5497856..7b97993 160000 --- a/3rdparty/ebcl +++ b/3rdparty/ebcl @@ -1 +1 @@ -Subproject commit 5497856be2761a6f4f1848636304cff134668766 +Subproject commit 7b97993448d086a92d9875db2c2815afa249ba65 diff --git a/c-buildcfg.cc b/c-buildcfg.cc index c641fcb..96bfa58 100644 --- a/c-buildcfg.cc +++ b/c-buildcfg.cc @@ -1,4 +1,6 @@ #include "externals.hh" +#include "common.hh" +#include "c-project.hh" #include "c-buildcfg.hh" #include @@ -159,11 +161,12 @@ T_SRDParserConfig BCLInitDefinitions_( ) noexcept struct T_BCLImpl_ { + const T_String fileName{ T_String::Pooled( "build.srd" ) }; T_SRDParserConfig pConfig; T_BCLImpl_( ) noexcept; - T_BuildConfigurations load( T_String const& path ); + T_BuildConfigurations load( ); }; /*----------------------------------------------------------------------------*/ @@ -172,16 +175,16 @@ T_BCLImpl_::T_BCLImpl_( ) noexcept : pConfig{ BCLInitDefinitions_( ) } { } -T_BuildConfigurations T_BCLImpl_::load( - T_String const& path ) +T_BuildConfigurations T_BCLImpl_::load( ) { // TODO: keep errors so we get warnings/notes too T_SRDParser parser{ pConfig }; T_SRDTextReader srdReader{ parser }; - T_File input{ path , E_FileMode::READ_ONLY }; + T_File input{ Common::Project( ).pathOf( fileName ) , + E_FileMode::READ_ONLY }; T_FileInputStream fis{ input }; - srdReader.read( path , fis ); + srdReader.read( fileName , fis ); return std::move( *parser.getData< T_SharedPtr< T_BuildConfigurations > >( ) ); } @@ -195,8 +198,7 @@ T_BuildConfigurationLoader::T_BuildConfigurationLoader( ) noexcept { } -T_BuildConfigurations T_BuildConfigurationLoader::load( - T_String const& path ) +T_BuildConfigurations T_BuildConfigurationLoader::load( ) { - return p< T_BCLImpl_ >( ).load( path ); + return p< T_BCLImpl_ >( ).load( ); } diff --git a/c-buildcfg.hh b/c-buildcfg.hh index 654df85..ce41ac2 100644 --- a/c-buildcfg.hh +++ b/c-buildcfg.hh @@ -50,7 +50,7 @@ struct T_BuildConfigurationLoader : public ebcl::A_PrivateImplementation // Load build configurations from the specified path; may throw // X_StreamError or X_SRDErrors - T_BuildConfigurations load( T_String const& path ); + T_BuildConfigurations load( ); }; /*----------------------------------------------------------------------------*/ diff --git a/c-opparser.cc b/c-opparser.cc index 8ae8ee3..3fddb5f 100644 --- a/c-opparser.cc +++ b/c-opparser.cc @@ -6,7 +6,9 @@ #include "c-syncoverrides.hh" #include +#include #include +#include using namespace ebcl; using namespace opast; @@ -172,8 +174,9 @@ struct T_ParserImpl_ T_SRDParserConfig ovParserConfig; T_SRDParser ovParser; - T_String curFile; - T_AutoArray< T_String , 32 > inclStack; + T_FSPath curFile; + T_FSPath baseDir; + T_AutoArray< T_FSPath , 32 > inclStack; T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser }; T_Visitor< uint32_t , uint32_t > callGraphVisitor{ @@ -192,7 +195,8 @@ struct T_ParserImpl_ // --------------------------------------------------------------------- - void main( T_SRDList const& list ) noexcept; + void main( T_FSPath const& file , + T_SRDList const& list ) noexcept; // --------------------------------------------------------------------- @@ -221,7 +225,8 @@ struct T_ParserImpl_ // --------------------------------------------------------------------- bool parseTopLevel( T_SRDList const& list ) noexcept; - void parseTLList( T_SRDList const& funcList ) noexcept; + void parseTLList( T_SRDList const& list ) noexcept; + void parseTLListItem( T_SRDList const& funcList ) noexcept; void handleInclude( T_SRDToken const& tFileName ) noexcept; @@ -331,8 +336,12 @@ inline T_ParserImpl_::T_ParserImpl_( { } void T_ParserImpl_::main( + T_FSPath const& file , T_SRDList const& input ) noexcept { + assert( file.isAbsolute( ) ); + curFile = file.canonical( ); + baseDir = file.parent( ); parseTopLevel( input ) && checkRequiredBlocks( input ) && checkCalls( ) @@ -948,18 +957,24 @@ bool T_ParserImpl_::parseTopLevel( T_SRDList const& input ) noexcept { M_LOGSTR_( "... Generating tree" , 2 ); - for ( auto const& t : input ) { - if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) { - parseTLList( t.list( ) ); - } else { - errors.addNew( "top-level list expected" , t.location( ) ); - } - } + parseTLList( input ); return errors.empty( ); } void T_ParserImpl_::parseTLList( T_SRDList const& input ) noexcept +{ + for ( auto const& t : input ) { + if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) { + parseTLListItem( t.list( ) ); + } else { + errors.addNew( "top-level list expected" , t.location( ) ); + } + } +} + +void T_ParserImpl_::parseTLListItem( + T_SRDList const& input ) noexcept { assert( input.size( ) != 0 ); auto const& ft{ input[ 0 ] }; @@ -970,7 +985,6 @@ void T_ParserImpl_::parseTLList( auto const& fw{ ft.stringValue( ) }; if ( fw == "include" ) { - // TODO if ( input.size( ) == 1 ) { errors.addNew( "file name expected" , ft.location( ) ); @@ -999,14 +1013,78 @@ void T_ParserImpl_::handleInclude( return; } - if ( !curFile ) { - curFile = tFileName.location( ).source( ); + // Resolve included file path + T_FSPath inclPath{ tFileName.stringValue( ) }; + if ( !inclPath.isValid( ) ) { + errors.addNew( "invalid file name" , + tFileName.location( ) ); + return; + } + if ( !inclPath.isAbsolute( ) ) { + inclPath = curFile.parent( ) + inclPath; + } + inclPath = inclPath.canonical( ); + if ( !inclPath.isUnder( baseDir ) ) { + errors.addNew( "file is not in the project's directory" , + tFileName.location( ) ); + return; } - // TODO: resolve included file path - // TODO: check for recursive includes - // TODO: load file - // TODO: parse it + // Check for recursive includes + if ( inclStack.contains( inclPath ) ) { + errors.addNew( "recursive file inclusion" , + tFileName.location( ) ); + return; + } + + // Load file + const auto relPath{ inclPath.makeRelative( baseDir ) }; + T_SRDMemoryTarget srdOut; + { + T_File input{ inclPath , E_FileMode::READ_ONLY }; + try { + input.open( ); + + } catch ( X_StreamError const& e ) { + T_StringBuilder sb; + sb << "could not open '" << relPath << "': " << e.what( ); + if ( e.code( ) == E_StreamError::SYSTEM_ERROR ) { + sb << " (error code " << e.systemError( ) << ")"; + } + errors.addNew( std::move( sb ) , tFileName.location( ) ); + return; + } + + srdOut.clearComments( true ).clearFlushToken( true ); + try { + T_SRDTextReader srdReader{ srdOut }; + T_FileInputStream fis{ input }; + srdReader.read( relPath.toString( ) , fis ); + + } catch ( X_StreamError const& e ) { + T_StringBuilder sb; + sb << "could not read '" << relPath << "': " << e.what( ); + if ( e.code( ) == E_StreamError::SYSTEM_ERROR ) { + sb << " (error code " << e.systemError( ) << ")"; + } + errors.addNew( std::move( sb ) , tFileName.location( ) ); + return; + + } catch ( X_SRDErrors const& e ) { + const auto ne{ e.errors.size( ) }; + for ( auto i = 0u ; i < ne ; i ++ ) { + errors.add( e.errors[ i ] ); + } + return; + } + } + + // Parse included file + inclStack.add( curFile ); + curFile = inclPath; + parseTLList( srdOut.list( ) ); + curFile = inclStack.last( ); + inclStack.removeLast( ); } /*----------------------------------------------------------------------------*/ @@ -2224,6 +2302,6 @@ bool T_OpsParser::parse( { errors_.clear( ); output_ = NewOwned< T_OpsParserOutput >( ); - p< T_ParserImpl_ >( ).main( input ); + p< T_ParserImpl_ >( ).main( filePath , input ); return errors_.empty( ); } diff --git a/common.cc b/common.cc index 43ea18b..264fe9f 100644 --- a/common.cc +++ b/common.cc @@ -24,21 +24,27 @@ struct CommonData_ {} }; +uint32_t Initialised_{ 0 }; std::aligned_storage_t< sizeof( CommonData_ ) , alignof( CommonData_ ) > Instance_; } // namespace /*----------------------------------------------------------------------------*/ -void Common::Init( +Common::Common( T_FSPath const& path ) noexcept { - new ((char*)&Instance_) CommonData_( path ); + if ( !Initialised_ ++ ) { + new ((char*)&Instance_) CommonData_( path ); + } } -void Common::Shutdown( ) noexcept +Common::~Common( ) noexcept { - ((CommonData_*)(char*)&Instance_)->~CommonData_( ); + assert( Initialised_ ); + if ( !-- Initialised_ ) { + ((CommonData_*)(char*)&Instance_)->~CommonData_( ); + } } /*----------------------------------------------------------------------------*/ diff --git a/common.hh b/common.hh index e845373..2cc2f16 100644 --- a/common.hh +++ b/common.hh @@ -13,8 +13,8 @@ class T_UndoManager; struct Common { - static void Init( T_FSPath const& path = {} ) noexcept; - static void Shutdown( ) noexcept; + explicit Common( T_FSPath const& path = {} ) noexcept; + ~Common( ) noexcept; static T_Project& Project( ) noexcept; static T_FilesWatcher& Watcher( ) noexcept; diff --git a/m-builder.cc b/m-builder.cc index 1bfa0a2..1d9107d 100644 --- a/m-builder.cc +++ b/m-builder.cc @@ -1,5 +1,6 @@ #include "externals.hh" +#include "common.hh" #include "c-opast.hh" #include "c-ops.hh" #include "c-opcomp.hh" @@ -34,7 +35,13 @@ void PrintStreamError( T_FSPath const& name , X_StreamError const& error ) noexcept { - const T_FSPath rp{ name.makeRelative( Filesystem::Cwd( ) ) }; + const T_FSPath rp{ ([&]() { + if ( error.path( ) ) { + return *( error.path( ) ); + } + return name; + }()).makeRelative( Filesystem::Cwd( ) ) }; + T_StringBuilder sb; sb << prefix << " '" << rp << "': " << error.what( ); if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) { @@ -138,6 +145,8 @@ int main( int argc , char** argv ) } } + + // Initialise common layer const T_FSPath srcPath{ ([&](){ const T_FSPath raw{ oSrcPath }; if ( !raw.isAbsolute( ) ) { @@ -149,6 +158,7 @@ int main( int argc , char** argv ) fprintf( stderr , "Invalid source path\n" ); return EXIT_FAILURE; } + Common common{ srcPath }; // Logger setup const uint32_t logLevel{ oLogLevel ? *oLogLevel : 0 }; @@ -163,11 +173,11 @@ int main( int argc , char** argv ) // Build configurations M_LOGSTR( "Loading build configurations" , 1 ); - const auto bcfgFile{ FilePath( srcPath , "build.srd" ) }; const T_BuildConfiguration cfg{ [&]() { try { - T_BuildConfigurationLoader bcfgLoader; - T_BuildConfigurations cfs{ bcfgLoader.load( bcfgFile.toString( ) ) }; + T_BuildConfigurations cfs{ + T_BuildConfigurationLoader{ }.load( ) + }; if ( oBuildCfg && !cfs.contains( oBuildCfg ) ) { fprintf( stderr , "===== BUILD CONFIGURATIONS\n" ); fprintf( stderr , "Build configuration not found\n" ); @@ -183,7 +193,9 @@ int main( int argc , char** argv ) } catch ( X_StreamError const& e ) { fprintf( stderr , "===== BUILD CONFIGURATIONS\n" ); - PrintStreamError( "Could not open" , bcfgFile , e ); + PrintStreamError( "Could not open" , + Common::Project( ).pathOf( "build.srd" ) , + e ); exit( EXIT_FAILURE ); } catch ( X_SRDErrors const& e ) { @@ -210,24 +222,25 @@ int main( int argc , char** argv ) // Open and load script M_LOGSTR( "Loading script" , 1 ); - const auto inputName{ FilePath( srcPath , "demo.srd" ) }; + char const* const scriptName{ "demo.srd" }; + const auto scriptPath{ Common::Project( ).pathOf( scriptName ) }; T_SRDMemoryTarget srdOut; { - T_File input( inputName.toString( ) , E_FileMode::READ_ONLY ); + T_File input( scriptPath , E_FileMode::READ_ONLY ); try { input.open( ); } catch ( X_StreamError const& e ) { - PrintStreamError( "Could not open" , inputName , e ); + PrintStreamError( "Could not open" , scriptPath , e ); return 1; } srdOut.clearComments( true ).clearFlushToken( true ); try { T_SRDTextReader srdReader{ srdOut }; T_FileInputStream fis{ input }; - srdReader.read( inputName.toString( ) , fis ); + srdReader.read( scriptName , fis ); } catch ( X_StreamError const& e ) { fprintf( stderr , "===== SCRIPT\n" ); - PrintStreamError( "Could not open" , inputName , e ); + PrintStreamError( "Could not open" , scriptPath , e ); exit( EXIT_FAILURE ); } catch ( X_SRDErrors const& e ) { WriteSRDErrors( "SCRIPT" , e.errors ); @@ -239,7 +252,7 @@ int main( int argc , char** argv ) M_LOGSTR( "Parsing script" , 1 ); T_OpsParser parser; parser.setLogger( logger ); - if ( !parser.parse( inputName , srdOut.list( ) ) ) { + if ( !parser.parse( scriptPath , srdOut.list( ) ) ) { WriteSRDErrors( "SCRIPT" , parser.errors( ) ); exit( EXIT_FAILURE ); } diff --git a/m-tool.cc b/m-tool.cc index 4c70a89..7ed1452 100644 --- a/m-tool.cc +++ b/m-tool.cc @@ -33,6 +33,7 @@ struct T_Main uint32_t stopResize = 0; ImVec2 prevSize; + UI ui; T_Optional< T_Demo > demo; T_Optional< T_SyncView > sequencer; @@ -46,8 +47,8 @@ struct T_Main /*----------------------------------------------------------------------------*/ T_Main::T_Main( T_FSPath const& path ) + : ui{ path } { - UI::Init( path ); prevSize = ImVec2( -1 , -1 ); } @@ -103,7 +104,6 @@ void T_Main::mainLoop( ) T_Main::~T_Main( ) { demo.clear( ); - UI::Shutdown( ); } /*----------------------------------------------------------------------------*/ diff --git a/ui.cc b/ui.cc index 1cf692f..b04723e 100644 --- a/ui.cc +++ b/ui.cc @@ -23,6 +23,7 @@ const char CopyShader_[] = { struct UIData_ { + Common common; T_UIApp window; T_Profiler profiler; T_TextureManager textures; @@ -35,24 +36,32 @@ struct UIData_ }; T_OutputDebugger odbg; T_UISync sync; + + UIData_( T_FSPath const& path ) noexcept + : common{ path } + {} }; +uint32_t Initialised_{ 0 }; std::aligned_storage_t< sizeof( UIData_ ) , alignof( UIData_ ) > Instance_; } // namespace /*----------------------------------------------------------------------------*/ -void UI::Init( T_FSPath const& path ) noexcept +UI::UI( T_FSPath const& path ) noexcept { - Common::Init( path ); - new ((char*)&Instance_) UIData_( ); + if ( !Initialised_ ++ ) { + new ((char*)&Instance_) UIData_( path ); + } } -void UI::Shutdown( ) noexcept +UI::~UI( ) noexcept { - ((UIData_*)(char*)&Instance_)->~UIData_( ); - Common::Shutdown( ); + assert( Initialised_ ); + if ( !-- Initialised_ ) { + ((UIData_*)(char*)&Instance_)->~UIData_( ); + } } /*----------------------------------------------------------------------------*/ diff --git a/ui.hh b/ui.hh index d5384c6..0b52190 100644 --- a/ui.hh +++ b/ui.hh @@ -15,8 +15,8 @@ struct T_UISync; struct UI { public: - static void Init( T_FSPath const& path = {} ) noexcept; - static void Shutdown( ) noexcept; + explicit UI( T_FSPath const& path = {} ) noexcept; + ~UI( ) noexcept; static T_UIApp& Main( ) noexcept; static T_Profiler& Profiler( ) noexcept;