From d77943f953a7c7bdc46c7ffa6815670b82e4f3b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sat, 2 Dec 2017 18:38:34 +0100
Subject: [PATCH] Demo building - Build configurations

---
 Makefile      |   1 +
 c-buildcfg.cc | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++
 c-buildcfg.hh |  59 ++++++++++++++++
 c-opcomp.hh   |   4 +-
 m-builder.cc  |  71 +++++++++++++++++--
 5 files changed, 319 insertions(+), 7 deletions(-)
 create mode 100644 c-buildcfg.cc
 create mode 100644 c-buildcfg.hh

diff --git a/Makefile b/Makefile
index e5c417e..ae437f8 100644
--- a/Makefile
+++ b/Makefile
@@ -25,6 +25,7 @@ IMGUI = imgui.cpp imgui_draw.cpp
 
 COMMON = \
 	 common.cc \
+	 c-buildcfg.cc \
 	 c-camera.cc \
 	 c-filewatcher.cc \
 	 c-project.cc \
diff --git a/c-buildcfg.cc b/c-buildcfg.cc
new file mode 100644
index 0000000..97b2a53
--- /dev/null
+++ b/c-buildcfg.cc
@@ -0,0 +1,191 @@
+#include "externals.hh"
+#include "c-buildcfg.hh"
+
+#include <ebcl/Files.hh>
+#include <ebcl/SRDDefinitions.hh>
+#include <ebcl/SRDParserConfig.hh>
+#include <ebcl/SRDParser.hh>
+#include <ebcl/SRDText.hh>
+
+using namespace ebcl;
+
+namespace {
+
+/*= SRD PARSER FOR BUILD CONFIGURATIONS ======================================*/
+
+using SP_Table_ = T_SharedPtr< T_BuildConfigurations >;
+
+bool BCLStartConfig_( T_SRDParserData const& data )
+{
+	*( data.targetData ) = T_BuildConfiguration{ };
+	return true;
+}
+
+bool BCLEndConfig_( T_SRDParserData const& data )
+{
+	auto const& in{ *data.input };
+	auto& table{ *( data.targetData->value< SP_Table_ >( ) ) };
+	auto& bCfg{ data.currentData->value< T_BuildConfiguration >( ) };
+	if ( !table.add( in[ 1 ].stringValue( ) , std::move( bCfg ) ) ) {
+		data.errors.add( "duplicate configuration" , in[ 1 ] );
+	}
+	return true;
+}
+
+/*----------------------------------------------------------------------------*/
+
+bool BCLResolutions_( T_SRDParserData const& data )
+{
+	auto const& in{ *data.input };
+	auto& bCfg{ data.currentData->value< T_BuildConfiguration >( ) };
+	const uint32_t nin{ in.size( ) };
+	if ( nin == 2 && in[ 1 ].type( ) == E_SRDTokenType::WORD ) {
+		bCfg.resolutionChooser = true;
+		bCfg.resolutions.clear( );
+		return true;
+	}
+
+	for ( auto i = 1u ; i < nin ; i ++ ) {
+		auto const& l{ in[ i ].list( ) };
+		const auto w{ l[ 0 ].longValue( ) };
+		const auto h{ l[ 1 ].longValue( ) };
+		bool fail{ false };
+
+		if ( w <= 0 || w >= 16384 ) {
+			fail = true;
+			data.errors.add( "invalid width" , l[ 0 ] );
+		}
+		if ( h <= 0 || h >= 16384 ) {
+			fail = true;
+			data.errors.add( "invalid height" , l[ 1 ] );
+		}
+		if ( fail ) {
+			continue;
+		}
+
+		const auto res{ std::make_pair< uint32_t , uint32_t >( w , h ) };
+		if ( bCfg.resolutions.contains( res ) ) {
+			data.errors.add( InPlace( ) ,
+					"duplicate resolution" ,
+					in[ i ].location( ) ,
+					E_SRDErrorType::WARNING );
+		} else {
+			bCfg.resolutions.add( res );
+		}
+	}
+
+	return true;
+}
+
+/*----------------------------------------------------------------------------*/
+
+T_SRDParserConfig BCLInitDefinitions_( ) noexcept
+{
+	using namespace ebcl::SRD;
+	T_SRDParserDefs defs{ "default" };
+	defs.enumeration( "on-off" ) << "off" << "on";
+	defs << OnStart( []( T_SRDParserData const& data ) -> bool {
+		*( data.currentData ) = NewShared< T_BuildConfigurations >( );
+		return true;
+	} );
+
+	defs.context( "default" )
+		<< ( Rule( ) << "cfg" << Text( ) << EnterContext( "config" )
+				<< OnEnter( BCLStartConfig_ )
+				<< OnExit( BCLEndConfig_ ) )
+	;
+
+	defs.context( "config" )
+		<< ( Rule( ) << "resolutions"
+			<< ( Alt( ) << "chooser"
+				<< ( AtLeast( 1 ) << ( List( ) << Int32( ) << Int32( ) ) ) )
+			<< BCLResolutions_ )
+		<< ( Rule( ) << "optimizer" << "off" )
+		<< ( Rule( ) << "optimizer" << "on" << EnterContext( "optim" ) )
+	;
+
+	defs.context( "optim" )
+		<< ( Rule( ) << "constant-folding" << "off"
+			<< []( T_SRDParserData const& d ) -> bool {
+				auto& bCfg{ d.currentData->value< T_BuildConfiguration >( ) };
+				bCfg.constantFolding = false;
+				return true;
+			} )
+		<< ( Rule( ) << "constant-folding" << "on" << EnterContext( "opt-cf" )
+			<< []( T_SRDParserData const& d ) -> bool {
+				auto& bCfg{ d.currentData->value< T_BuildConfiguration >( ) };
+				bCfg.constantFolding = true;
+				return true;
+			} )
+		<< ( Rule( ) << "dead-code-elimination" << Enum( "on-off" )
+			<< []( T_SRDParserData const& d ) -> bool {
+				auto& bCfg{ d.currentData->value< T_BuildConfiguration >( ) };
+				bCfg.deadCodeElimination = true;
+				return true;
+			} )
+	;
+
+	defs.context( "opt-cf" )
+		<< ( Rule( ) << "fixed-resolution" << Enum( "on-off" )
+			<< []( T_SRDParserData const& d ) -> bool {
+				auto& bCfg{ d.currentData->value< T_BuildConfiguration >( ) };
+				bCfg.cfFixedSize = true;
+				return true;
+			} )
+		<< ( Rule( ) << "inputs" << Enum( "on-off" )
+			<< []( T_SRDParserData const& d ) -> bool {
+				auto& bCfg{ d.currentData->value< T_BuildConfiguration >( ) };
+				bCfg.cfInputs = true;
+				return true;
+			} )
+	;
+
+	return T_SRDParserConfig{ defs };
+}
+
+
+/*= BUILD CONFIGURATIONS LOADER ==============================================*/
+
+struct T_BCLImpl_
+{
+	T_SRDParserConfig pConfig;
+
+	T_BCLImpl_( ) noexcept;
+
+	T_BuildConfigurations load( T_String const& path );
+};
+
+/*----------------------------------------------------------------------------*/
+
+T_BCLImpl_::T_BCLImpl_( ) noexcept
+	: pConfig{ BCLInitDefinitions_( ) }
+{ }
+
+T_BuildConfigurations T_BCLImpl_::load(
+		T_String const& path )
+{
+	// 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_FileInputStream fis{ input };
+	srdReader.read( path , fis );
+
+	return std::move( *parser.getData< T_SharedPtr< T_BuildConfigurations > >( ) );
+}
+
+} // namespace <anon>
+
+/*----------------------------------------------------------------------------*/
+
+T_BuildConfigurationLoader::T_BuildConfigurationLoader( ) noexcept
+	: A_PrivateImplementation( new T_BCLImpl_( ) )
+{ }
+
+
+T_BuildConfigurations T_BuildConfigurationLoader::load(
+		T_String const& path )
+{
+	return p< T_BCLImpl_ >( ).load( path );
+}
diff --git a/c-buildcfg.hh b/c-buildcfg.hh
new file mode 100644
index 0000000..aa908e4
--- /dev/null
+++ b/c-buildcfg.hh
@@ -0,0 +1,59 @@
+#pragma once
+#ifndef REAL_BUILD
+# include "externals.hh"
+#endif
+
+
+/*= BUILD CONFIGURATIONS =====================================================*/
+
+// Build configuration
+struct T_BuildConfiguration
+{
+	// Targets -------------------------------------------------------------
+
+		// Resolutions
+		bool resolutionChooser{ true };
+		T_Array< std::pair< uint32_t , uint32_t > > resolutions;
+
+		// Enable optimizer?
+		bool optimize{ false };
+
+	// Constant folding ----------------------------------------------------
+
+		// Main toggle
+		bool constantFolding{ false };
+		// Optimize $width and $height away if the size is fixed
+		bool cfFixedSize{ false };
+		// Get rid of (get-input) for constant or undefined curves
+		bool cfInputs{ false };
+
+	// Dead code elimination -----------------------------------------------
+
+		bool deadCodeElimination{ false };
+};
+
+// Table of avaiable build configurations
+using T_BuildConfigurations = T_KeyValueTable< T_String , T_BuildConfiguration >;
+
+// Build configurations loader
+struct T_BuildConfigurationLoader : public ebcl::A_PrivateImplementation
+{
+	T_BuildConfigurationLoader( ) noexcept;
+
+	// Load build configurations from the specified path; may throw
+	// X_StreamError or X_SRDErrors
+	T_BuildConfigurations load( T_String const& path );
+};
+
+/*----------------------------------------------------------------------------*/
+
+// Logger
+using F_OPGenLog = std::function< T_StringBuilder( ) >;
+using F_OPLogger = std::function< void( F_OPGenLog , uint32_t ) >;
+
+// Runtime configuration (logger, etc.)
+struct T_BuildRuntimeConfiguration
+{
+	F_OPLogger logger{ [](auto,auto){} };
+};
+
diff --git a/c-opcomp.hh b/c-opcomp.hh
index 69ae2b6..bf36a81 100644
--- a/c-opcomp.hh
+++ b/c-opcomp.hh
@@ -1,4 +1,5 @@
 #pragma once
