Add functions RegexReplace, RegexSearch and RegexMatch

console-destruction
Lukas Werling 2016-09-16 21:15:52 +02:00
parent 4b54b86d30
commit d7cd224a4f
5 changed files with 299 additions and 1 deletions

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>RegexMatch</title>
<category>Script</category>
<subcat>Strings</subcat>
<version>8.0 OC</version>
<syntax>
<rtype>array</rtype>
<params>
<param>
<type>string</type>
<name>text</name>
<desc>String to match.</desc>
</param>
<param>
<type>string</type>
<name>regex</name>
<desc>Regular expression in ECMAScript syntax.</desc>
</param>
<param>
<type>int</type>
<name>flags</name>
<desc>
Bitmask of the following values:
<table>
<rowh>
<col>Constant</col>
<col>Description</col>
</rowh>
<row>
<literal_col>Regex_CaseInsensitive</literal_col>
<col>Regular expression is case insensitive.</col>
</row>
<row>
<literal_col>Regex_FirstOnly</literal_col>
<col>Stop after first match, returning an array with zero or one elements.</col>
</row>
</table>
</desc>
</param>
</params>
</syntax>
<desc>
Performs a regular expression match, returning an array of arrays containing [full match, submatch 1, submatch 2, etc.].
</desc>
<examples>
<example>
<code><funclink>Log</funclink>("%v", RegexMatch("OC 8.0", "(\\w+)\\s(\\d+)\\.(\\d+)"));</code>
<text>Parses a version string, printing [["OC 8.0", "OC", "8", "0"]].</text>
</example>
</examples>
<related>
<funclink>RegexSearch</funclink>
<funclink>RegexReplace</funclink>
</related>
</func>
<author>Luchs</author><date>2016-09</date>
</funcs>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>RegexReplace</title>
<category>Script</category>
<subcat>Strings</subcat>
<version>8.0 OC</version>
<syntax>
<rtype>string</rtype>
<params>
<param>
<type>string</type>
<name>text</name>
<desc>Source string in which the replacements occur.</desc>
</param>
<param>
<type>string</type>
<name>regex</name>
<desc>Regular expression in ECMAScript syntax</desc>
</param>
<param>
<type>string</type>
<name>replacement</name>
<desc>New substring by which the regular expression matches will be replaced. Can reference submatches with $1, $2 etc.</desc>
</param>
<param>
<type>int</type>
<name>flags</name>
<desc>
Bitmask of the following values:
<table>
<rowh>
<col>Constant</col>
<col>Description</col>
</rowh>
<row>
<literal_col>Regex_CaseInsensitive</literal_col>
<col>Regular expression is case insensitive.</col>
</row>
<row>
<literal_col>Regex_FirstOnly</literal_col>
<col>Only replace the first match instead of all matches.</col>
</row>
</table>
</desc>
</param>
</params>
</syntax>
<desc>
Returns a string in which all regular expression matches are replaced by a replacement string.
</desc>
<examples>
<example>
<code><funclink>Log</funclink>(RegexReplace("hello world", "(\\w+)\\s(\\w+)", "$2 $1"));</code>
<text>Swaps two words, displaying "world hello".</text>
</example>
</examples>
<related>
<funclink>ReplaceString</funclink>
<funclink>RegexSearch</funclink>
<funclink>RegexMatch</funclink>
</related>
</func>
<author>Luchs</author><date>2016-09</date>
</funcs>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>RegexSearch</title>
<category>Script</category>
<subcat>Strings</subcat>
<version>8.0 OC</version>
<syntax>
<rtype>array</rtype>
<params>
<param>
<type>string</type>
<name>text</name>
<desc>String to search in.</desc>
</param>
<param>
<type>string</type>
<name>regex</name>
<desc>Regular expression in ECMAScript syntax.</desc>
</param>
<param>
<type>int</type>
<name>flags</name>
<desc>
Bitmask of the following values:
<table>
<rowh>
<col>Constant</col>
<col>Description</col>
</rowh>
<row>
<literal_col>Regex_CaseInsensitive</literal_col>
<col>Regular expression is case insensitive.</col>
</row>
<row>
<literal_col>Regex_FirstOnly</literal_col>
<col>Stop after first match, returning an array with zero or one elements.</col>
</row>
</table>
</desc>
</param>
</params>
</syntax>
<desc>
Performs a regular expression match, returning an array of match positions.
</desc>
<examples>
<example>
<code><funclink>Log</funclink>("%d", RegexSearch("hello world", "l"));</code>
<text>Searches for the character l, returning [2, 3, 9].</text>
</example>
</examples>
<related>
<funclink>RegexReplace</funclink>
<funclink>RegexMatch</funclink>
</related>
</func>
<author>Luchs</author><date>2016-09</date>
</funcs>

View File

