Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7532725
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 30, 20262026-05-30T05:32:14+00:00 2026-05-30T05:32:14+00:00

Part of a simple skeleton utility I’m hacking on I have a grammar for

  • 0

Part of a simple skeleton utility I’m hacking on I have a grammar for triggering substitutions in text. I thought it a wonderful way to get comfortable with Boost.Spirit, but the template errors are a joy of a unique kind.

Here is the code in its entirety:

#include <iostream>
#include <iterator>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace bsq = boost::spirit::qi;

namespace {
template<typename Iterator>
struct skel_grammar : public bsq::grammar<Iterator> {
    skel_grammar();

private:
    bsq::rule<Iterator> macro_b;
    bsq::rule<Iterator> macro_e;
    bsq::rule<Iterator, bsq::ascii::space_type> id;
    bsq::rule<Iterator> macro;
    bsq::rule<Iterator> text;
    bsq::rule<Iterator> start;
};

template<typename Iterator>
skel_grammar<Iterator>::skel_grammar() : skel_grammar::base_type(start)
{
    text = bsq::no_skip[+(bsq::char_ - macro_b)[bsq::_val += bsq::_1]];
    macro_b = bsq::lit("<<");
    macro_e = bsq::lit(">>");
    macro %= macro_b >> id >> macro_e;
    id %= -(bsq::ascii::alpha | bsq::char_('_'))
        >> +(bsq::ascii::alnum | bsq::char_('_'));
    start = *(text | macro);
}
}  // namespace

int main(int argc, char* argv[])
{
    std::string input((std::istreambuf_iterator<char>(std::cin)),
                      std::istreambuf_iterator<char>());
    skel_grammar<std::string::iterator> grammar;
    bool r = bsq::parse(input.begin(), input.end(), grammar);
    std::cout << std::boolalpha << r << '\n';
    return 0;
}

What’s wrong with this code?

  • 1 1 Answer
  • 2 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-30T05:32:15+00:00Added an answer on May 30, 2026 at 5:32 am

    Mmm. I feel that we have discussed a few more details in chat than have been reflected in the question as it is.

    Let me entertain you with my ‘toy’ implementation, complete with test cases, of a grammar that will recognize <<macros>> like this, including nested expansion of the same.

    Notable features:

    1. Expansion is done using a callback (process()), giving you maximum flexibility (you could use a look up table, cause parsing to fail depending on the macro content, or even have sideeffects independent of the output
    2. the parser is optimized to favour streaming mode. Look at spirit::istream_iterator on how to parse input in streaming mode (Stream-based Parsing Made Easy). This has the obvious benefits if your input stream is 10 GB, and contains only 4 macros – it is the difference between crawling performance (or running out of memory) and just scaling.
      • note that the demo still writes to a string buffer (via oss). You could, however, easily, hook the output directly to std::cout or, say, an std::ofstream instance
    3. Expansion is done eagerly, so you can have nifty effects using indirect macros. See the testcases
    4. I even demoed a simplistic way to support escaping the << or >> delimiters (#define SUPPORT_ESCAPES)

    Without further ado:

    The Code

    Note due to laziness, I require -std==c++0x, but only when SUPPORT_ESCAPES is defined

    //#define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    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; // fail the parse
            }
    
            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 + ">>"; // this makes the unsupported macros appear unchanged
            }
    
            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
                {
    #ifndef SUPPORT_ESCAPES
                    o = std::copy(r.begin(),r.end(),o);
    #else
                    auto f = std::begin(r), l = std::end(r);
                    while(f!=l)
                    {
                        if (('\\'==*f) && (l == ++f))
                            break;
                        *o++ = *f++;
                    }
    #endif
                    return true; // false to fail the parse
                }
            } copy;
    
            skel_grammar(OutIt& out) : skel_grammar::base_type(start)
            {
                using namespace qi;
    
    #ifdef SUPPORT_ESCAPES
                rawch = ('\\' >> char_) | char_;
    #else
    #           define rawch qi::char_
    #endif
    
                macro = ("<<" >> (
                               (*(rawch - ">>" - "<<") [ _val += _1 ]) 
                             % macro                   [ _val += _1 ] // allow nests
                          ) >> 
                          ">>")  
                    [ _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);
    
    
    #           undef rawch
            }
    
            private:
    #ifdef SUPPORT_ESCAPES
            qi::rule<Iterator, char()> rawch;
    #endif
            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"
    #ifdef SUPPORT_ESCAPES // bonus: escapes
            "You can escape \\<<hello>> (not expanded to '<<hello>>')\n"
            "Demonstrate how it <<avoids <\\<nesting\\>> macros>>.\n"
    #endif
            ;
    
        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;
    }
    

    The Test Output

    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>>.
    

    There is quite a lot of functionality hidden there to grok. I suggest you look at the test cases and the process() callback alongside each other to see what is going on.

    Cheers & HTH 🙂

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a simple grammar that works for the most part, but at one
I have to write a program that sniffs network packets (part1-the simple part). And
I have a simple model class (Part), which pulls from it's information from a
This is probably a simple misunderstanding on my part. Have a simple interface: public
I have a very simple web part. I have a single grid view, which
What I have? I have a simple web part which has a Table .
I have a visual web part (a simple form) with RequiredFieldValidators. But a problem
I have the following 3 tables as part of a simple item tagging schema:
I have a simple custom web part with three drop downs that reads from
I have this simple code (part of a project) : void displayFileProperties(struct stat* file,char*

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.