forked from Alepha/Alepha
Some string algorithms also from my scrach environment.
The most useful one is the variable expansion code.
This commit is contained in:
98
string_algorithms.cpp
Normal file
98
string_algorithms.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include "string_algorithms.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
||||
#include "error.hpp"
|
||||
|
||||
namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||
{
|
||||
namespace
|
||||
{
|
||||
namespace C
|
||||
{
|
||||
const bool debug= false;
|
||||
const bool debugExpansion= false or C::debug;
|
||||
const bool debugCommas= false or C::debug;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
exports::expandVariables( const std::string &text, const VarMap &vars, const char sigil )
|
||||
{
|
||||
if( C::debugExpansion ) error() << "Expanding variables in " << text << std::endl;
|
||||
|
||||
std::string rv;
|
||||
std::string varName;
|
||||
|
||||
enum { Symbol, Normal } mode= Normal;
|
||||
|
||||
for( const char ch: text )
|
||||
{
|
||||
if( mode == normal and ch == sigil )
|
||||
{
|
||||
mode= Symbol;
|
||||
varName.clear();
|
||||
continue;
|
||||
}
|
||||
if( mode == Symbol and ch == sigil )
|
||||
{
|
||||
mode= Normal;
|
||||
if( not varName.empty() )
|
||||
{
|
||||
if( not vars.contains( varName ) )
|
||||
{
|
||||
throw std::runtime_error( "No such variable: `" + varName + "`" );
|
||||
}
|
||||
if( C::debugVariableExpansion ) error() << "Expanding variable with name `" << varName << "`" << std::endl;
|
||||
rv+= vars.at( varName )();
|
||||
}
|
||||
else rv+= sigil;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ¤t= mode == Normal ? rv : varName;
|
||||
current+= ch;
|
||||
}
|
||||
|
||||
if( mode != Normal ) throw std::runtime_error( "Unterminated variable `" + varName + " in expansion of `" + text + "`" );
|
||||
if( C::debugExpansion ) error() << "Expansion was: `" << rv << "`" << std::endl;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
71
string_algorithms.h
Normal file
71
string_algorithms.h
Normal file
@ -0,0 +1,71 @@
|
||||
static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
||||
{
|
||||
inline namespace exports {}
|
||||
|
||||
using VarMap= std::map< std::string, std::function< std::string () > >;
|
||||
|
||||
inline namespace exports
|
||||
{
|
||||
/*!
|
||||
* Returns a new string with text-replacement variables expanded.
|
||||
*
|
||||
* @param text The source text to expand variables within.
|
||||
* @param vars A map of variable names to values to expand.
|
||||
* @param sigil A character which encloses the variable name. (If the character is `'%'` for example,
|
||||
* then `"%variable%"` is a variable name.)
|
||||
*/
|
||||
std::string expandVariables( const std::string &text, const VarMap &vars, const char sigil );
|
||||
|
||||
/*!
|
||||
* Returns a vector of strings parsed from a comma separated string.
|
||||
*
|
||||
* @note This function is almost a split function, but it supports explicit
|
||||
* backslashes for various special characters and the `','` character.
|
||||
*
|
||||
* @param text The text to parse.
|
||||
* @return A vector of the substrings found in the string.
|
||||
*/
|
||||
std::vector< std::string > parseCommas( const std::string &text );
|
||||
|
||||
|
||||
/*!
|
||||
* Parses an integral range description into a vector of values.
|
||||
*/
|
||||
template< Integral T >
|
||||
std::vector< T >
|
||||
parseRange( const std::string &s )
|
||||
{
|
||||
auto tokens= split( s, "-" );
|
||||
if( tokens.empty() or tokens.size() > 2 )
|
||||
{
|
||||
throw std::runtime_error( "Expected an integer or a range." );
|
||||
}
|
||||
// If there's no range, or we had a negative number, just emit that.
|
||||
if( tokens.size() == 1 or tokens.at( 0 ).empty() ) return { boost::lexical_cast< T >( s ) };
|
||||
|
||||
const auto low= boost::lexical_cast< T >( tokens.at( 0 ) );
|
||||
const auto high= boost::lexical_cast< T >( tokens.at( 0 ) );
|
||||
|
||||
std::vector< T > rv{ high - low + 1 };
|
||||
std::iota( begin( rv ), end( rv ), low );
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Cavorite::inline exports::inline string_algorithms
|
||||
{
|
||||
using namespace detail::exports::string_algorithms;
|
||||
}
|
||||
Reference in New Issue
Block a user