+#include "c-buildcfg.hh"
 #include "c-filewatcher.hh"
 #include "c-opast.hh"
 #include "c-project.hh"
@@ -7,9 +8,6 @@
 
 /*= PARSER ===================================================================*/
 
-using F_OPGenLog = std::function< T_StringBuilder( ) >;
-using F_OPLogger = std::function< void( F_OPGenLog , uint32_t ) >;
-
 // Parser output. Consists in a root node as well as other details (table of
 // constants, data types, etc...)
 struct T_OpsParserOutput
diff --git a/m-builder.cc b/m-builder.cc
index bdddbfb..aef625b 100644
--- a/m-builder.cc
+++ b/m-builder.cc
@@ -44,7 +44,18 @@ void WriteSRDError(
 		T_StringBuilder& sb ,
 		T_SRDError const& error )
 {
-	sb << error.location( ) << " - " << error.error( ) << "\n";
+	switch ( error.type( ) ) {
+	    case E_SRDErrorType::ERROR:
+		sb << "[ERROR";
+		break;
+	    case E_SRDErrorType::WARNING:
+		sb << " [WARN";
+		break;
+	    case E_SRDErrorType::NOTE:
+		sb << " [NOTE";
+		break;
+	}
+	sb << '(' << error.location( ) << ")] " << error.error( ) << '\n';
 }
 
 
