Skip to content

Commit 994ae02

Browse files
committed
Refactoring: Use Compile Time Regular Expressions instead of reg::Ex
1 parent 8a4c626 commit 994ae02

23 files changed

+6244
-564
lines changed

ctre/ctre.hpp

Lines changed: 5735 additions & 0 deletions
Large diffs are not rendered by default.

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
include_directories(
44
${PROJECT_SOURCE_DIR}/filesystem
5+
${PROJECT_SOURCE_DIR}/ctre
56
${PROJECT_SOURCE_DIR}/libmd5
67
${PROJECT_SOURCE_DIR}/liblodepng
78
${PROJECT_SOURCE_DIR}/libmscgen

src/configimpl.l

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
#include <iostream>
3333
#include <iomanip>
3434

35+
#include <ctre.hpp>
36+
3537
#include "config.h"
36-
#include "regex.h"
3738
#include "configimpl.h"
3839
#include "version.h"
3940
#include "portable.h"
@@ -1354,43 +1355,44 @@ void ConfigImpl::emptyValueToDefault()
13541355
}
13551356
}
13561357

1357-
static const reg::Ex reEnvVar(R"(\$\((\a[\w.-]*)\))"); // e.g. $(HOME)
1358-
static const reg::Ex reEnvVarExt(R"(\$\((\a[\w.-]*\(\a[\w.-]*\))\))"); // e.g. $(PROGRAMFILES(X86))
1359-
static const reg::Ex reEnvVarCMake(R"(@\a\w*@)"); // CMake type replacement
1358+
static constexpr auto reEnvVar = ctll::fixed_string{ R"(\$\(([[:alpha:]_][[:word:]\.\-]*)\))" }; // e.g. $(HOME)
1359+
static constexpr auto reEnvVarExt = ctll::fixed_string{ R"(\$\(([[:alpha:]_][[:word:]\.\-]*\([[:alpha:]_][[:word:]\.\-]*\))\))" }; // e.g. $(PROGRAMFILES(X86))
1360+
static constexpr auto reEnvVarCMake = ctll::fixed_string{ R"(@[[:alpha:]_][[:word:]]*@)" }; // CMake type replacement
13601361

13611362
static bool containsEnvVar(QCString &str)
13621363
{
1363-
reg::Match m;
13641364
std::string s = str.str();
1365-
return reg::search(s,m,reEnvVar) || reg::search(s,m,reEnvVarExt) || reg::search(s,m,reEnvVarCMake);
1365+
return ctre::search<reEnvVar>(s) || ctre::search<reEnvVarExt>(s) || ctre::search<reEnvVarCMake>(s);
13661366
}
13671367

13681368
static void substEnvVarsInString(QCString &str)
13691369
{
13701370
if (str.isEmpty()) return;
1371-
auto replace = [](const std::string &s, const reg::Ex &re) -> std::string
1372-
{
1373-
reg::Iterator it(s,re);
1374-
reg::Iterator end;
1375-
std::string result;
1376-
size_t p = 0;
1377-
for (; it!=end ; ++it)
1378-
{
1379-
const auto &match = *it;
1380-
size_t i = match.position();
1381-
size_t l = match.length();
1382-
result+=s.substr(p,i-p);
1383-
std::string matchContents = match[1].str();
1384-
QCString env=Portable::getenv(matchContents.c_str()); // get content of $(..) match
1385-
substEnvVarsInString(env); // recursively expand variables if needed.
1386-
result+=env.str();
1387-
p=i+l;
1388-
}
1389-
result+=s.substr(p);
1390-
return result;
1391-
};
13921371

1393-
str = QCString(replace(replace(str.str(),reEnvVar),reEnvVarExt)).stripWhiteSpace();
1372+
// Note: need C++20 to templatize the lambdas, for now use a macro
1373+
#define ENV_REPLACE(name,reType) \
1374+
auto name = [](const std::string &s) -> std::string \
1375+
{ \
1376+
size_t p=0; \
1377+
std::string result; \
1378+
for (auto match : ctre::range<reType>(s)) \
1379+
{ \
1380+
auto &&m1 = match.get<1>(); \
1381+
size_t i = match.begin()-s.begin(); \
1382+
result += s.substr(p,i-p); \
1383+
QCString env = Portable::getenv(std::string(m1)); \
1384+
substEnvVarsInString(env); \
1385+
result += env.str(); \
1386+
p = i + match.size(); \
1387+
} \
1388+
result += s.substr(p); \
1389+
return result; \
1390+
}
1391+
1392+
ENV_REPLACE(env_replace,reEnvVar);
1393+
ENV_REPLACE(env_replace_ext,reEnvVarExt);
1394+
1395+
str = QCString(env_replace(env_replace_ext(str.str()))).stripWhiteSpace();
13941396
}
13951397

