16 Şubat 2018 Cuma

spirit phoenix

Giriş
Şu satırı dahil ederiz.
#include <boost/spirit/include/phoenix.hpp>
Bir rule için semantic action çalıştırmayı sağlar. Açıklaması şöyle
Phoenix, a companion library bundled with Spirit, is specifically suited for binding semantic actions. It is like Boost.Lambda on steroids, with special custom features that make it easy to integrate semantic actions with Spirit.
Semantic action'lardan kaçınmak lazım deniyor. Açıklaması şöyle
Phoenix Actors are needlessly more complex than the dedicated functor. They have a sweet point (mainly simple assignment or builtin operations). But if the actor is any kind of non-trivial you'll see the complexity ramp up quickly, not just for the human but also for the compiler. This leads to

  • slow compilation
  • sub-optimal emitted code
  • harder to maintain source
  • new classes of error (e.g. when the expression templates hold references to locals/temporaries, something that Boost Proto (and hence Phoenix) does not prevent or signal. In fact, it encourages it by assuming all template expressions are ephemeral, but I digress).
Açıklaması şöyle
Interestingly: Spirit X3 dropped Phoenix altogether, even though Phoenix was once the brain child of Spirit.
The new style uses c++14 polymorphic lambdas, that look 90% like the helper function object in the original code, but inline as a lambda.

bind metodu
Örnek
Elimizde bir yapı olsun.
struct MyStruct {
  int a;
  int b; 
  int c;
};
Bu yapıyı kullanan bir karma grammar olsun.
struct MyStructGenerator : bsk::grammar<boost::spirit::ostream_iterator, MyStruct()>
{
  MyStructGenerator() : MyStructGenerator::base_type(start_)
  {
    
     start_ = '<' 
          << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::c, bsk::_val)]
          << ','
          << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::b, bsk::_val)]
          << ','
          << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::a, bsk::_val)]
          << '>';
    }

  ~MyStructGenerator() = default;
  bsk::rule<boost::spirit::ostream_iterator, MyStruct()> start_;
};
Şöyle yaparız.
 MyStruct ms = { 3, 2, 1 };
std::cout << boost::spirit::karma::format(MyStructGenerator(), ms) << std::endl;
Çıktı olarak şunu alırız.
<1, 2, 3>
Örnek
Elimizde bir yapı olsun
struct VariableHolder {
    std::string name;     // contains "varName"
    std::string fromFile; // contains "$(varName)" or "$varName"
};
Bu değişkenlere şöyle bind işlemi yaparız.
auto _name     = phoenix::bind(&VariableHolder::name, qi::_val);
auto _fromFile = phoenix::bind(&VariableHolder::fromFile, qi::_val);
Daha sonra kuralları tanımlarız. start kuralı her eşleştiğinde hem _name hem de_fromFile değişkeni atanır.
variableName = qi::alpha >> +qi::alnum;
        variable = '$' >> (variableName | '(' >> variableName >> ')');

start = qi::as_string [ qi::raw [ variable [ _name = qi::_1 ]] 
                      ] [_fromFile = qi::_1 ];
Örnek
Elimizde bir yapı olsun.
struct Data {
  double minFrequency = 0.0;
  double maxFrequency = 0.0;
  double minElevation = 0.0;
  double maxElevation = 0.0;
};
Eşleşme olunca alanlara değer atamak için şöyle yaparız.
template <typename It, typename Skipper = qi::blank_type>
struct grammar : qi::grammar<It, Data(), Skipper> {

  grammar() : grammar::base_type(start) {

    using namespace qi;
    auto minFrequency = bind(&Data::minFrequency, _r1);
    auto maxFrequency = bind(&Data::maxFrequency, _r1);
    auto minElevation = bind(&Data::minElevation, _r1);
    auto maxElevation = bind(&Data::maxElevation, _r1);

    known =
      ("FMin Antenna" >> lit('=') >> int_)[minFrequency = _1] |
      ("FMax Antenna" >> lit('=') >> int_)[maxFrequency = _1] |
      ("EMin Antenna" >> lit('=') >> int_)[minElevation = _1] |
      ("EMax Antenna" >> lit('=') >> int_)[maxElevation = _1]
      ;

    unknown = +alnum >> '=' >> +alnum;

    setting = (known(_r1) | unknown) >> +eol;

    start =
      no_case["[GENERAL]"] >> eol 
      >> *setting(_val);
  }

private:
  qi::rule<It, Data(), Skipper> start;
  qi::rule<It, void(Data&), Skipper> setting, known;
  qi::rule<It, Skipper> unknown;
};
Örnek
Elimizde bir rule olsun. rule eşleşince değer atamak için şöyle yaparız.
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
  property() : property::base_type(start) {
    using namespace qi;

    name          = +(graph - char_(".][")); // not matching spaces please
    qualifiedName = name % '.';

    start = skip(ascii::space) ['[' >> qualifiedName >> ']']
                        [_val = phx::bind(lookup, phx::cref(sample), _1) ]
                ;

    BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
  }

  private:
    qi::rule<Iterator, std::deque<std::string>(),
      qi::ascii::space_type> qualifiedName;
    qi::rule<Iterator, std::string()> name;
    qi::rule<Iterator, int()> start;
};
lookup metodunu şöyle yaparız. Burada Node sınıfı bizim kodumuz.
int lookup(Node const& context, std::deque<std::string> path);
function metodu
Yaklanan şeyin başına _tag_ eklemek için şöyle yaparız.
struct convertTag_f {
  std::string operator()(std::string const& tag) const {
    return "_tag_" + tag;
  }
};
boost::phoenix::function<convertTag_f> convertTag;

TestGrammar() : TestGrammar::base_type(expr)
{
  using namespace qi::labels;

  tag  = qi::as_string[+ascii::char_] [ _val += convertTag(_1) ];
  expr = ascii::char_('!') >> tag;
}
BOOST_PHOENIX_ADAPT_FUNCTION
Örnek
Şöyle yaparız
BOOST_PHOENIX_ADAPT_FUNCTION(long, stol_, std::stol, 1);
Şöyle bir rule tanımlarız.
qi::rule<std::string::const_iterator, long()> weird_num;

{
  using namespace qi;
  weird_num = as_string[ skip(alpha) [+digit] ] [_val = stol_(_1) ];
}
Şöyle yaparız.
std::string const s = "AB1234xyz5678C9";

long x = 0;
boost::spirit::qi::parse(s.begin(), s.end(), weird_num, x);
Örnek
Şöyle yaparız.
BOOST_PHOENIX_ADAPT_FUNCTION(std::string, regex_replace_, boost::regex_replace, 4)
ri_type
Derinliği 32'yi geçmemesi için şöyle yaparız.
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;


qi::rule<std::string::const_iterator, std::string(size_t depth)> term;
qi::_r1_type _depth;
term %= 
  qi::eps(_depth < 32) >>
  qi::string("(") >> *term(_depth + 1) >> qi::string(")");




Hiç yorum yok:

Yorum Gönder