1
0
forked from Alepha/Alepha
Files
Alepha/string_algorithms.cc

183 lines
4.5 KiB
C++

static_assert( __cplusplus > 2020'99 );
#include "string_algorithms.h"
#include <memory>
#include <algorithm>
#include <exception>
#include <Alepha/IOStreams/EnableExceptions.h>
#include "error.h"
#include "AutoRAII.h"
namespace Alepha::Hydrogen ::detail:: string_algorithms_m
{
namespace
{
namespace C
{
const bool debug= false;
const bool debugExpansion= false or C::debug;
const bool debugCommas= false or C::debug;
const bool debugIOStreamLifecycle= false or C::debug;
}
struct VariableExpansionStreambuf
: public IOStreams::StackableStreambuf
{
public:
VarMap substitutions;
std::stringbuf varName;
char sigil;
enum { Symbol= 1, Normal= 0 } mode= Normal;
int throws= 0;
explicit
VariableExpansionStreambuf( std::ostream &os, VarMap &&substitutions, const char sigil )
: StackableStreambuf( os ), substitutions( std::move( substitutions ) ), sigil( sigil )
{}
void
writeChar( const char ch ) override
{
if( C::debugExpansion ) error() << "Entry to write and got `" << ch << '`' << std::endl;
std::ostream current{ out().rdbuf() };
if( mode == Normal and ch == sigil )
{
if( C::debugExpansion ) error() << "Normal and got `" << ch << '`' << std::endl;
mode= Symbol;
varName.str( "" );
return;
}
if( mode == Symbol and ch == sigil )
{
if( C::debugExpansion ) error() << "Closed symbol and got `" << ch << '`' << std::endl;
mode= Normal;
const std::string name( varName.view() );
if( C::debugExpansion )
{
error() << "Asked to expand `" << name << '`' << std::endl;
}
if( not name.empty() )
{
if( C::debugExpansion ) error() << "Name wasn't empty" << std::endl;
if( not substitutions.contains( name ) )
{
if( C::debugExpansion ) error() << "Throwing" << std::endl;
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( C::debugExpansion ) error() << "Stashing `" << ch << '`' << std::endl;
if( mode == Symbol ) current.rdbuf( &varName );
current << ch;
}
void
drain()
{
if( C::debugIOStreamLifecycle ) error() << "Drain called, and mode is: " << mode << std::endl;
if( mode != Normal )
{
if( C::debugIOStreamLifecycle ) error() << "Mode not being normal, we're throwing (" << ++throws << " times now)..." << std::endl;
mode= Normal;
throw std::runtime_error{ "Unterminated variable `" + varName.str() + "` in expansion." };
}
}
};
}
void
impl::build_streambuf( std::ostream &os, StartSubstitutions &&params )
{
new VariableExpansionStreambuf{ os, std::move( params.substitutions ), params.sigil };
}
std::string
exports::expandVariables( const std::string &text, const VarMap &vars, const char sigil )
{
std::ostringstream oss;
IOStreams::EnableExceptions exc{ oss };
oss << StartSubstitutions{ sigil, vars };
oss << text;
oss << EndSubstitutions;
return std::move( oss ).str();
}
std::vector< std::string >
exports::parseCommas( const std::string &string )
{
enum { Text, Backslash } state= Text;
std::vector< std::string > rv;
std::string next;
for( const char ch: string )
{
if( state == Backslash )
{
state= Text;
next+= ch;
}
else if( state == Text )
{
if( ch == '\\' ) state= Backslash;
else if( ch == ',' )
{
if( C::debugCommas ) error() << "Parsed from commas: `" << next << "`" << std::endl;
rv.push_back( std::move( next ) );
next.clear();
}
else next+= ch;
}
}
if( C::debugCommas ) error() << "Final parsed from commas: `" << next << "`" << std::endl;
rv.push_back( std::move( next ) );
return rv;
}
std::vector< std::string >
exports::split( const std::string &s, const char token )
{
std::vector< std::string > rv;
std::string next;
for( const char ch: s )
{
if( ch != token )
{
next+= ch;
continue;
}
rv.push_back( std::move( next ) );
next.clear();
}
rv.push_back( std::move( next ) );
return rv;
}
std::vector< std::string >
exports::split( std::string s, const std::string &delim )
{
std::vector< std::string > rv;
while( true )
{
const auto pos= s.find( delim );
rv.push_back( s.substr( 0, pos ) );
if( pos == std::string::npos ) break;
s= s.substr( pos + delim.size() );
}
return rv;
}
}