Commit 729d30ec authored by twanvl's avatar twanvl

Made a Regex class that wraps either boost::regex or wxRegEx (split from ScriptRegex).

Use Regex for keywords.
parent 43ced447
...@@ -126,20 +126,38 @@ String KeywordParam::make_separator_before() const { ...@@ -126,20 +126,38 @@ String KeywordParam::make_separator_before() const {
}*/ }*/
void KeywordParam::compile() { void KeywordParam::compile() {
// compile separator_before // compile separator_before
if (!separator_before_is.empty() && !separator_before_re.IsValid()) { if (!separator_before_is.empty() && separator_before_re.empty()) {
separator_before_re.Compile(_("^") + separator_before_is, wxRE_ADVANCED); separator_before_re.assign(_("^") + separator_before_is);
if (eat_separator) { if (eat_separator) {
separator_before_eat.Compile(separator_before_is + _("$"), wxRE_ADVANCED); separator_before_eat.assign(separator_before_is + _("$"));
} }
} }
// compile separator_after // compile separator_after
if (!separator_after_is.empty() && !separator_after_re.IsValid()) { if (!separator_after_is.empty() && separator_after_re.empty()) {
separator_after_re.Compile(separator_after_is + _("$"), wxRE_ADVANCED); separator_after_re.assign(separator_after_is + _("$"));
if (eat_separator) { if (eat_separator) {
separator_after_eat.Compile(_("^") + separator_after_is, wxRE_ADVANCED); separator_after_eat.assign(_("^") + separator_after_is);
} }
} }
} }
void KeywordParam::eat_separator_before(String& text) {
if (separator_before_eat.empty()) return;
Regex::Results result;
if (separator_before_eat.matches(result, text)) {
// keep only stuff before the separator
assert(result.position() + result.size() == text.size());
text.resize(result.position());
}
}
void KeywordParam::eat_separator_after(const String& text, size_t& i) {
if (separator_after_eat.empty()) return;
Regex::Results result;
if (separator_after_eat.matches(result, text.begin() + i, text.end())) {
// advance past the separator
assert(result.position() == 0);
i += result.length();
}
}
size_t Keyword::findMode(const vector<KeywordModeP>& modes) const { size_t Keyword::findMode(const vector<KeywordModeP>& modes) const {
// find // find
...@@ -161,12 +179,15 @@ size_t Keyword::findMode(const vector<KeywordModeP>& modes) const { ...@@ -161,12 +179,15 @@ size_t Keyword::findMode(const vector<KeywordModeP>& modes) const {
// ----------------------------------------------------------------------------- : Regex stuff // ----------------------------------------------------------------------------- : Regex stuff
void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) { void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) {
if (!force && match_re.IsValid()) return; if (!force && !match_re.empty()) return;
parameters.clear(); parameters.clear();
// Prepare regex // Prepare regex
String regex; String regex;
String text; // normal, non-regex, text String text; // normal, non-regex, text
vector<KeywordParamP>::const_iterator param = parameters.begin(); vector<KeywordParamP>::const_iterator param = parameters.begin();
#if USE_CASE_INSENSITIVE_KEYWORDS
regex = _("(?i)"); // case insensitive matching
#endif
// Parse the 'match' string // Parse the 'match' string
for (size_t i = 0 ; i < match.size() ;) { for (size_t i = 0 ; i < match.size() ;) {
Char c = match.GetChar(i); Char c = match.GetChar(i);
...@@ -175,14 +196,14 @@ void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) { ...@@ -175,14 +196,14 @@ void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) {
size_t start = skip_tag(match, i), end = match_close_tag(match, i); size_t start = skip_tag(match, i), end = match_close_tag(match, i);
String type = match.substr(start, end-start); String type = match.substr(start, end-start);
// find parameter type 'type' // find parameter type 'type'
KeywordParamP p; KeywordParamP param;
FOR_EACH_CONST(pt, param_types) { FOR_EACH_CONST(pt, param_types) {
if (pt->name == type) { if (pt->name == type) {
p = pt; param = pt;
break; break;
} }
} }
if (!p) { if (!param) {
// throwing an error can mean a set will not be loaded! // throwing an error can mean a set will not be loaded!
// instead, simply disable the keyword // instead, simply disable the keyword
//throw InternalError(_("Unknown keyword parameter type: ") + type); //throw InternalError(_("Unknown keyword parameter type: ") + type);
...@@ -190,45 +211,33 @@ void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) { ...@@ -190,45 +211,33 @@ void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) {
valid = false; valid = false;
return; return;
} }
parameters.push_back(p); parameters.push_back(param);
// modify regex : match text before // modify regex : match text before
p->compile(); param->compile();
if (p->separator_before_eat.IsValid() && p->separator_before_eat.Matches(text)) { // remove the separator from the text to prevent duplicates
// remove the separator from the text to prevent duplicates param->eat_separator_before(text);
size_t start, len;
p->separator_before_eat.GetMatch(&start, &len);
text = text.substr(0, start);
}
regex += _("(") + regex_escape(text) + _(")"); regex += _("(") + regex_escape(text) + _(")");
text.clear(); text.clear();
// modify regex : match parameter // modify regex : match parameter
regex += _("(") + make_non_capturing(p->match) + (p->optional ? _(")?") : _(")")); regex += _("(") + make_non_capturing(param->match) + (param->optional ? _(")?") : _(")"));
i = skip_tag(match, end); i = skip_tag(match, end);
// eat separator_after? // eat separator_after?
if (p->separator_after_eat.IsValid() && p->separator_after_eat.Matches(match.substr(i))) { param->eat_separator_after(match, i);
size_t start, len;
p->separator_before_eat.GetMatch(&start, &len);
i += start + len;
}
} else { } else {
text += c; text += c;
i++; i++;
} }
} }
regex += _("(") + regex_escape(text) + _(")"); regex += _("(") + regex_escape(text) + _(")");
regex = _("\\y") + regex + _("(?=$|[^a-zA-Z0-9\\(])"); // only match whole words #if USE_BOOST_REGEX
#if USE_CASE_INSENSITIVE_KEYWORDS regex = _("\\<")
int flags = wxRE_ADVANCED | wxRE_ICASE; // case insensitive matching
#else #else
int flags = wxRE_ADVANCED regex = _("\\y")
#endif #endif
if (match_re.Compile(regex, flags)) { + regex + _("(?=$|[^a-zA-Z0-9\\(])"); // only match whole words
// not valid if it matches "", that would make MSE hang match_re.assign(regex);
valid = !match_re.Matches(_("")); // not valid if it matches "", that would make MSE hang
} else { valid = !match_re.matches(_(""));
valid = false;
throw InternalError(_("Error creating match regex"));
}
} }
// ----------------------------------------------------------------------------- : KeywordTrie // ----------------------------------------------------------------------------- : KeywordTrie
...@@ -332,19 +341,8 @@ void KeywordDatabase::add(const Keyword& kw) { ...@@ -332,19 +341,8 @@ void KeywordDatabase::add(const Keyword& kw) {
i = match_close_tag_end(kw.match, i); i = match_close_tag_end(kw.match, i);
// parameter, is there a separator we should eat? // parameter, is there a separator we should eat?
if (param < kw.parameters.size()) { if (param < kw.parameters.size()) {
wxRegEx& sep_before = kw.parameters[param]->separator_before_eat; kw.parameters[param]->eat_separator_before(text);
wxRegEx& sep_after = kw.parameters[param]->separator_after_eat; kw.parameters[param]->eat_separator_after(kw.match, i);
if (sep_before.IsValid() && sep_before.Matches(text)) {
// remove the separator from the text to prevent duplicates
size_t start, len;
sep_before.GetMatch(&start, &len);
text = text.substr(0, start);
}
if (sep_after.IsValid() && sep_after.Matches(kw.match.substr(i))) {
size_t start, len;
sep_after.GetMatch(&start, &len);
i += start + len;
}
} }
++param; ++param;
// match anything // match anything
...@@ -487,7 +485,6 @@ String KeywordDatabase::expand(const String& text, ...@@ -487,7 +485,6 @@ String KeywordDatabase::expand(const String& text,
FOR_EACH(n, current) { FOR_EACH(n, current) {
FOR_EACH(f, n->finished) { FOR_EACH(f, n->finished) {
const Keyword* kw = f; const Keyword* kw = f;
assert(kw->match_re.IsValid());
if (used.insert(kw).second) { if (used.insert(kw).second) {
// we have found a possible match, for a keyword which we have not seen before // we have found a possible match, for a keyword which we have not seen before
if (tryExpand(*kw, i, tagged, untagged, result, expand_type, if (tryExpand(*kw, i, tagged, untagged, result, expand_type,
...@@ -525,11 +522,13 @@ bool KeywordDatabase::tryExpand(const Keyword& kw, ...@@ -525,11 +522,13 @@ bool KeywordDatabase::tryExpand(const Keyword& kw,
Value* stat_key) const Value* stat_key) const
{ {
// try to match regex against the *untagged* string // try to match regex against the *untagged* string
if (!kw.match_re.Matches(untagged)) return false; assert(!kw.match_re.empty());
Regex::Results match;
if (!kw.match_re.matches(match, untagged)) return false;
// Find match position // Find match position
size_t start_u, len_u; size_t start_u = match.position();
kw.match_re.GetMatch(&start_u, &len_u, 0); size_t len_u = match.length();
size_t start = untagged_to_index(tagged, start_u, true), size_t start = untagged_to_index(tagged, start_u, true),
end = untagged_to_index(tagged, start_u + len_u, true); end = untagged_to_index(tagged, start_u + len_u, true);
if (start == end) return false; // don't match empty keywords if (start == end) return false; // don't match empty keywords
...@@ -560,34 +559,36 @@ bool KeywordDatabase::tryExpand(const Keyword& kw, ...@@ -560,34 +559,36 @@ bool KeywordDatabase::tryExpand(const Keyword& kw,
// Split the keyword, set parameters in context // Split the keyword, set parameters in context
// The even captures are parameter values, the odd ones are the plain text in between // The even captures are parameter values, the odd ones are the plain text in between
String total; // the total keyword String total; // the total keyword
size_t match_count = kw.match_re.GetMatchCount(); assert(match.size() - 1 == 1 + 2 * kw.parameters.size());
assert(match_count - 1 == 1 + 2 * kw.parameters.size());
size_t part_start = start; size_t part_start = start;
for (size_t j = 1 ; j < match_count ; ++j) { for (size_t submatch = 1 ; submatch < match.size() ; ++submatch) {
// we start counting at 1, so // the matched part
// j = 1 mod 2 -> text size_t part_start_u = match.position(submatch);
// j = 0 mod 2 -> parameter #((j-1)/2) == (j/2-1) size_t part_len_u = match.length((int)submatch);
size_t part_start_u, part_len_u; size_t part_end_u = part_start_u + part_len_u;
kw.match_re.GetMatch(&part_start_u, &part_len_u, j);
// note: start_u can be (uint)-1 when part_len_u == 0 // note: start_u can be (uint)-1 when part_len_u == 0
size_t part_end = part_len_u > 0 ? untagged_to_index(tagged, part_start_u + part_len_u, true) : part_start; size_t part_end = part_len_u > 0 ? untagged_to_index(tagged, part_end_u, true) : part_start;
String part = tagged.substr(part_start, part_end - part_start); String part(tagged, part_start, part_end - part_start);
// strip left over </kw tags // strip left over </kw tags
part = remove_tag(part,_("</kw-")); part = remove_tag(part,_("</kw-"));
if ((j % 2) == 0) { // we start counting at 1, so
// submatch = 1 mod 2 -> text
// submatch = 0 mod 2 -> parameter
if ((submatch % 2) == 0) {
// parameter // parameter
KeywordParam& kwp = *kw.parameters[j/2-1]; KeywordParam& kwp = *kw.parameters[(submatch - 2) / 2];
String param = untagged.substr(part_start_u, part_len_u); // untagged version String param = untagged.substr(part_start_u, part_len_u); // untagged version
// strip separator_before // strip separator_before
String separator_before, separator_after; String separator_before, separator_after;
if (kwp.separator_before_re.IsValid() && kwp.separator_before_re.Matches(param)) { Regex::Results sep_match;
size_t sep_start, sep_len; // start should be 0 if (!kwp.separator_before_re.empty() && kwp.separator_before_re.matches(sep_match, param)) {
kwp.separator_before_re.GetMatch(&sep_start, &sep_len); size_t sep_end = sep_match.length();
separator_before = param.substr(0, sep_start + sep_len); assert(sep_match.position() == 0); // should only match at start of param
param = param.substr(sep_start + sep_len); separator_before.assign(param, 0, sep_end);
param.erase(0, sep_end);
// strip from tagged version // strip from tagged version
size_t sep_end_t = untagged_to_index(part, sep_start + sep_len, false); size_t sep_end_t = untagged_to_index(part, sep_end, false);
part = get_tags(part, 0, sep_end_t, true, true) + part.substr(sep_end_t); part = get_tags(part, 0, sep_end_t, true, true) + part.substr(sep_end_t);
// transform? // transform?
if (kwp.separator_script) { if (kwp.separator_script) {
...@@ -596,11 +597,11 @@ bool KeywordDatabase::tryExpand(const Keyword& kw, ...@@ -596,11 +597,11 @@ bool KeywordDatabase::tryExpand(const Keyword& kw,
} }
} }
// strip separator_after // strip separator_after
if (kwp.separator_after_re.IsValid() && kwp.separator_after_re.Matches(param)) { if (!kwp.separator_after_re.empty() && kwp.separator_after_re.matches(sep_match, param)) {
size_t sep_start, sep_len; // start + len should be param.size() size_t sep_start = sep_match.position();
kwp.separator_after_re.GetMatch(&sep_start, &sep_len); assert(sep_match[0].second == param.end()); // should only match at end of param
separator_after = param.substr(sep_start); separator_after.assign(param, sep_start);
param = param.substr(0, sep_start); param.resize(sep_start);
// strip from tagged version // strip from tagged version
size_t sep_start_t = untagged_to_index(part, sep_start, false); size_t sep_start_t = untagged_to_index(part, sep_start, false);
part = part.substr(0, sep_start_t) + get_tags(part, sep_start_t, part.size(), true, true); part = part.substr(0, sep_start_t) + get_tags(part, sep_start_t, part.size(), true, true);
...@@ -631,7 +632,7 @@ bool KeywordDatabase::tryExpand(const Keyword& kw, ...@@ -631,7 +632,7 @@ bool KeywordDatabase::tryExpand(const Keyword& kw,
} }
} }
part = separator_before + script_part->toString() + separator_after; part = separator_before + script_part->toString() + separator_after;
ctx.setVariable(String(_("param")) << (int)(j/2), script_param); ctx.setVariable(String(_("param")) << (int)(submatch/2), script_param);
} else if (correct_case) { } else if (correct_case) {
// Plain text, check if the case matches // Plain text, check if the case matches
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
#include <util/dynamic_arg.hpp> #include <util/dynamic_arg.hpp>
#include <wx/regex.h> #include <util/regex.hpp>
DECLARE_POINTER_TYPE(KeywordParam); DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode); DECLARE_POINTER_TYPE(KeywordMode);
...@@ -42,11 +42,11 @@ class KeywordParam : public IntrusivePtrBase<KeywordParam> { ...@@ -42,11 +42,11 @@ class KeywordParam : public IntrusivePtrBase<KeywordParam> {
bool optional; ///< Can this parameter be left out (a placeholder is then used) bool optional; ///< Can this parameter be left out (a placeholder is then used)
String match; ///< Regular expression to match (including separators) String match; ///< Regular expression to match (including separators)
String separator_before_is; ///< Regular expression of separator before the param String separator_before_is; ///< Regular expression of separator before the param
wxRegEx separator_before_re; ///< Regular expression of separator before the param, compiled Regex separator_before_re; ///< Regular expression of separator before the param, compiled
wxRegEx separator_before_eat;///< Regular expression of separator before the param, if eat_separator Regex separator_before_eat;///< Regular expression of separator before the param, if eat_separator
String separator_after_is; ///< Regular expression of separator after the param String separator_after_is; ///< Regular expression of separator after the param
wxRegEx separator_after_re; ///< Regular expression of separator after the param, compiled Regex separator_after_re; ///< Regular expression of separator after the param, compiled
wxRegEx separator_after_eat; ///< Regular expression of separator after the param, if eat_separator Regex separator_after_eat; ///< Regular expression of separator after the param, if eat_separator
bool eat_separator; ///< Remove the separator from the match string if it also appears there (prevent duplicates) bool eat_separator; ///< Remove the separator from the match string if it also appears there (prevent duplicates)
OptionalScript script; ///< Transformation of the value for showing as the parameter OptionalScript script; ///< Transformation of the value for showing as the parameter
OptionalScript reminder_script; ///< Transformation of the value for showing in the reminder text OptionalScript reminder_script; ///< Transformation of the value for showing in the reminder text
...@@ -61,6 +61,11 @@ class KeywordParam : public IntrusivePtrBase<KeywordParam> { ...@@ -61,6 +61,11 @@ class KeywordParam : public IntrusivePtrBase<KeywordParam> {
/// Compile regexes for separators /// Compile regexes for separators
void compile(); void compile();
/// Remove separator_before from the end of the text
void eat_separator_before(String& text);
/// Advance i past separator_before if it is at position i in the text
void eat_separator_after(const String& text, size_t& i);
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
...@@ -96,7 +101,7 @@ class Keyword : public IntrusivePtrVirtualBase { ...@@ -96,7 +101,7 @@ class Keyword : public IntrusivePtrVirtualBase {
* captures 1,3,... capture the plain text of the match string * captures 1,3,... capture the plain text of the match string
* captures 2,4,... capture the separators and parameters * captures 2,4,... capture the separators and parameters
*/ */
wxRegEx match_re; Regex match_re;
bool fixed; ///< Is this keyword uneditable? (true for game keywods, false for set keywords) bool fixed; ///< Is this keyword uneditable? (true for game keywods, false for set keywords)
bool valid; ///< Is this keyword okay (reminder text compiles & runs; match does not match "") bool valid; ///< Is this keyword okay (reminder text compiles & runs; match does not match "")
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="5" RuntimeLibrary="5"
BufferSecurityCheck="TRUE" BufferSecurityCheck="TRUE"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -98,6 +100,8 @@ ...@@ -98,6 +100,8 @@
RuntimeLibrary="4" RuntimeLibrary="4"
BufferSecurityCheck="FALSE" BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE" EnableFunctionLevelLinking="TRUE"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -165,6 +169,8 @@ ...@@ -165,6 +169,8 @@
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="3" RuntimeLibrary="3"
BufferSecurityCheck="TRUE" BufferSecurityCheck="TRUE"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -241,6 +247,8 @@ ...@@ -241,6 +247,8 @@
BufferSecurityCheck="FALSE" BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE" EnableFunctionLevelLinking="TRUE"
EnableEnhancedInstructionSet="1" EnableEnhancedInstructionSet="1"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -317,6 +325,8 @@ ...@@ -317,6 +325,8 @@
BufferSecurityCheck="FALSE" BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE" EnableFunctionLevelLinking="TRUE"
EnableEnhancedInstructionSet="1" EnableEnhancedInstructionSet="1"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -393,6 +403,8 @@ ...@@ -393,6 +403,8 @@
BufferSecurityCheck="FALSE" BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE" EnableFunctionLevelLinking="TRUE"
EnableEnhancedInstructionSet="1" EnableEnhancedInstructionSet="1"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="3" UsePrecompiledHeader="3"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -462,6 +474,8 @@ ...@@ -462,6 +474,8 @@
BasicRuntimeChecks="0" BasicRuntimeChecks="0"
RuntimeLibrary="3" RuntimeLibrary="3"
BufferSecurityCheck="FALSE" BufferSecurityCheck="FALSE"
TreatWChar_tAsBuiltInType="FALSE"
ForceConformanceInForLoopScope="TRUE"
RuntimeTypeInfo="TRUE" RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="2" UsePrecompiledHeader="2"
PrecompiledHeaderThrough="util/prec.hpp" PrecompiledHeaderThrough="util/prec.hpp"
...@@ -2242,6 +2256,54 @@ ...@@ -2242,6 +2256,54 @@
<File <File
RelativePath=".\util\real_point.hpp"> RelativePath=".\util\real_point.hpp">
</File> </File>
<File
RelativePath=".\util\regex.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Debug Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Profile Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode fast build|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Debug Unicode NoInit|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\regex.hpp">
</File>
<File <File
RelativePath=".\util\vector2d.hpp"> RelativePath=".\util\vector2d.hpp">
</File> </File>
......
...@@ -9,144 +9,24 @@ ...@@ -9,144 +9,24 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <script/functions/functions.hpp> #include <script/functions/functions.hpp>
#include <script/functions/util.hpp> #include <script/functions/util.hpp>
#include <util/regex.hpp>
#include <util/error.hpp> #include <util/error.hpp>
// Use boost::regex as opposed to wxRegex
/* 2008-09-01:
* Script profiling shows that the boost library is significantly faster:
* When loading a large magic set (which calls ScriptManager::updateAll):
* function Calls wxRegEx boost
* ------------------------------------------------------------------
* replace 3791 0.38607 0.20857
* filter_text 11 0.32251 0.02446
*
* (times are avarage over all calls, in ms)
*/
#define USE_BOOST_REGEX 1
#if USE_BOOST_REGEX
#include <boost/regex.hpp>
#include <boost/regex/pattern_except.hpp>
typedef boost::basic_regex<Char> BoostRegex;
#endif
DECLARE_POINTER_TYPE(ScriptRegex); DECLARE_POINTER_TYPE(ScriptRegex);
DECLARE_TYPEOF_COLLECTION(pair<Variable COMMA ScriptValueP>); DECLARE_TYPEOF_COLLECTION(pair<Variable COMMA ScriptValueP>);
// ----------------------------------------------------------------------------- : Regex type // ----------------------------------------------------------------------------- : Regex type
/// A regular expression for use in a script /// A regular expression for use in a script
class ScriptRegex : public ScriptValue { class ScriptRegex : public ScriptValue, public Regex {
public: public:
virtual ScriptType type() const { return SCRIPT_REGEX; } virtual ScriptType type() const { return SCRIPT_REGEX; }
virtual String typeName() const { return _("regex"); } virtual String typeName() const { return _("regex"); }
#if USE_BOOST_REGEX ScriptRegex(const String& code) {
assign(code);
ScriptRegex(const String& code) { }
// compile string
try {
regex.assign(code.begin(),code.end());
} catch (const boost::regex_error& e) {
/// TODO: be more precise
throw ScriptError(String::Format(_("Error while compiling regular expression: '%s'\nAt position: %d\n%s"),
code.c_str(), e.position(), String(e.what(), IF_UNICODE(wxConvUTF8,String::npos))));
}
}
struct Results : public boost::match_results<const Char*> {
/// Get a sub match
inline String str(int sub = 0) const {
const_reference v = (*this)[sub];
return String(v.first, v.second);
}
/// Format a replacement string
inline String format(const String& format) const {
std::basic_string<Char> fmt(format.begin(),format.end());
String output;
boost::match_results<const Char*>::format(
insert_iterator<String>(output, output.end()), fmt, boost::format_sed);
return output;
}
};
inline bool matches(const String& str) {
return regex_search(str.c_str(), regex);
}
inline bool matches(Results& results, const Char* begin, const Char* end) {
return regex_search(begin, end, results, regex);
}
inline void replace_all(String* input, const String& format) {
//std::basic_string<Char> fmt; format_string(format,fmt);
std::basic_string<Char> fmt(format.begin(),format.end());
String output;
regex_replace(insert_iterator<String>(output, output.end()),
input->begin(), input->end(), regex, fmt, boost::format_sed);
*input = output;
}
private:
BoostRegex regex; ///< The regular expression
#else
ScriptRegex(const String& code) {
// compile string
if (!regex.Compile(code, wxRE_ADVANCED)) {
throw ScriptError(_("Error while compiling regular expression: '") + code + _("'"));
}
assert(regex.IsValid());
}
// Interface for compatability with boost::regex
struct Results {
typedef pair<const Char*,const Char*> value_type; // (begin,end)
typedef value_type const_reference;
/// Number of submatches (+1 for the total match)
inline size_t size() const { return regex->GetMatchCount(); }
/// Get a submatch
inline value_type operator [] (int sub) const {
size_t pos, length;
bool ok = regex->GetMatch(&pos, &length, sub);
assert(ok);
return make_pair(begin + pos, begin + pos + length);
}
/// Get a sub match
inline String str(int sub = 0) const {
const_reference v = (*this)[sub];
return String(v.first, v.second);
}
/// Format a replacement string
inline String format(const String& format) const {
const_reference v = (*this)[0];
String inside(v.first, v.second);
regex->ReplaceFirst(&inside, format);
return inside;
}
private:
wxRegEx* regex;
const Char* begin;
friend class ScriptRegex;
};
inline bool matches(const String& str) {
return regex.Matches(str);
}
inline bool matches(Results& results, const Char* begin, const Char* end) {
results.regex = &regex;
results.begin = begin;
return regex.Matches(begin, 0, end - begin);
}
inline void replace_all(String* input, const String& format) {
regex.Replace(input, format);
}
private:
wxRegEx regex; ///< The regular expression
#endif
public:
/// Match only if in_context also matches /// Match only if in_context also matches
bool matches(Results& results, const String& str, const Char* begin, const ScriptRegexP& in_context) { bool matches(Results& results, const String& str, const Char* begin, const ScriptRegexP& in_context) {
if (!in_context) { if (!in_context) {
...@@ -156,15 +36,16 @@ class ScriptRegex : public ScriptValue { ...@@ -156,15 +36,16 @@ class ScriptRegex : public ScriptValue {
Results::const_reference match = results[0]; Results::const_reference match = results[0];
String context_str(str.begin(), match.first); // before String context_str(str.begin(), match.first); // before
context_str += _("<match>"); context_str += _("<match>");
context_str.append(match.second, str.end()); context_str.append(match.second, str.end()); // after
if (in_context->matches(context_str)) { if (in_context->matches(context_str)) {
return true; return true; // the context matches, done
} }
begin = match.second; // skip begin = match.second; // skip
} }
return false; return false;
} }
} }
using Regex::matches;
}; };
ScriptRegexP regex_from_script(const ScriptValueP& value) { ScriptRegexP regex_from_script(const ScriptValueP& value) {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <wx/wx.h> #include <wx/wx.h>
#include <wx/image.h> #include <wx/image.h>
#include <wx/datetime.h> #include <wx/datetime.h>
#include <wx/regex.h> #include <wx/regex.h> // TODO : remove, see regex.hpp
// Std headers // Std headers
#include <vector> #include <vector>
...@@ -78,6 +78,15 @@ class FileName : public wxString { ...@@ -78,6 +78,15 @@ class FileName : public wxString {
#include "locale.hpp" #include "locale.hpp"
#include "error.hpp" #include "error.hpp"
#include "reflect.hpp" #include "reflect.hpp"
#include "regex.hpp"
#ifdef _MSC_VER
//# pragma conform(forScope,on) // in "for(int x=..);" x goes out of scope after the for
// somehow forScope pragma doesn't work in precompiled headers, use this hack instead:
#ifdef _DEBUG
#define for if(false);else for
#endif
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/regex.hpp>
#include <util/error.hpp>
#if USE_BOOST_REGEX
// ----------------------------------------------------------------------------- : Regex : boost
void Regex::assign(const String& code) {
// compile string
try {
regex.assign(code.begin(),code.end());
} catch (const boost::regex_error& e) {
/// TODO: be more precise
throw ScriptError(String::Format(_("Error while compiling regular expression: '%s'\nAt position: %d\n%s"),
code.c_str(), e.position(), String(e.what(), IF_UNICODE(wxConvUTF8,String::npos))));
}
}
void Regex::replace_all(String* input, const String& format) {
//std::basic_string<Char> fmt; format_string(format,fmt);
std::basic_string<Char> fmt(format.begin(),format.end());
String output;
regex_replace(insert_iterator<String>(output, output.end()),
input->begin(), input->end(), regex, fmt, boost::format_sed);
*input = output;
}
#else // USE_BOOST_REGEX
// ----------------------------------------------------------------------------- : Regex : wx
void Regex::assign(const String& code) {
// compile string
if (!regex.Compile(code, wxRE_ADVANCED)) {
throw ScriptError(_("Error while compiling regular expression: '") + code + _("'"));
}
assert(regex.IsValid());
}
#endif // USE_BOOST_REGEX
// ----------------------------------------------------------------------------- : Regex : common
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_UTIL_REGEX
#define HEADER_UTIL_REGEX
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// Use boost::regex as opposed to wxRegex
/* 2008-09-01:
* Script profiling shows that the boost library is significantly faster:
* When loading a large magic set (which calls ScriptManager::updateAll):
* function Calls wxRegEx boost
* ------------------------------------------------------------------
* replace 3791 0.38607 0.20857
* filter_text 11 0.32251 0.02446
*
* (times are avarage over all calls, in ms)
*/
#define USE_BOOST_REGEX 1
#if USE_BOOST_REGEX
#include <boost/regex.hpp>
#include <boost/regex/pattern_except.hpp>
#endif
// ----------------------------------------------------------------------------- : Boost implementation
#if USE_BOOST_REGEX
/// Our own regular expression wrapper
/** Suppors both boost::regex and wxRegEx.
* Has an interface like boost::regex, but compatible with wxStrings.
*/
class Regex {
public:
struct Results : public boost::match_results<const Char*> {
/// Get a sub match
inline String str(int sub = 0) const {
const_reference v = (*this)[sub];
return String(v.first, v.second);
}
/// Format a replacement string
inline String format(const String& format) const {
std::basic_string<Char> fmt(format.begin(),format.end());
String output;
boost::match_results<const Char*>::format(
insert_iterator<String>(output, output.end()), fmt, boost::format_sed);
return output;
}
};
void assign(const String& code);
inline bool matches(const String& str) const {
return regex_search(str.begin(), str.end(), regex);
}
inline bool matches(Results& results, const String& str) const {
return regex_search(str.begin(), str.end(), results, regex);
}
inline bool matches(Results& results, const Char* begin, const Char* end) const {
return regex_search(begin, end, results, regex);
}
void replace_all(String* input, const String& format);
inline bool empty() const {
return regex.empty();
}
private:
boost::basic_regex<Char> regex; ///< The regular expression
};
// ----------------------------------------------------------------------------- : Wx implementation
#else
/// Our own regular expression wrapper
/** Suppors both boost::regex and wxRegEx.
* Has an interface like boost::regex.
*/
class Regex
public:
// Interface for compatability with boost::regex
class Results {
public:
typedef pair<const Char*,const Char*> value_type; // (begin,end)
typedef value_type const_reference;
/// Number of submatches (+1 for the total match)
inline size_t size() const { return regex->GetMatchCount(); }
/// Get a submatch
inline value_type operator [] (int sub) const {
size_t pos, length;
bool ok = regex->GetMatch(&pos, &length, sub);
assert(ok);
return make_pair(begin + pos, begin + pos + length);
}
inline size_t position(int sub = 0) const {
size_t pos, length;
bool ok = regex->GetMatch(&pos, &length, sub);
assert(ok);
return pos;
}
inline size_t length(int sub = 0) const {
size_t pos, length;
bool ok = regex->GetMatch(&pos, &length, sub);
assert(ok);
return length;
}
/// Get a sub match
inline String str(int sub = 0) const {
const_reference v = (*this)[sub];
return String(v.first, v.second);
}
/// Format a replacement string
inline String format(const String& format) const {
const_reference v = (*this)[0];
String inside(v.first, v.second);
regex->ReplaceFirst(&inside, format);
return inside;
}
private:
wxRegEx* regex;
const Char* begin;
friend class ScriptRegex;
};
void assign(const String& code);
inline bool matches(const String& str) const {
return regex.Matches(str);
}
inline bool matches(Results& results, const String& str) const {
results.regex = &regex;
results.begin = str.begin();
return regex.Matches(str);
}
inline bool matches(Results& results, const Char* begin, const Char* end) const {
results.regex = &regex;
results.begin = begin;
return regex.Matches(begin, 0, end - begin);
}
inline void replace_all(String* input, const String& format) {
regex.Replace(input, format);
}
inline bool empty() const {
return !regex.IsValid();
}
private:
wxRegEx regex; ///< The regular expression
};
#endif
// ----------------------------------------------------------------------------- : EOF
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment