4

[It seems my explanations and expectations are not clear at all, so I added precision on how I'd like to use the feature at the end of the post]

I'm currently working on grammars using boost qi. I had a loop construction for a rule cause I needed to build it from the elements of a vector. I have re-written it with simple types, and it looks like:

#include <string>

// using boost 1.43.0
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_eps.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace bqi = boost::spirit::qi;

typedef const char* Iterator;

// function that you can find [here][1]
template<typename P> void test_phrase_parser(char const* input, P const& p, bool full_match = true);

int main()
{
    // my working rule type:
    bqi::rule<Iterator, std::string()> myLoopBuiltRule;
    std::vector<std::string> v;
    std::vector<std::string>::const_iterator iv;

    v.push_back("abc");
    v.push_back("def");
    v.push_back("ghi");
    v.push_back("jkl");

    myLoopBuiltRule = (! bqi::eps);
    for(iv = v.begin() ; iv != v.end() ; iv++)
    {
        myLoopBuiltRule =
                myLoopBuiltRule.copy()  [ bqi::_val = bqi::_1 ]
                | bqi::string(*iv)      [ bqi::_val = bqi::_1 ]
                ;
    }
    debug(myLoopBuiltRule);

    char s[] = "  abc ";

    test_phrase_parser(s, myLoopBuiltRule);
}

(Looks like here does not want to be replaced by corresponding hyperlink, so here is the address to find function test_phrase_parser(): http://www.boost.org/doc/libs/1_43_0/libs/spirit/doc/html/spirit/qi/reference/basics.html)

All was for the best in the best of all worlds... until I had to pass an argument to this rule. Here is the new rule type:

    // my not-anymore-working rule type:
    bqi::rule<Iterator, std::string(int*)> myLoopBuiltRule;

'int*' type is for example purpose only, my real pointer is adressing a much more complex class... but still a mere pointer.

I changed my 'for' loop accordingly, i.e.:

    for(iv = v.begin() ; iv != v.end() ; iv++)
    {
        myLoopBuiltRule =
                myLoopBuiltRule.copy()(bqi::_r1)    [ bqi::_val = bqi::_1 ]
                | bqi::string(*iv)      [ bqi::_val = bqi::_1 ]
                ;
    }

I had to add a new rule because test_phrase_parser() cannot guess which value is to be given to the int pointer:

bqi::rule<Iterator> myInitialRule;

And change everything that followed the for loop:

myInitialRule = myLoopBuiltRule((int*)NULL);

debug(myLoopBuiltRule);

char s[] = "  abc ";

test_phrase_parser(s, myInitialRule);

Then everything crashed:

/home/sylvain.darras/software/repository/software/external/include/boost/boost_1_43_0/boost/spirit/home/qi/nonterminal/rule.hpp:199: error: no matching function for call to ‘assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::operator=(const Expr&)

Then I got crazy and tried:

myLoopBuiltRule =
        myLoopBuiltRule.copy(bqi::_r1)  [ bqi::_val = bqi::_1 ]
        | bqi::string(*iv)      [ bqi::_val = bqi::_1 ]

-->

error: no matching function for call to ‘boost::spirit::qi::rule<const char*, std::string(int*), boost::fusion::unused_type, boost::fusion::unused_type, boost::fusion::unused_type>::copy(const boost::phoenix::actor<boost::spirit::attribute<1> >&)’

Then I got mad and wrote:

myLoopBuiltRule =
        myLoopBuiltRule(bqi::_r1)   [ bqi::_val = bqi::_1 ]
        | bqi::string(*iv)      [ bqi::_val = bqi::_1 ]

Which compiles since it is perfectly syntactically correct, but which magnificently stack overflows coz it happily, nicely, recursively, calls itself to death...

Then I lost my mind and typed:

myLoopBuiltRule =
        jf jhsgf jshdg fjsdgh fjsg jhsdg jhg sjfg jsgh df

Which, as you probably expect, has failed to compile.

You imagine that before writing the above novel, I checked out on the web, but didn't find out anything related to copy() and argument passing in the same time. Has anyone already experienced this problem ? Have I missed something ?

Be assured that any help will be really really appreciated.

PS: Great thanks to hkaiser who has, without knowing it, answered a lot of my boost::qi problems through google (but this one).


Further information:

The purpose of my parser is to read files written in a given language L. The purpose of my post is to propagate my "context" (i.e.: variable definitions and especially constant values, so I can compute expressions).

The number of variable types I handle is small, but it's bound to grow, so I keep these types in a container class. I can loop on these managed types.

So, let's consider a pseudo-algorithm of what I would like to achive:

LTypeList myTypes;
LTypeList::const_iterator iTypes;

bqi::rule<Iterator, LType(LContext*)> myLoopBuiltRule;

myLoopBuiltRule = (! bqi::eps);
for(iTypes = myTypes.begin() ; iTypes != myTypes.end() ; iTypes++)
{
    myLoopBuiltRule =
        myLoopBuiltRule.copy()(bqi::_r1)    [ bqi::_val = bqi::_1 ]
        | iTypes->getRule()(bqi::_r1)           [ bqi::_val = bqi::_1 ]
}

This is done during initialization and then myLoopBuiltRule is used and reused with different LContext*, parsing multiple types. And since some L types can have bounds, which are integer expressions, and that these integer expressions can exhibit constants, I (think that I) need my inherited attribute to take my LContext around and be able to compute expression value.

Hope I've been clearer in my intentions.

sehe
  • 374,641
  • 47
  • 450
  • 633
Sylv
  • 51
  • 6
  • Hmm. I have a feeling I might be missing some 'intrinsic complexity' (read: you have reduced your sample too well :)) but, what about: http://coliru.stacked-crooked.com/view?id=a57713ffa2c981b34857323ef570b4f6-f674c1a6d04c632b71a62362c0ccfc51 (compilation expires online, but you can run it) – sehe Jul 01 '13 at 22:03
  • 1
    First of all, thanks a lot for your help and the time you spend on my question. Unfortunately, when reading below answer and comments, I feel like a little kid talking to adults... I'm not sure I could solve my problem with given propositions cause it seems confusing to me. The solution I understood deals with qi::symbols but then not-sehe is right: my sample is too reduced... Here is the point: the loop-added `| bqi::string(*iv)` is in fact `| another_rule_N(bqi::_r1)`. :-( – Sylv Jul 02 '13 at 09:03
  • @Sylv I think this question could be very interesting but unfortunately I'm failing to grasp exactly what you want. Could you try providing a slightly less simplified example input and rules you want to compose? – llonesmiz Jul 02 '13 at 09:35
  • In that case I suggest `qi::symbols>` and replace the use of inherited attributes (`_r1`) with `phx::ref(parser_member_variable)`; The most conspicuous downside here would be that the parser may not be reused (and the rules aren't reentrant in the same way: there is no context stack for `parser_member_variable` like there would be for inherited/local attributes. – sehe Jul 02 '13 at 09:36
  • Did you see http://stackoverflow.com/questions/16177184/generating-spirit-parser-expressions-from-a-variadic-list-of-alternative-parser/16181550#16181550 I linked? It might be of interest. – sehe Jul 02 '13 at 10:01
  • @not-sehe: yes, I've read it, but I'm still not able to figure out where I should indicate my bqi::_r1 ? – Sylv Jul 02 '13 at 11:34
  • @Sylv until you clarify what _purpose_ the inherited attribute (`_r1`) serves, we will not be able to come up with alternatives. This means either **(a)** you can wait until someone does all the grunt work and comes up with some overly generic meta-programming to lift the existing limitations **(b)** you can take action in own hands and write a custom parser. Frankly, I don't recommend either, instead, encourage you to rethink the _functional requirements_ and parser design for a minute. Or several. Hope this helps – sehe Jul 02 '13 at 11:46
  • Oups, sorry, I edited my post by adding section "Further information" when you told me I might have reduced my sample too much, but forgot to tell you. Sorry again. I hope this additional piece of information will be helpful to understand my need of bqi::_r1. – Sylv Jul 02 '13 at 11:54
  • @Sylv I saw it. Read my question carefully. The italicized parts. – sehe Jul 02 '13 at 12:07
  • @not-sehe I'm not sure to understand exactly what's missing in the purpose or functional requirements (english is not my native language, as you might have noticed), but I'll try to be more precise: types can appear in many blocks of language L. Each one of these blocks is a class which inherits from `LContext` (which allows variable look up). Each block have its own context, searching in its own definitions and those obtained from its specific includes. When `myLoopBuiltRule` is called, I have no idea where it comes from, I can only rely on the `LContext` pointer. Have I answered ? – Sylv Jul 02 '13 at 12:38
  • 1
    Thank you. [Sadly, I think your use case confirmed that it is not viable to replace the inherited attribute by a local, and likewise for an 'outside' reference.] _I can only say, this is the main reason why I tend to separate parsing and interpretation (source -> [parsing] -> AST -> [compile] -> intermediate representation -> [engine] -> desired effects :))._ – sehe Jul 02 '13 at 12:48
  • 1
    Argh. Now that you point out compiler-writing methodology, I must admit that the AST you mentioned looks like a good idea... that I should have had a few months ago. And it seems to solve other boring peripheral problems I've had in the past. So, I won't bother you any further nor take much of your time. Thanks to you and cv_and_he. Even if I doubt it, I hope to be able to help you in the future. – Sylv Jul 02 '13 at 13:22
  • No problem! I like a challenge. We all learn on SO, no worries. I do so all the time. Thanks for sharing your experience with me. – sehe Jul 02 '13 at 14:01

1 Answers1

2

Note I just extended my answer with a few more informational links. In this particular case I have a hunch that you could just get away with the Nabialek trick and replacing the inherited attribute with a corresponding qi::locals<> instead. If I have enough time, I might work out a demonstration later.

Caveats, expositioning the problem

Please be advised that there are issues when copying proto expression trees and spirit parser expressions in particular - it will create dangling references as the internals are not supposed to live past the end of the containing full expressions. See BOOST_SPIRIT_AUTO on Zero to 60 MPH in 2 seconds!

Also see these answers which also concerns themselves with building/composing rules on the fly (at runtime):

Nabialek Trick

In general, I'd very strongly advise against combining rules at runtime. Instead, if you're looking to 'add alternatives' to a rule at runtime, you can always use qi::symbols<> instead. The trick is to store a rule in the symbol-table and use qi::lazy to call the rule. In particular, this is known as the Nabialek Trick.

I have a toy command-line arguments parser here that demonstrates how you could use this idiom to match a runtime-defined set of command line arguments:

Limitations of qi::lazy, what's next?

Unfortunately, qi::lazy does not support inherited arguments see e.g.

You might be better off writing a custom parser component, as documented here:

I'll try to find some time to work out a sample that replaces inherited arguments by qi::locals later.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Was there anything wrong with [this](http://boost.2283326.n4.nabble.com/spirit-qi-crashes-when-using-pointer-to-grammar-tp4643031p4643596.html)? – llonesmiz Jul 01 '13 at 15:40
  • @cv_and_he interesting read, I've not seen it before. I'll have to read it later. However, for now, judging from the name llonesmiz I'd say chances are pretty good that there's nothing wrong with it :) – sehe Jul 01 '13 at 15:43
  • I have it on good authority that he likes to answer even when he is not 100% certain that he is right, hoping that he might help. – llonesmiz Jul 01 '13 at 15:45
  • @cv_and_he that may be true, but I have it on good authority (including my own witness) that he's also very well-versed in Boost (and Spirit). Like I said, I'll have a look later. It looked to be a cross solution of a custom parser component combined with qi::lazy. I suppose the `phx::cref` part is the suspect part - as it may assume things about the lifetime of references passed that may not be true/convenient. But for now, family time. – sehe Jul 01 '13 at 15:49
  • 3
    It is just calculating the result type of the expression `rule(inh_attr)` in the same way as it is calculated [here](http://www.boost.org/boost/spirit/home/qi/nonterminal/detail/fcall.hpp) and using that as the return type of the phoenix function. (I'm llonesmiz btw). – llonesmiz Jul 01 '13 at 15:52
  • lol - that's good authority :) (wait - what is this? not-sehe talking to not-llonesmiz o.O) – sehe Jul 01 '13 at 16:07