forked from Mirrors/openclonk
Add functions RegexReplace, RegexSearch and RegexMatch
parent
4b54b86d30
commit
d7cd224a4f
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue