2

I have a lot of rules that have common prefix and suffix:

rule = begin_stuff >> some >> other >> stuff >> end_stuff.

(where begin_stuff and end_stuff are composed from literals)

I wanted to be able to say

 rule = wrapped(some >> other >> stuff);

I tried something along the lines of

  template<typename Rule> Rule wrapped(Rule inside) 
  {
    Rule result;
    result = begin_stuff >> inside >> end_stuff;
    return result;
  }

but all I get is lots of compile-time assertions from Qi.

How can I refactor Spirit rules in this fashion?

2 Answers2

2

I think you are looking for 'subrules' (that Spirit V1/classical used to have). These are obsolete now.

Have a look at

  • c++11 auto and BOOST_AUTO

    auto subexpression = int_ >> ',' >> double_;
    qi::rule<It> rule  = "A:" >> subexpression >> "Rest:" >> (subexpression % eol);
    

    There used to be issues with using auto on Spirit rules (notably with MSVC) (see Zero to 60 MPH in 2 seconds! and comments) but I have been informed this (soon) is no longer an issue:

    Yep. Anyway,FYI, it's fixed in Spirit-3. You can 
    use auto all you want. 
    
    Regards, 
    -- 
    Joel de Guzman
    
  • qi::lazy

  • inherited arguments - introduced in the Mini XML - ASTs tutorial

Here is a proof of concept that passes a common subrule to different 'compound' rules to allow for wrapping in (), [] or {}:

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

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

typedef std::string::const_iterator It;

template <typename R>
    void test(const std::string& input, R const& rule)
{
    It f(input.begin()), l(input.end());
    bool ok = qi::phrase_parse(f,l,rule,qi::space);
    std::cout << "'" << input << "'\tparse " << (ok?"success":"failure") << "\n";
}

int main()
{
    typedef qi::rule<It,                    qi::space_type> common_rule;
    typedef qi::rule<It, void(common_rule), qi::space_type> compound_rule;

    common_rule common = qi::int_;

    compound_rule 
        in_parens   = qi::lit('(') >> qi::_r1 >> ')',
        in_brackets = qi::lit('[') >> qi::_r1 >> ']',
        in_braces   = qi::lit('{') >> qi::_r1 >> '}';

    test("{ 231 }"  , in_braces  (phx::ref(common )) );
    test("{ hello }", in_braces  (phx::val("hello")) );

    test("( 231 )"  , in_parens  (phx::ref(common )) );
    test("( hello )", in_parens  (phx::val("hello")) );

    test("[ 231 ]"  , in_brackets(phx::ref(common )) );
    test("[ hello ]", in_brackets(phx::val("hello")) );
}

Output:

'{ 231 }'   parse success
'{ hello }' parse success
'( 231 )'   parse success
'( hello )' parse success
'[ 231 ]'   parse success
'[ hello ]' parse success

PS. Note the above is not a typical Spirit grammar. This way doesn't play too well when the 'common' rule would expose different attributes.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • sehe, thanks for could you answer. Could you finish it? The last word "Also" tells me you had more to say... –  Nov 16 '12 at 13:34
  • @Arkadiy I don't remember anything specific I was going to say. Except maybe the thing about 'auto' that I did add. Cheers – sehe Nov 16 '12 at 15:18
  • Question: how do we get from val("hello") to common_rule? –  Nov 16 '12 at 19:42
  • @Arkadiy Erm, the compiler does the conversions. In precisely the same way as `qi::rule hello_rule = "hello";` - i.e. the string literal `"hello"` is implicitely treated as `qi::lit("hello")`. [The `phx::val` is just there to make this a lazy value.] – sehe Nov 16 '12 at 20:03
  • What I don't get is the fact that compound rule has `void(common_rule)` in it, and yet we're passing a string (wrapped in val). Could we pass a parser? –  Nov 19 '12 at 14:36
  • @Arkadiy That is the whole point. `common_rule` is just a typedef for a specif `qi::rule<>`. Yes, of course, you can pass a parser. Which is what we _do_. For this reason I demonstrated it by passing a parser byref (`phx::ref(common)`) and a parser byval (`phx::val("hello")`). As you can see I tried to be comprehensive by showing _different_ rules, but you can just as easily pass `phx::val(uint_)` or even `phx::val('{' >> qi::double_ % ';' >> '}')` – sehe Nov 19 '12 at 16:13
  • The example you provided compiles fine for me however the output I get is: `'{ 231 }' parse failure` `'{ hello }' parse failure` `'( 231 )' parse failure` `'( hello )' parse failure` Any idea what that might be caused by? – naderman May 02 '13 at 14:45
  • Funny, seems to work if I wrap the qi::_r1 in qi::lazy() - not sure why. – naderman May 02 '13 at 15:04
  • @naderman I won't be near a computer to test/fix this in a while. However in the mean time I have a hunch that there is UB involved, in which case using qi::lazy effectively only _masks_ the symptom for your program. The problem would be references to temps being held inside expression templates for _r1. See here for dissection of a similar situation that exhibited this particular problem http://stackoverflow.com/questions/16177184/generating-spirit-parser-expressions-from-a-variadic-list-of-alternative-parser/16181550#16181550 (and how I "fixed" it in that case...) – sehe May 02 '13 at 22:55
1

I think you need to use the Qi Confix Parser Derective from Spirit Repository. It is exactly what you need.

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169