Mmm. I feel that we have discussed several details in the chat, which were reflected in the question, as it is.
Let me entertain you with my implementation of “toys,” complete with test cases, of a grammar that recognizes <<macros>>how it is, including a nested extension of the same.
Known Features:
, .
escape-
<< >> (
#define SUPPORT_ESCAPES)
:
- , -std==c++0x, , SUPPORT_ESCAPES
namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;
namespace fsn= boost::fusion;
namespace
{
#define SUPPORT_ESCAPES
static bool process(std::string& macro)
{
if (macro == "error") {
return false;
}
if (macro == "hello") {
macro = "bye";
} else if (macro == "bye") {
macro = "We meet again";
} else if (macro == "sideeffect") {
std::cerr << "this is a side effect while parsing\n";
macro = "(done)";
} else if (std::string::npos != macro.find('~')) {
std::reverse(macro.begin(), macro.end());
macro.erase(std::remove(macro.begin(), macro.end(), '~'));
} else {
macro = std::string("<<") + macro + ">>";
}
return true;
}
template<typename Iterator, typename OutIt>
struct skel_grammar : public qi::grammar<Iterator>
{
struct fastfwd {
template<typename,typename> struct result { typedef bool type; };
template<typename R, typename O>
bool operator()(const R&r,O& o) const
{
o = std::copy(r.begin(),r.end(),o);
auto f = std::begin(r), l = std::end(r);
while(f!=l)
{
if (('\\'==*f) && (l == ++f))
break;
*o++ = *f++;
}
return true;
}
} copy;
skel_grammar(OutIt& out) : skel_grammar::base_type(start)
{
using namespace qi;
rawch = ('\\' >> char_) | char_;
macro = ("<<" >> (
(*(rawch - ">>" - "<<") [ _val += _1 ])
% macro [ _val += _1 ]
) >>
">>")
[ _pass = phx::bind(process, _val) ];
start =
raw [ +(rawch - "<<") ] [ _pass = phx::bind(copy, _1, phx::ref(out)) ]
% macro [ _pass = phx::bind(copy, _1, phx::ref(out)) ]
;
BOOST_SPIRIT_DEBUG_NODE(start);
BOOST_SPIRIT_DEBUG_NODE(macro);
}
private:
qi::rule<Iterator, char()> rawch;
qi::rule<Iterator, std::string()> macro;
qi::rule<Iterator> start;
};
}
int main(int argc, char* argv[])
{
std::string input =
"Greeting is <<hello>> world!\n"
"Side effects are <<sideeffect>> and <<other>> vars are untouched\n"
"Empty <<>> macros are ok, as are stray '>>' pairs.\n"
"<<nested <<macros>> (<<hello>>?) work>>\n"
"The order of expansion (evaluation) is _eager_: '<<<<hello>>>>' will expand to the same as '<<bye>>'\n"
"Lastly you can do algorithmic stuff too: <<!esrever ~ni <<hello>>>>\n"
"You can escape \\<<hello>> (not expanded to '<<hello>>')\n"
"Demonstrate how it <<avoids <\\<nesting\\>> macros>>.\n"
;
std::ostringstream oss;
std::ostream_iterator<char> out(oss);
skel_grammar<std::string::iterator, std::ostream_iterator<char> > grammar(out);
std::string::iterator f(input.begin()), l(input.end());
bool r = qi::parse(f, l, grammar);
std::cout << "parse result: " << (r?"success":"failure") << "\n";
if (f!=l)
std::cout << "unparsed remaining: '" << std::string(f,l) << "'\n";
std::cout << "Streamed output:\n\n" << oss.str() << '\n';
return 0;
}
-
this is a side effect while parsing
parse result: success
Streamed output:
Greeting is bye world!
Side effects are (done) and <<other>> vars are untouched
Empty <<>> macros are ok, as are stray '>>' pairs.
<<nested <<macros>> (bye?) work>>
The order of expansion (evaluation) is _eager_: 'We meet again' will expand to the same as 'We meet again'
Lastly you can do algorithmic stuff too: eyb in reverse!
You can escape <<hello>> (not expanded to 'bye')
Demonstrate how it <<avoids <<nesting>> macros>>.
, , . process() , , .
HTH:)