With my growing constexpr fascination, I thought of using it for something that would be really hard using template meta-programs. How about implementing a compile-time regular expression matcher? Fortunately, a simple regular expression matcher has already been written by Rob Pike. I just rewrote it using constexpr: single return statement in functions, no modifications to the parameters, abundant ternery operators, and recursion. Here we go...
To compile it, as of today, you need g++ 4.6 or better. You've to pass REGEX and TEXT as #defines while compilation. For instance, -D REGEX=o$ -D TEXT=Foo It matches!
I used two macros TO_STR and To_STR_IMPL to convert the REGEX and TEXT into string literals. #R is basically using the preprocessor stringification technique. For some reason I need two separate TO_STR macros for TEXT substitution and stringification. Seems like the gcc preprocessor can't do those two things in a single macro.
Have fun!
constexpr int match_c(const char *regexp, const char *text);
constexpr int matchhere_c(const char *regexp, const char *text);
constexpr int matchstar_c(int c, const char *regexp, const char *text);
constexpr int matchend_c(const char * regexp, const char * text);
constexpr int matchend_c(const char * regexp, const char * text)
{
return matchhere_c(regexp, text) ? 1 :
(*text == '\0') ? 0 : matchend_c(regexp, text+1);
}
constexpr int match_c(const char *regexp, const char *text)
{
return (regexp[0] == '^') ? matchhere_c(regexp+1, text) :
matchend_c(regexp, text);
}
/* matchhere: search for regexp at beginning of text */
constexpr int matchhere_c(const char *regexp, const char *text)
{
return (regexp[0] == '\0') ? 1 :
(regexp[1] == '*') ? matchstar_c(regexp[0], regexp+2, text) :
(regexp[0] == '$' && regexp[1] == '\0') ? (*text == '\0') :
(*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) ?
matchhere_c(regexp+1, text+1) : 0;
}
/* matchstar: search for c*regexp at beginning of text */
constexpr int matchstar_c(int c, const char * regexp, const char *text)
{
return matchhere_c(regexp, text) ? 1 :
(*text != '\0' && (*text == c || c == '.')) ?
matchstar_c(c, regexp, text+1) : 0;
}
#define TO_STR_IMPL(R) #R
#define TO_STR(R) TO_STR_IMPL(R)
int main(void)
{
static_assert(match_c(TO_STR(REGEX), TO_STR(TEXT)), "...");
return 0;
}
To compile it, as of today, you need g++ 4.6 or better. You've to pass REGEX and TEXT as #defines while compilation. For instance, -D REGEX=o$ -D TEXT=Foo It matches!
I used two macros TO_STR and To_STR_IMPL to convert the REGEX and TEXT into string literals. #R is basically using the preprocessor stringification technique. For some reason I need two separate TO_STR macros for TEXT substitution and stringification. Seems like the gcc preprocessor can't do those two things in a single macro.
Have fun!
Comments
Perhaps I am missing something though.
Think of parsing XPath and SQL queries for syntactical correctness. An XML data-binding tool can check for constraints specified in xsd when XML object is created from string literals. For instace, checking a phone number has two dashes and 10 digits. You don't have to wait till you get a run-time exception. A compiler can find out a typo in a string literal!
Another example could be a library that may require (or not require) absolute path for a file and checks that at compile-time. I think it is possible to design ifstream constructor to cause compilation failure if an empty string literal is passed as a filename.
tryWao wat a sueful information given
<a href="/examandlearning.in></a>
of a particular word?
To quote the standard: "A constant-expression function cannot be called before it is
defined."
But eg. in matchend_c() you do exactly this: You call matchhere_c() before matchhere_c() is defined. You have just declared it at that point.