forked from Alepha/Alepha
Change variable substitution to work on streams.
This commit is contained in:
@ -2,6 +2,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
|
|
||||||
#include "string_algorithms.h"
|
#include "string_algorithms.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
@ -17,11 +18,136 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
|||||||
const bool debugExpansion= false or C::debug;
|
const bool debugExpansion= false or C::debug;
|
||||||
const bool debugCommas= false or C::debug;
|
const bool debugCommas= false or C::debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VariableExpansionStreambuf
|
||||||
|
: public std::streambuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::streambuf *underlying= nullptr;
|
||||||
|
VarMap substitutions;
|
||||||
|
std::stringbuf varName;
|
||||||
|
char sigil;
|
||||||
|
enum { Symbol, Normal } mode= Normal;
|
||||||
|
|
||||||
|
void
|
||||||
|
writeChar( const char ch )
|
||||||
|
{
|
||||||
|
std::ostream current{ underlying };
|
||||||
|
|
||||||
|
if( mode == Normal and ch == sigil )
|
||||||
|
{
|
||||||
|
mode= Symbol;
|
||||||
|
varName.str( "" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( mode == Symbol and ch == sigil )
|
||||||
|
{
|
||||||
|
mode= Normal;
|
||||||
|
const std::string name( varName.view() );
|
||||||
|
if( not name.empty() )
|
||||||
|
{
|
||||||
|
if( not substitutions.contains( name ) )
|
||||||
|
{
|
||||||
|
throw std::runtime_error{ "No such variable: `" + name +"`" };
|
||||||
|
}
|
||||||
|
if( C::debugExpansion ) error() << "Expanding variable with name `" << name << "`" << std::endl;
|
||||||
|
current << substitutions.at( name )();
|
||||||
|
}
|
||||||
|
else current << sigil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( mode == Symbol ) current.rdbuf( &varName );
|
||||||
|
current << ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
overflow( const int ch ) override
|
||||||
|
{
|
||||||
|
if( ch == EOF ) throw std::logic_error{ "EOF!" };
|
||||||
|
writeChar( ch );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize
|
||||||
|
xsputn( const char *const data, const std::streamsize amt ) override
|
||||||
|
{
|
||||||
|
for( std::streamsize i= 0; i< amt; ++i ) overflow( data[ i ] );
|
||||||
|
return amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drain()
|
||||||
|
{
|
||||||
|
if( mode != Normal ) throw std::runtime_error{ "Unterminated variable `" + varName.str() + " in expansion." };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto wrapperIndex= std::ios::xalloc();
|
||||||
|
|
||||||
|
void
|
||||||
|
releaseWrapper( std::ios_base &ios )
|
||||||
|
{
|
||||||
|
auto *const streambuf= static_cast< VariableExpansionStreambuf * >( ios.pword( wrapperIndex ) );
|
||||||
|
if( not streambuf ) throw std::logic_error{ "Attempt to remove a substitution context which doesn't exist." };
|
||||||
|
|
||||||
|
streambuf->drain();
|
||||||
|
dynamic_cast< std::ostream & >( ios ).rdbuf( streambuf->underlying );
|
||||||
|
delete streambuf;
|
||||||
|
ios.pword( wrapperIndex )= nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wordwrapCallback( const std::ios_base::event event, std::ios_base &ios, const int idx )
|
||||||
|
{
|
||||||
|
if( wrapperIndex != idx ) throw std::logic_error{ "Wrong index." };
|
||||||
|
|
||||||
|
if( not ios.pword( wrapperIndex ) ) return;
|
||||||
|
|
||||||
|
if( event == std::ios_base::erase_event ) releaseWrapper( ios );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
impl::operator << ( std::ostream &os, StartSubstitutions &¶ms )
|
||||||
|
{
|
||||||
|
auto streambuf= std::make_unique< VariableExpansionStreambuf >();
|
||||||
|
streambuf->underlying= os.rdbuf( streambuf.get() );
|
||||||
|
streambuf->substitutions= std::move( params.substitutions );
|
||||||
|
streambuf->sigil= params.sigil;
|
||||||
|
auto &state= os.iword( wrapperIndex );
|
||||||
|
if( not state )
|
||||||
|
{
|
||||||
|
state= 1;
|
||||||
|
os.register_callback( wordwrapCallback, wrapperIndex );
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( os.pword( wrapperIndex ) == nullptr );
|
||||||
|
os.pword( wrapperIndex )= streambuf.release();
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
impl::operator << ( std::ostream &os, EndSubstitutions_t )
|
||||||
|
{
|
||||||
|
releaseWrapper( os );
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
exports::expandVariables( const std::string &text, const VarMap &vars, const char sigil )
|
exports::expandVariables( const std::string &text, const VarMap &vars, const char sigil )
|
||||||
{
|
{
|
||||||
|
#if 1
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
oss << StartSubstitutions{ sigil, vars };
|
||||||
|
oss << text;
|
||||||
|
oss << EndSubstitutions;
|
||||||
|
|
||||||
|
return std::move( oss ).str();
|
||||||
|
#else
|
||||||
if( C::debugExpansion ) error() << "Expanding variables in " << text << std::endl;
|
if( C::debugExpansion ) error() << "Expanding variables in " << text << std::endl;
|
||||||
|
|
||||||
std::string rv;
|
std::string rv;
|
||||||
@ -61,6 +187,7 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
|||||||
if( C::debugExpansion ) error() << "Expansion was: `" << rv << "`" << std::endl;
|
if( C::debugExpansion ) error() << "Expansion was: `" << rv << "`" << std::endl;
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector< std::string >
|
std::vector< std::string >
|
||||||
|
|||||||
@ -36,6 +36,28 @@ namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
|||||||
*/
|
*/
|
||||||
std::string expandVariables( const std::string &text, const VarMap &vars, const char sigil );
|
std::string expandVariables( const std::string &text, const VarMap &vars, const char sigil );
|
||||||
|
|
||||||
|
struct StartSubstitutions
|
||||||
|
{
|
||||||
|
const char sigil;
|
||||||
|
VarMap substitutions;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
StartSubstitutions( const char sigil, VarMap substitutions )
|
||||||
|
: sigil( sigil ), substitutions( std::move( substitutions ) )
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note that these function as a stack -- so `EndSubstitutions` will
|
||||||
|
// terminate the top of that stack.
|
||||||
|
constexpr struct EndSubstitutions_t {} EndSubstitutions;
|
||||||
|
|
||||||
|
inline namespace impl
|
||||||
|
{
|
||||||
|
std::ostream &operator << ( std::ostream &, StartSubstitutions && );
|
||||||
|
|
||||||
|
std::ostream &operator << ( std::ostream &, EndSubstitutions_t );
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Returns a vector of strings parsed from a comma separated string.
|
* Returns a vector of strings parsed from a comma separated string.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user