@ -33,10 +33,13 @@
</desc>
<examples>
<example>
<code><funclink>Log(</funclink>ReplaceString("An apple a day keeps the apple away.", "apple", "banana"));</code>
<code><funclink>Log</funclink>(ReplaceString("An apple a day keeps the apple away.", "apple", "banana"));</code>
<text>Displays "An banana a day keeps the banana away."</text>
</example>
</examples>
<related>
<funclink>RegexReplace</funclink>
</related>
</func>
<author>Sven2</author><date>2016-07</date>
</funcs>

View File

@ -24,6 +24,8 @@
#include "lib/C4Random.h"
#include "C4Version.h"
#include <regex>
//========================== Some Support Functions =======================================
StdStrBuf FnStringFormat(C4PropList * _this, C4String *szFormatPar, C4Value * Pars, int ParCount)
@ -468,6 +470,101 @@ static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars)
return pEffect->DoCall(pTarget, szCallFn, Pars[3], Pars[4], Pars[5], Pars[6], Pars[7], Pars[8], Pars[9]);
}
/* Regex */
static const long
Regex_CaseInsensitive = (1 << 0),
Regex_FirstOnly = (1 << 1);
static std::regex_constants::syntax_option_type C4IntToSyntaxOption(long flags)
{
std::regex_constants::syntax_option_type out = std::regex::ECMAScript;
if (flags & Regex_CaseInsensitive)
out |= std::regex::icase;
return out;
}
static std::regex_constants::match_flag_type C4IntToMatchFlag(long flags)
{
std::regex_constants::match_flag_type out = std::regex_constants::match_default;
if (flags & Regex_FirstOnly)
out |= std::regex_constants::format_first_only;
return out;
}
static Nillable<C4String *> FnRegexReplace(C4PropList * _this, C4String *source, C4String *regex, C4String *replacement, long flags)
{
if (!source || !regex || !replacement) return C4Void();
try
{
std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
std::string out = std::regex_replace(source->GetCStr(), re, replacement->GetCStr(), C4IntToMatchFlag(flags));
return ::Strings.RegString(out.c_str());
}
catch (const std::regex_error& e)
{
throw C4AulExecError(FormatString("RegexReplace: %s", e.what()).getData());
}
}
static Nillable<C4ValueArray *> FnRegexSearch(C4PropList * _this, C4String *source, C4String *regex, long flags)
{
if (!source || !regex) return C4Void();
try
{
std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
std::smatch m;
C4ValueArray *out = new C4ValueArray();
std::string cur(source->GetCStr());
long i = 0, pos = 0;
while (std::regex_search(cur, m, re))
{
pos += GetCharacterCount(m.prefix().str().c_str());
(*out)[i++] = C4VInt(pos);
if (flags & Regex_FirstOnly) break;
pos += GetCharacterCount(m.str().c_str());
cur = m.suffix();
}
return out;
}
catch (const std::regex_error& e)
{
throw C4AulExecError(FormatString("RegexSearch: %s", e.what()).getData());
}
}
static Nillable<C4ValueArray *> FnRegexMatch(C4PropList * _this, C4String *source, C4String *regex, long flags)
{
if (!source || !regex) return C4Void();
try
{
std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
std::smatch m;
C4ValueArray *out = new C4ValueArray();
std::string cur(source->GetCStr());
long i = 0;
while (std::regex_search(cur, m, re))
{
C4ValueArray *match = new C4ValueArray(1);
long j = 0;
for (auto sm : m)
{
(*match)[j++] = C4VString(String(sm.str().c_str()));
}
(*out)[i++] = C4VArray(match);
if (flags & Regex_FirstOnly) break;
cur = m.suffix();
}
return out;
}
catch (const std::regex_error& e)
{
throw C4AulExecError(FormatString("RegexMatch: %s", e.what()).getData());
}
}
static C4Value FnLog(C4PropList * _this, C4Value * Pars)
{
Log(FnStringFormat(_this, Pars[0].getStr(), &Pars[1], 9).getData());
@ -964,6 +1061,9 @@ C4ScriptConstDef C4ScriptConstMap[]=
{ "FX_Call_EngCorrosion" ,C4V_Int, C4FxCall_EngCorrosion }, // energy loss through corrosion (acid)
{ "FX_Call_EngGetPunched" ,C4V_Int, C4FxCall_EngGetPunched }, // energy loss from punch
{ "Regex_CaseInsensitive" ,C4V_Int, Regex_CaseInsensitive },
{ "Regex_FirstOnly" ,C4V_Int, Regex_FirstOnly },
{ "C4V_Nil", C4V_Int, C4V_Nil},
{ "C4V_Int", C4V_Int, C4V_Int},
{ "C4V_Bool", C4V_Int, C4V_Bool},
@ -1034,6 +1134,9 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
F(RemoveEffect);
F(GetEffect);
F(GetEffectCount);
F(RegexReplace);
F(RegexSearch);
F(RegexMatch);
F(Distance);
F(Angle);
F(GetChar);