Re: x3 beginner semantic actions

Henri Menke <[email protected]> Wed, 25 Apr 2018 11:20:48 +1200
Newsgroups gmane.comp.parsers.spirit.general
Message-ID <[email protected]>
On Wed, 2018-04-25 at 11:09 +1200, Henri Menke wrote:
> On Tue, 2018-04-24 at 15:48 +0100, Patrick Welche wrote:
> > 
> > In a bid to understand semantic actions a bit better, I thought I would
> > try to write an x3 program to count the number of + and - (think heads
> > and tails) in a sequence. The aim was to see:
> > 
> > 1) how to deal with matching on a char, but wanting to use an int as
> > attribute,
> >    i.e., not the expected type of attribute.
> > 2) how to select which int buried inside a struct to use
> > 3) how to initialise said int
> > 
> > 1) might be doable with e.g.
> > 
> >                 auto const head
> > 		        = rule<class _, int, true>
> >                         = lit('+') // [addhead]
> >                         ;
> > 
> > 2) given that toss is int or int, it is just an int, so presumably
> >    tosses.heads always gets sent to head or tail?
> > 
> > 3) I don't see how to not overwrite the result with the initialisation
> > 
> > Presumably this is just the wrong way of looking at it. (I don't want to
> > "cheat" and have external variables which the lambda change directly -
> > or is that the only way?)
> - Use eps [zerotosses] to initialize the attribute to zero.  I use aggregate
> initialization because this is C++14.
> 
>     auto zerotosses = [&](auto& ctx){
>         boost::spirit::x3::_val(ctx) = {0,0};
>     };
> 
> - addhead and addtail should increment the heads and tails members.
> 
>     auto addhead = [&](auto& ctx){
>         boost::spirit::x3::_val(ctx).heads++;
>     };
>     auto addtail = [&](auto& ctx){
>         boost::spirit::x3::_val(ctx).tails++;
>     };
> 
> You shouldn't use semantic actions for this at all but I guess you know that.
> Use x3::symbols instead.

Attached you can find how I'd do it with x3::symbols.  I still need one semantic
action for adding the attribute to the value.  Maybe one could even get rid of
that but I don't know how.  I'm not a Spirit expert either.

Cheers, Henri

> 
> Cheers, Henri
> 
> > 
> > 
> > 
> > Cheers,
> > 
> > Patrick
> > ----------------------------------------------------------------------------
> > --
> > Check out the vibrant tech community on one of the world's most
> > engaging tech sites, Slashdot.org! http://sdm.link/slashdot
> > _______________________________________________
> > Spirit-general mailing list
> > [email protected]
> > https://lists.sourceforge.net/lists/listinfo/spirit-general

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

_______________________________________________
Spirit-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/spirit-general
coin.cpp (text/x-c++src, 1.4 KB)
#define BOOST_SPIRIT_X3_DEBUG

#include <iostream>
#include <string>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;

struct tosses {
    int heads;
    int tails;
    tosses& operator+=(tosses const &other) {
        heads += other.heads;
        tails += other.tails;
        return *this;
    }
};

struct head_or_tail_ : x3::symbols<tosses> {
    head_or_tail_() {
        add
            ("+", tosses{1,0})
            ("-", tosses{0,1})
            ;
    }
} head_or_tail;

int main()
{
    std::string buf("++--+-++++-");
    auto iter = buf.begin(), end = buf.end();

    auto const parser = [] {
        using namespace boost::spirit::x3;

        auto const add = [](auto &ctx) { _val(ctx) += _attr(ctx); };
        auto const toss
            = x3::rule<class toss, tosses>{"tosses"}
            = +head_or_tail [add];

        return toss;
    };

    tosses res{0,0};
    boost::spirit::x3::ascii::space_type space;
    bool ok = boost::spirit::x3::phrase_parse(iter, end, parser(), space, res);
    if (ok) {
        std::cout << "Parse success\n";
    }
    else {
        std::cout << "Parsing failed\n";
    }

    if (iter != end) {
        std::cout << "Bit left over at the end: " << std::string(iter, end) << std::endl;
    }
    else {
        std::cout << "heads: " << res.heads << std::endl 
                  << "tails: " << res.tails << std::endl;
    }

    return 0;
}