@@ -57,6 +68,7 @@ int main( int argc , char** argv )
 	// Parse command line
 	T_Optional< uint32_t > oLogLevel;
 	T_String oSrcPath;
+	T_String oBuildCfg;
 	int c;
 	while ( ( c = getopt_long( argc , argv , "L:s:c:" , CmdLineOpts_ , nullptr ) ) != -1 ) {
 		switch ( c ) {
@@ -68,13 +80,17 @@ int main( int argc , char** argv )
 		    case 's':
 			if ( oSrcPath ) {
 				fprintf( stderr , "Duplicate source path\n" );
-				exit( EXIT_FAILURE );
+				return EXIT_FAILURE;
 			}
 			oSrcPath = optarg;
 			break;
 
 		    case 'c':
-			fprintf( stderr , "-c/--config option ignored for now\n" );
+			if ( oSrcPath ) {
+				fprintf( stderr , "Duplicate build configuration\n" );
+				return EXIT_FAILURE;
+			}
+			oBuildCfg = optarg;
 			break;
 
 		}
@@ -88,9 +104,56 @@ int main( int argc , char** argv )
 		}
 		auto sb{ func( ) };
 		sb << '\0';
-		printf( "LOG{%d} %s\n" , level , sb.data( ) );
+		printf( "(%d) %s\n" , level , sb.data( ) );
 	} };
 
+	// Build configurations
+	const T_String bcfgFile( [&]() {
+		T_StringBuilder sb;
+		if ( oSrcPath ) {
+			sb << oSrcPath;
+			if ( !oSrcPath.endsWith( "/" ) ) {
+				sb << '/';
+			}
+		}
+		sb << "build.srd";
+		return T_String{ std::move( sb ) };
+	}( ) );
+	const T_BuildConfiguration cfg{ [&]() {
+		try {
+			T_BuildConfigurationLoader bcfgLoader;
+			T_BuildConfigurations cfs{ bcfgLoader.load( bcfgFile ) };
+			if ( oBuildCfg && !cfs.contains( oBuildCfg ) ) {
+				fprintf( stderr , "===== BUILD CONFIGURATIONS\n" );
+				fprintf( stderr , "Build configuration not found\n" );
+				exit( EXIT_FAILURE );
+			}
+			if ( oBuildCfg ) {
+				return *cfs.get( oBuildCfg );
+			}
+			if ( cfs.size( ) ) {
+				return cfs[ 0 ];
+			}
+			return T_BuildConfiguration{ };
+
+		} catch ( X_StreamError const& e ) {
+			fprintf( stderr , "===== BUILD CONFIGURATIONS\n" );
+			PrintStreamError( "Could not open" , bcfgFile , e );
+			exit( EXIT_FAILURE );
+
+		} catch ( X_SRDErrors const& e ) {
+			T_StringBuilder sb;
+			sb << "===== BUILD CONFIGURATIONS\n";
+			const auto nErrors( e.errors.size( ) );
+			for ( auto i = 0u ; i < nErrors ; i ++ ) {
+				WriteSRDError( sb , e.errors[ i ] );
+			}
+			sb << '\0';
+			fprintf( stderr , "%s" , sb.data( ) );
+			exit( EXIT_FAILURE );
+		}
+	} () };
+
 	// Open file
 	const T_String inputName( [&]() {
 		T_StringBuilder sb;