13961398
static void substEnvVarsInStrList(StringVector &sl)
@@ -1911,9 +1913,8 @@ void Config::checkAndCorrect(bool quiet, const bool check)
19111913
for (const auto &alias : aliasList)
19121914
{
19131915
// match aliases of the form re1='name=' and re2='name{2} ='
1914-
static const reg::Ex re1(R"(^\a[\w-]*\s*=)");
1915-
static const reg::Ex re2(R"(^\a[\w-]*{\d+}\s*=)");
1916-
if (!reg::search(alias,re1) && !reg::search(alias,re2))
1916+
static constexpr auto re = ctll::fixed_string{ R"(^[[:alpha:]_][[:word:]\-]*(\{\d+\})?\s*=)" };
1917+
if (!ctre::search<re>(alias))
19171918
{
19181919
err("Illegal ALIASES format '%s'. Use \"name=value\" or \"name{n}=value\", where n is the number of arguments\n",
19191920
alias.c_str());

src/datetime.cpp

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#include <array>
2020
#include <functional>
2121

22-
#include "regex.h"
22+
#include "ctre.hpp"
23+
2324
#include "datetime.h"
2425
#include "config.h"
2526
#include "portable.h"
@@ -79,45 +80,6 @@ QCString yearToString()
7980
return QCString().setNum(current.tm_year+1900);
8081
}
8182

82-
struct SpecFormat
83-
{
84-
const reg::Ex re;
85-
int count;
86-
int offset;
87-
int format;
88-
};
89-
90-
using TMFieldAssigner = std::function< void(std::tm &,int value) >;
91-
92-
struct DateTimeField
93-
{
94-
TMFieldAssigner assigner;
95-
int minVal;
96-
int maxVal;
97-
const char *name;
98-
};
99-
100-
static std::array<SpecFormat,5> g_specFormats
101-
{{
102-
// regular expression, num values, offset, format bits
103-
{ std::string(R"((\d+)-(\d+)-(\d+)\s*(\d+):(\d+):(\d+))"), 6, 0, SF_Date|SF_Time|SF_Seconds }, // format 13-04-2015 12:34:56
104-
{ std::string(R"((\d+)-(\d+)-(\d+)\s*(\d+):(\d+))"), 5, 0, SF_Date|SF_Time }, // format 13-04-2015 12:34
105-
{ std::string(R"((\d+)-(\d+)-(\d+))"), 3, 0, SF_Date }, // format 13-04-2015
106-
{ std::string(R"((\d+):(\d+):(\d+))"), 3, 3, SF_Time|SF_Seconds }, // format 12:34:56
107-
{ std::string(R"((\d+):(\d+))"), 2, 3, SF_Time } // format 12:34
108-
}};
109-
110-
static std::array<DateTimeField,6> g_assignValues
111-
{{
112-
// assigner, minVal, maxVal, name
113-
{ [](std::tm &tm,int value) { tm.tm_year = value-1900; }, 1900, 9999, "year" },
114-
{ [](std::tm &tm,int value) { tm.tm_mon = value-1; }, 1, 12, "month" },
115-
{ [](std::tm &tm,int value) { tm.tm_mday = value; }, 1, 31, "day" },
116-
{ [](std::tm &tm,int value) { tm.tm_hour = value; }, 0, 23, "hour" },
117-
{ [](std::tm &tm,int value) { tm.tm_min = value; }, 0, 59, "minute" },
118-
{ [](std::tm &tm,int value) { tm.tm_sec = value; }, 0, 59, "second" }
119-
}};
120-
12183
static void determine_weekday( std::tm& tm )
12284
{
12385
auto cpy = tm;
@@ -133,6 +95,16 @@ static void determine_weekday( std::tm& tm )
13395

13496
QCString dateTimeFromString(const QCString &spec,std::tm &dt,int &format)
13597
{
98+
struct DateTimeField
99+
{
100+
using TMFieldAssigner = std::function< void(std::tm &,int value) >;
101+
std::string value;
102+
TMFieldAssigner assigner;
103+
int minVal;
104+
int maxVal;
105+
const char *name;
106+
};
107+
136108
// for an empty spec field return the current date and time
137109
dt = getCurrentDateTime();
138110
if (spec.isEmpty())
@@ -142,30 +114,43 @@ QCString dateTimeFromString(const QCString &spec,std::tm &dt,int &format)
142114
}
143115

144116
// find a matching pattern
145-
std::string s = spec.str();
146-
for (const auto &fmt : g_specFormats)
117+
static constexpr auto re = ctll::fixed_string{ R"(((\d+)-(\d+)-(\d+))?\s*((\d+):(\d+)(:(\d+))?)?)" };
118+
auto&& [ match, dateOpt, yearStr, monthStr, dayStr, timeOpt, hourStr, minStr, secOpt, secStr ] = ctre::match<re>(spec.str());
119+
if (match)
147120
{
148-
reg::Match m;
149-
if (reg::match(s,m,fmt.re)) // match found
121+
if (dateOpt) format|=SF_Date;
122+
if (timeOpt) format|=SF_Time;
123+
if (secOpt) format|=SF_Seconds;
124+
125+
const std::array<DateTimeField,6> values
126+
{{
127+
// value, assigner, minVal, maxVal, name
128+
{ yearStr.str(), [](std::tm &tm,int value) { tm.tm_year = value-1900; }, 1900, 9999, "year" },
129+
{ monthStr.str(), [](std::tm &tm,int value) { tm.tm_mon = value-1; }, 1, 12, "month" },
130+
{ dayStr.str(), [](std::tm &tm,int value) { tm.tm_mday = value; }, 1, 31, "day" },
131+
{ hourStr.str(), [](std::tm &tm,int value) { tm.tm_hour = value; }, 0, 23, "hour" },
132+
{ minStr.str(), [](std::tm &tm,int value) { tm.tm_min = value; }, 0, 59, "minute" },
133+
{ secStr.str(), [](std::tm &tm,int value) { tm.tm_sec = value; }, 0, 59, "second" }
134+
}};
135+
136+
for (const auto &dtf : values)
150137
{
151-
for (int i=0; i<fmt.count; i++)
138+
if (!dtf.value.empty()) // value is specified
152139
{
153-
int value = std::atoi(m[i+1].str().c_str());
154-
const DateTimeField &dtf = g_assignValues[i+fmt.offset];
155-
if (value<dtf.minVal || value>dtf.maxVal) // check if the value is in the expected range
140+
int value = std::atoi(dtf.value.c_str());
141+
if (value<dtf.minVal || value>dtf.maxVal) // value out of range
156142
{
157143
return QCString().sprintf("value for %s is %d which is outside of the value range [%d..%d]",
158144
dtf.name, value, dtf.minVal, dtf.maxVal);
159145
}
160146
dtf.assigner(dt,value);
161147
}
162-
format = fmt.format;
163-
if (format&SF_Date) // if we have a date also determine the weekday
164-
{
165-
determine_weekday(dt);
166-
}
167-
return QCString();
168148
}
149+
if (dateOpt)
150+
{
151+
determine_weekday(dt);
152+
}
153+
return QCString();
169154
}
170155

171156
// no matching pattern found

src/docparser.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include <ctype.h>
2121

22+
#include "ctre.hpp"
23+
2224
#include "classlist.h"
2325
#include "cmdmapper.h"
2426
#include "config.h"
@@ -230,12 +232,9 @@ void DocParser::checkArgumentName()
230232
//printf("isDocsForDefinition()=%d\n",context.memberDef->isDocsForDefinition());
231233
if (al.empty()) return; // no argument list
232234

233-
static const reg::Ex re(R"(\$?\w+\.*)");
234-
reg::Iterator it(name,re);
235-
reg::Iterator end;
236-
for (; it!=end ; ++it)
235+
static constexpr auto re = ctll::fixed_string{ R"(\$?\w+\.*)" };
236+
for (auto match : ctre::range<re>(name))
237237
{
238-
const auto &match = *it;
239238
QCString aName=match.str();
240239
if (lang==SrcLangExt_Fortran) aName=aName.lower();
241240
//printf("aName='%s'\n",qPrint(aName));
@@ -594,9 +593,9 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
594593
tok!=TK_ENDLIST
595594
)
596595
{
597-
static const reg::Ex specialChar(R"([.,|()\[\]:;?])");
596+
static constexpr auto specialChar = ctll::fixed_string{ R"([.,|()\[\]:;?])" };
598597
if (tok==TK_WORD && context.token->name.length()==1 &&
599-
reg::match(context.token->name.str(),specialChar))
598+
ctre::match<specialChar>(context.token->name.str()))
600599
{
601600
// special character that ends the markup command
602601
return tok;

src/doctokenizer.l

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ typedef yyguts_t *yyscan_t;
3535
#include <string>
3636
#include <cassert>
3737

38+
#include "ctre.hpp"
39+
3840
#include "doctokenizer.h"
3941
#include "cmdmapper.h"
4042
#include "config.h"
@@ -45,7 +47,6 @@ typedef yyguts_t *yyscan_t;
4547
#include "doxygen.h"
4648
#include "portable.h"
4749
#include "cite.h"
48-
#include "regex.h"
4950

5051
#define YY_NO_INPUT 1
5152
#define YY_NO_UNISTD_H 1
@@ -425,14 +426,13 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
425426
{
426427
lineCount(yytext,yyleng);
427428
std::string text(yytext);
428-
static const reg::Ex re(R"([*+][^*+]*$)"); // find last + or *
429-
reg::Match match;
430-
reg::search(text,match,re);
431-
size_t listPos = match.position();
432-
assert(listPos!=std::string::npos);
429+
static constexpr auto re = ctll::fixed_string{ R"([*+][^*+]*$)" }; // find last + or *
430+
auto match = ctre::search<re>(text);
431+
assert(match);
432+
size_t markPos = match.begin()-text.begin();
433433
yyextra->token->isEnumList = FALSE;
434434
yyextra->token->id = -1;
435-
yyextra->token->indent = computeIndent(yytext,listPos);
435+
yyextra->token->indent = computeIndent(yytext,markPos);
436436
return TK_LISTITEM;
437437
}
438438
}
@@ -444,11 +444,10 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
444444
else
445445
{
446446
std::string text(yytext);
447-
static const reg::Ex re(R"(\d+)");
448-
reg::Match match;
449-
reg::search(text,match,re);
450-
size_t markPos = match.position();
451-
assert(markPos!=std::string::npos);
447+
static constexpr auto re = ctll::fixed_string{ R"(\d+)" };
448+
auto match = ctre::search<re>(text);
449+
assert(match);
450+
size_t markPos = match.begin()-text.begin();
452451
yyextra->token->isEnumList = true;
453452
bool ok = false;
454453
int id = QCString(match.str()).toInt(&ok);
@@ -480,11 +479,10 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
480479
{
481480
lineCount(yytext,yyleng);
482481
std::string text=extractPartAfterNewLine(QCString(yytext)).str();
483-
static const reg::Ex re(R"([*+][^*+]*$)"); // find last + or *
484-
reg::Match match;
485-
reg::search(text,match,re);
486-
size_t markPos = match.position();
487-
assert(markPos!=std::string::npos);
482+
static constexpr auto re = ctll::fixed_string{ R"([*+][^*+]*$)" }; // find last + or *
483+
auto match = ctre::search<re>(text);
484+
assert(match);
485+
size_t markPos = match.begin()-text.begin();
488486
yyextra->token->isEnumList = FALSE;
489487
yyextra->token->id = -1;
490488
yyextra->token->indent = computeIndent(text.c_str(),markPos);
@@ -500,11 +498,10 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
500498
{
501499
lineCount(yytext,yyleng);
502500
std::string text=extractPartAfterNewLine(QCString(yytext)).str();
503-
static const reg::Ex re(R"(\d+)");
504-
reg::Match match;
505-
reg::search(text,match,re);
506-
size_t markPos = match.position();
507-
assert(markPos!=std::string::npos);
501+
static constexpr auto re = ctll::fixed_string{ R"(\d+)" };
502+
auto match = ctre::search<re>(text);
503+
assert(match);
504+
size_t markPos = match.begin()-text.begin();
508505
yyextra->token->isEnumList = true;
509506
bool ok = false;
510507
int id = QCString(match.str()).toInt(&ok);

0 commit comments

Comments
 (0)