polserver/pol-core/bscript/bregexp.cpp

444 lines
12 KiB
C++
Raw Permalink Normal View History

#include "bscript/bregexp.h"
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
#include <stdexcept>
#include "bscript/barray.h"
#include "bscript/berror.h"
#include "bscript/blong.h"
#include "bscript/bstring.h"
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
#include "clib/strutil.h"
#include "bscript/exectype.h"
#include "bscript/executor.h"
#include "bscript/executor.inl.h"
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
namespace Pol::Bscript
{
// Traits to unify narrow vs wide regex handling
template <typename RegexT>
struct regex_traits;
template <>
struct regex_traits<boost::regex>
{
using string_type = std::string;
using match_type = boost::smatch;
using iterator_type = boost::sregex_iterator;
static string_type convert( const std::string& s ) { return s; }
};
template <>
struct regex_traits<boost::wregex>
{
using string_type = std::wstring;
using match_type = boost::wsmatch;
using iterator_type = boost::wsregex_iterator;
static string_type convert( const std::string& s ) { return Clib::to_wstring( s ); }
};
// Generic helpers
template <typename RegexT>
BObjectImp* do_find( const RegexT& re, const String* value, int d, boost::match_flag_type flags )
{
using traits = regex_traits<RegexT>;
using match_type = typename traits::match_type;
using string_type = typename traits::string_type;
string_type input = traits::convert( value->value() );
match_type match;
if ( d >= 0 && static_cast<size_t>( d ) < input.size() &&
boost::regex_search( input.cbegin() + d, input.cend(), match, re, flags ) )
{
return new BLong( Clib::clamp_convert<int>( match.position() + d + 1 ) );
}
return new BLong( 0 );
}
template <typename RegexT>
BObjectImp* do_match( const RegexT& re, const String* value, boost::match_flag_type flags )
{
using traits = regex_traits<RegexT>;
using match_type = typename traits::match_type;
using string_type = typename traits::string_type;
using iterator_type = typename traits::iterator_type;
auto add_match = []<typename MatchT>( const MatchT& pieces_match )
{
std::unique_ptr<BStruct> result( new BStruct );
std::unique_ptr<ObjArray> groups( new ObjArray );
result->addMember( "matched", new String( pieces_match.str( 0 ) ) );
for ( size_t i = 1; i < pieces_match.size(); ++i )
{
if ( pieces_match[i].matched )
{
std::unique_ptr<BStruct> group( new BStruct );
group->addMember( "matched", new String( pieces_match.str( i ) ) );
group->addMember( "offset",
new BLong( Clib::clamp_convert<int>( pieces_match.position( i ) + 1 ) ) );
groups->addElement( group.release() );
}
else
{
groups->addElement( UninitObject::create() );
}
}
result->addMember( "groups", groups.release() );
result->addMember( "offset",
new BLong( Clib::clamp_convert<int>( pieces_match.position() + 1 ) ) );
return result.release();
};
string_type input = traits::convert( value->value() );
// Non-global regex: Return first struct{ matched, groups, offset } or uninit
if ( flags & boost::regex_constants::format_first_only )
{
match_type match;
if ( boost::regex_search( input.cbegin(), input.cend(), match, re, flags ) )
{
return add_match( match );
}
return UninitObject::create();
}
// Global regex: Return array of all struct{ matched, groups, offset }
iterator_type current_match( input.cbegin(), input.cend(), re, flags );
iterator_type last_match;
std::unique_ptr<ObjArray> all_matches( new ObjArray );
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
while ( current_match != last_match )
{
all_matches->addElement( add_match( *current_match ) );
++current_match;
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
}
return all_matches.release();
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
}
template <typename RegexT>
BObjectImp* do_replace( const RegexT& re, const String* value, const String* replacement,
boost::match_flag_type flags )
{
using traits = regex_traits<RegexT>;
auto input = traits::convert( value->value() );
auto repl = traits::convert( replacement->value() );
auto result = boost::regex_replace( input, re, repl, flags );
return new String( result );
}
template <typename RegexT>
BObjectImp* do_replace( const RegexT& re, Executor& ex, BRegExp* bregexp, const String* value,
BFunctionRef* replacement_callback, boost::match_flag_type flags )
{
using traits = regex_traits<RegexT>;
using match_type = typename traits::match_type;
using string_type = typename traits::string_type;
using iterator_type = typename traits::iterator_type;
auto make_args = []( const auto& match, const auto& input )
{
BObjectRefVec args;
// Matched string
args.push_back( BObjectRef( new String( match.str() ) ) );
// Add group captures as an array
std::unique_ptr<ObjArray> groups( new ObjArray );
for ( size_t i = 1; i < match.size(); ++i )
{
if ( match[i].matched )
{
std::unique_ptr<BStruct> group( new BStruct );
group->addMember( "matched", new String( match.str( i ) ) );
group->addMember( "offset",
new BLong( Clib::clamp_convert<int>( match.position( i ) + 1 ) ) );
groups->addElement( group.release() );
}
else
{
groups->addElement( UninitObject::create() );
}
}
args.emplace_back( groups.release() );
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
// Add offset and original string as arguments
args.push_back( BObjectRef( new BLong( Clib::clamp_convert<int>( match.position() + 1 ) ) ) );
args.push_back( BObjectRef( new String( input ) ) );
return args;
};
auto input = std::make_unique<string_type>( traits::convert( value->value() ) );
iterator_type it( input->cbegin(), input->cend(), re, flags );
iterator_type end;
if ( it == end )
{
// No matches found, return the original string
return value->copy();
}
string_type replaced_result;
std::size_t lastPos = 0;
match_type match = *it;
BObjectRefVec args = make_args( match, *input );
// Append the text before the first match
replaced_result.append( *input, lastPos, match.position() - lastPos );
lastPos = match.position() + match.length();
auto callback = [make_args = std::move( make_args ),
regex = BObjectRef( bregexp ), // keep regex alive
flags, input = std::move( input ), it = std::move( it ), end = std::move( end ),
lastPos = std::move( lastPos ), match = std::move( match ),
replaced_result = std::move( replaced_result )](
Executor& ex, BContinuation* continuation,
BObjectRef result ) mutable -> BObjectImp*
{
// Append the replacement from the callback
replaced_result.append( traits::convert( result->impptr()->getStringRep() ) );
++it;
// If no more matches, or not global, return the result
if ( it == end || ( flags & boost::regex_constants::format_first_only ) )
{
// Append the tail after the last match
replaced_result.append( *input, lastPos, std::string::npos );
return new String( replaced_result );
}
// Process the next match
match = *it;
replaced_result.append( *input, lastPos, match.position() - lastPos );
lastPos = match.position() + match.length();
BObjectRefVec args = make_args( match, *input );
return ex.withContinuation( continuation, std::move( args ) );
};
return ex.makeContinuation( BObjectRef( new BObject( replacement_callback ) ),
std::move( callback ), std::move( args ) );
}
template <typename RegexT>
BObjectImp* do_split( const RegexT& re, const String* value, size_t limit,
boost::match_flag_type flags )
{
using traits = regex_traits<RegexT>;
using string_type = typename traits::string_type;
using iterator_type = typename traits::iterator_type;
std::unique_ptr<ObjArray> result( new ObjArray );
auto input = traits::convert( value->value() );
auto start = input.cbegin();
auto end = input.cend();
iterator_type it( start, end, re, flags );
iterator_type end_it;
std::size_t last_pos = 0;
for ( ; it != end_it && result->ref_arr.size() < limit - 1; ++it )
{
const auto& m = *it;
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
// text before the match
result->addElement( new String( string_type( start + last_pos, m[0].first ) ) );
// captured groups (if any)
for ( std::size_t i = 1; i < m.size(); ++i )
{
if ( result->ref_arr.size() >= limit )
break;
if ( m[i].matched )
{
result->addElement( new String( m[i].str() ) );
}
else
{
result->addElement( UninitObject::create() );
}
}
last_pos = m[0].second - start;
}
// remaining tail
if ( result->ref_arr.size() < limit )
{
result->addElement( new String( string_type( start + last_pos, end ) ) );
}
return result.release();
}
BRegExp::BRegExp( RegexT regex, boost::match_flag_type match_flags )
: BObjectImp( OTRegExp ), regex_( std::move( regex ) ), match_flags_( match_flags )
{
}
BObjectImp* BRegExp::create( const std::string& pattern, const std::string& flags )
{
boost::match_flag_type match_flags =
boost::regex_constants::match_single_line | boost::regex_constants::format_first_only;
boost::regex_constants::syntax_option_type flag = boost::regex_constants::ECMAScript;
bool unicode = false;
for ( const auto& ch : flags )
{
switch ( ch )
{
case 'u':
unicode = true;
break;
case 'i':
flag |= boost::regex_constants::icase;
break;
case 'm':
match_flags &= ~boost::regex_constants::match_single_line;
break;
case 'g':
match_flags &= ~boost::regex_constants::format_first_only;
break;
default:
return new BError( "Invalid flag character" );
}
}
try
{
if ( unicode )
{
auto pattern_w = Clib::to_wstring( pattern );
return new BRegExp( boost::wregex( pattern_w, flag ), match_flags );
}
return new BRegExp( boost::regex( pattern, flag ), match_flags );
}
catch ( ... )
{
return new BError( "Invalid regular expression" );
}
}
BRegExp::BRegExp( const BRegExp& i )
: BObjectImp( OTRegExp ), regex_( i.regex_ ), match_flags_( i.match_flags_ )
{
}
BObjectImp* BRegExp::find( const String* value_, int d ) const
{
try
{
return std::visit( [&]( auto&& re ) { return do_find( re, value_, d, match_flags_ ); },
regex_ );
}
catch ( ... )
{
return new BError( "Error during regular expression operation" );
}
}
BObjectImp* BRegExp::match( const String* value_ ) const
{
try
{
return std::visit( [&]( auto&& re ) { return do_match( re, value_, match_flags_ ); }, regex_ );
}
catch ( ... )
{
return new BError( "Error during regular expression operation" );
}
}
BObjectImp* BRegExp::replace( const String* value_, const String* replacement ) const
{
try
{
return std::visit(
[&]( auto&& re ) { return do_replace( re, value_, replacement, match_flags_ ); }, regex_ );
}
catch ( ... )
{
return new BError( "Error during regular expression operation" );
}
}
BObjectImp* BRegExp::replace( Executor& ex, const String* str, BFunctionRef* replacement_callback )
{
try
{
return std::visit(
[&]( auto&& re )
{ return do_replace( re, ex, this, str, replacement_callback, match_flags_ ); }, regex_ );
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
}
catch ( ... )
{
return new BError( "Error during regular expression operation" );
}
}
BObjectImp* BRegExp::split( const String* str, size_t limit ) const
{
try
{
return std::visit( [&]( auto&& re ) { return do_split( re, str, limit, match_flags_ ); },
regex_ );
}
catch ( ... )
{
return new BError( "Error during regular expression operation" );
}
}
BObjectImp* BRegExp::copy() const
{
return new BRegExp( *this );
}
const char* BRegExp::typeOf() const
{
return "RegExp";
}
u8 BRegExp::typeOfInt() const
{
return OTRegExp;
}
std::string BRegExp::getStringRep() const
{
return "<RegExp>";
}
size_t BRegExp::sizeEstimate() const
{
return sizeof( BRegExp );
}
bool BRegExp::operator<( const BObjectImp& ) const
{
return false;
}
bool BRegExp::isTrue() const
{
return true;
}
} // namespace Pol::Bscript