The best approach is to analyze it using a preprocessor. I believe that a preprocessor can be a very powerful tool for creating EDSL (embedded domain languages), but you must first understand the limitations of a preprocessor that parses things. A preprocessor can only analyze predefined tokens. Therefore, the syntax needs to be slightly changed by placing brackets around the expressions, and the FREEZE macro should also surround it (I just chose FREEZE, it could be called anything):
FREEZE(take(x*x) with(x, container))
Using this syntax, you can convert it to a preprocessor sequence (of course, using the Boost.Preprocessor library). After you use it as a preprocessor sequence, you can apply many algorithms to it to transform it as you like. A similar approach is done using the Linq library for C ++, where you can write this:
LINQ(from(x, numbers) where(x > 2) select(x * x))
Now, to convert to a pp sequence first, you need to identify the keywords you need to parse, for example:
#define KEYWORD(x) BOOST_PP_CAT(KEYWORD_, x) #define KEYWORD_take (take) #define KEYWORD_with (with)
So how does this work when you call KEYWORD(take(x*x) with(x, container)) , it will expand to (take)(x*x) with(x, container) , which is the first step to converting its in pp sequence. Now, to continue, we need to use the while construct from the Boost.Preprocessor library, but first we need to define some small macros to help us with this:
// Detects if the first token is parenthesis
This provides better detection of brackets and voids. And it provides a HEAD and TAIL macro that works slightly differently than BOOST_PP_SEQ_HEAD . (Boost.Preprocessor cannot process sequences that have vardiac parameters). Now here is how we can define the TO_SEQ macro that uses the while construct:
#define TO_SEQ(x) TO_SEQ_WHILE_M \ ( \ BOOST_PP_WHILE(TO_SEQ_WHILE_P, TO_SEQ_WHILE_O, (,x)) \ ) #define TO_SEQ_WHILE_P(r, state) TO_SEQ_P state #define TO_SEQ_WHILE_O(r, state) TO_SEQ_O state #define TO_SEQ_WHILE_M(state) TO_SEQ_M state #define TO_SEQ_P(prev, tail) BOOST_PP_NOT(IS_EMPTY(tail)) #define TO_SEQ_O(prev, tail) \ BOOST_PP_IF(IS_PAREN(tail), \ TO_SEQ_PAREN, \ TO_SEQ_KEYWORD \ )(prev, tail) #define TO_SEQ_PAREN(prev, tail) \ (prev (HEAD(tail)), TAIL(tail)) #define TO_SEQ_KEYWORD(prev, tail) \ TO_SEQ_REPLACE(prev, KEYWORD(tail)) #define TO_SEQ_REPLACE(prev, tail) \ (prev HEAD(tail), TAIL(tail)) #define TO_SEQ_M(prev, tail) prev
Now when you call TO_SEQ(take(x*x) with(x, container)) , you should get the sequence (take)((x*x))(with)((x, container)) .
Now this sequence is much easier to work with (due to the Boost.Preprocessor library). Now you can undo it, transform, filter, collapse, etc. It is extremely powerful and much more flexible than defining macros. For example, in the Linq library, the query from(x, numbers) where(x > 2) select(x * x) converted to these macros:
LINQ_WHERE(x, numbers)(x > 2) LINQ_SELECT(x, numbers)(x * x)
What are these macros, then it will generate a lambda for understanding the list, but it will work much more with them when it generates a lambda. You can do the same in your library, take(x*x) with(x, container) can be converted to something like this:
FREEZE_TAKE(x, container, x*x)
In addition, you do not define macros like take that invade global space.
Note. These macros here require the C99 preprocessor and therefore will not work in MSVC. (However, there are workarounds)