Question

article à propos de l'esprit d'amplification des actions sémantiques il est mentionné que

  

Il y a en fait 2 autres arguments   étant passé: le contexte de l'analyseur et   référence à un booléen « hit »   paramètre. Le contexte de l'analyseur est   de sens que si l'action sémantique   est fixé quelque part à droite   côté d'une règle. Nous verrons plus   informations à ce sujet sous peu. le   valeur booléenne peut être définie sur false   à l'intérieur des invalide d'action sémantiques   le match en rétrospective, ce qui rend la   analyseur échoue.

Toutes très bien, mais j'ai essayé de trouver un exemple en passant un objet de fonction que l'action sémantique qui utilise les autres paramètres (contexte de l'analyseur et booléennes hit), mais je n'ai trouvé aucune. J'aimerais voir un exemple en utilisant des objets ou des fonctions régulières fonction, comme je peux à peine assimilez le voodoo phénix

Était-ce utile?

La solution

une très bonne question (et aussi une boîte de Pandore) parce qu'il obtient à l'interface du qi et de phénix. Je ne l'ai pas vu un exemple non plus, donc je vais étends l'article un peu dans ce sens.

Comme vous le dites, les fonctions de actions sémantiques peut prendre jusqu'à trois paramètres

  1. attribut Assorti - couvert dans l'article
  2. Contexte - contient l'interface-qi Phoenix
  3. drapeau Match - manipuler l'état de correspondance

drapeau match

Comme l'indique l'article, le second paramètre n'a pas de sens à moins que l'expression fait partie d'une règle, commençons donc avec le troisième. Un espace réservé pour le deuxième paramètre est toujours nécessaire si et pour cette utilisation boost::fusion::unused_type. Ainsi, une fonction modifiée de l'article à utiliser le troisième paramètre est:

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){
    //output parameters
    std::cout << "matched integer: '" << attribute << "'" << std::endl
              << "match flag: " << mFlag << std::endl;

    //fiddle with match flag
    mFlag = false;
}

namespace qi = boost::spirit::qi;

int main(void){
   std::string input("1234 6543");
   std::string::const_iterator begin = input.begin(), end = input.end();

   bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space);

   std::cout << "return: " << returnVal << std::endl;
   return 0;
}

qui sorties:

matched integer: '1234'
match flag: 1
return: 0

Tout cet exemple ne fait basculer le match à une non-correspondance, qui se reflète dans la sortie de l'analyseur. Selon hkaiser, en augmentation 1.44 et en définissant l'indicateur match false entraînera le match à l'échec de la manière normale. Si des alternatives sont définies, l'analyseur et tenter de revenir en arrière faire correspondre comme on pouvait s'y attendre. Cependant, boost <= 1,43 un bug Esprit empêche backtracking, ce qui provoque un comportement étrange. Pour le voir, Phoenix ajouter comprennent boost/spirit/include/phoenix.hpp et changer l'expression à

qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]

Vous attendez que, lorsque l'analyseur :: qi entier échoue, le qi alternatif :: chiffres pour correspondre au début de l'entrée à « 1 », mais la sortie est:

matched integer: '1234'
match flag: 1
6
return: 1

Le 6 est le premier chiffre du second int dans l'entrée qui indique le remplacement est prise à l'aide du skipper et sans retour en arrière. Notez également que le match est considéré comme réussi, basé sur l'alternative.

Une fois boost 1.44 est sorti, le drapeau de match sera utile pour appliquer des critères de correspondance qui pourraient être autrement difficiles à exprimer dans une séquence d'analyseur. Notez que le drapeau de correspondance peut être manipulé dans les expressions de Phoenix en utilisant l'espace réservé _pass.

paramètre Contexte

Le paramètre le plus intéressant est la seconde, qui contient l'interface qi-Phoenix, ou dans le jargon qi, le contexte de l'action sémantique. Pour illustrer cela, d'abord examiner une règle:

rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>

Le paramètre de contexte incarne l'attribut, Arg1, ... ARGn et qi :: habitants paramters modèle, enveloppé dans un boost :: esprit :: contexte type de modèle. Cet attribut diffère du paramètre de fonction: la fonction paramètre attribut est la valeur analysable, alors que cet attribut est la valeur de la règle elle-même. Une action sémantique doit cartographier la première à la seconde. Voici un exemple d'un type de contexte possible (équivalents d'expression de Phoenix indiqué):

using namespace boost;
spirit::context<              //context template
    fusion::cons<             
        int&,                 //return int attribute (phoenix: _val)
        fusion::cons<
            char&,            //char argument1       (phoenix: _r1)
            fusion::cons<
                float&,       //float argument2      (phoenix: _r2) 
                fusion::nil   //end of cons list
            >,
        >,
    >,
    fusion::vector2<          //locals container
        char,                 //char local           (phoenix: _a)
        unsigned int          //unsigned int local   (phoenix: _b)
    > 
>

Notez l'attribut de retour et la liste des arguments prendre la forme d'une liste de style Lisp ( ). Pour accéder à ces variables dans une fonction, l'accès aux membres de attribute ou locals du modèle struct context avec fusion :: à <> (). Par exemple, pour une con variable de contexte

//assign return attribute
fusion::at_c<0>(con.attributes) = 1;

//get the second rule argument
float arg2 = fusion::at_c<2>(con.attributes);

//assign the first local
fusion::at_c<1>(con.locals) = 42;

Pour modifier l'exemple de l'article à utiliser le deuxième argument, modifier la définition de la fonction et les appels phrase_parse:

...
typedef 
    boost::spirit::context<
        boost::fusion::cons<int&, boost::fusion::nil>, 
        boost::fusion::vector0<> 
    > f_context;
void f(int attribute, const f_context& con, bool& mFlag){
   std::cout << "matched integer: '" << attribute << "'" << std::endl
             << "match flag: " << mFlag << std::endl;

   //assign output attribute from parsed value    
   boost::fusion::at_c<0>(con.attributes) = attribute;
}
...
int matchedInt;
qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule = qi::int_[f];
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt);
std::cout << "matched: " << matchedInt << std::endl;
....

Ceci est un exemple très simple de simples cartes de la valeur à la valeur analysable d'attribut de sortie, mais les extensions devrait être assez évident. Il suffit de faire les paramètres du modèle de struct contexte correspondent à la sortie de la règle, l'entrée et les types locaux. Notez que ce type de correspondance directe entre le type analysable / valeur type / valeur de sortie peut être fait automatiquement en utilisant des règles automatiques, avec un %= au lieu d'un = lors de la définition de la règle:

qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule %= qi::int_;

à mon humble avis, l'écriture d'une fonction pour chaque action serait assez fastidieux, par rapport au mémoire et équivalents d'expression phénix lisibles. Je sympathise avec le point de vue de voodoo, mais pour un peu de temps une fois que vous travaillez avec Phoenix, la sémantique et la syntaxe ne sont pas terriblement difficile.

Edit: Accès contexte de la règle w / Phoenix

La variable de contexte est définie que lorsque l'analyseur fait partie d'une règle. Penser à un analyseur comme étant une expression d'entrée qui consomme, lorsqu'une règle se traduit par les valeurs d'analyseur (qi :: _ 1) en une valeur de règle (qi :: _ val). La différence est souvent non négligeable, par exemple lorsque le qi :: val a un type de classe qui doit être construit à partir POD valeurs analysées. Ci-dessous un exemple simple.

part de dire Let de notre entrée est une séquence de trois entiers CSV (de x1, x2, x3), et nous nous soucions que par une fonction arithmétique de ces trois entiers (f = x0 + (x1 + x2) * x3), où x0 est valeur obtenue ailleurs. Une option est de lire dans les entiers et calculer la fonction, ou utiliser alternativement Phoenix pour faire les deux.

Dans cet exemple, utiliser une règle avec un attribut de sortie (la valeur de la fonction), et l'entrée (X0), et un local (pour transmettre des informations entre les analyseurs individuels avec la règle). Voici l'exemple complet.

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

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

int main(void){
   std::string input("1234, 6543, 42");
   std::string::const_iterator begin = input.begin(), end = input.end();

   qi::rule<
      std::string::const_iterator,
      int(int),                    //output (_val) and input (_r1)
      qi::locals<int>,             //local int (_a)
      ascii::space_type
   >
      intRule =
            qi::int_[qi::_a = qi::_1]             //local = x1
         >> ","
         >> qi::int_[qi::_a += qi::_1]            //local = x1 + x2
         >> ","
         >> qi::int_
            [
               qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0
            ];

   int ruleValue, x0 = 10;
   qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue);
   std::cout << "rule value: " << ruleValue << std::endl;
   return 0;
}

En variante, tous les ints peuvent être analysés comme un vecteur, et la fonction évaluée avec une seule action sémantique (le % ci-dessous est l'opérateur de la liste et les éléments du vecteur sont accessibles avec Phoenix :: at):

namespace ph = boost::phoenix;
...
    qi::rule<
        std::string::const_iterator,
        int(int),
        ascii::space_type
    >
    intRule =
        (qi::int_ % ",")
        [
            qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1))
                      * ph::at(qi::_1,2) + qi::_r1
        ];
....

Pour ce qui précède, si l'entrée est incorrecte (deux ints au lieu de trois), la mauvaise chose pourrait se produire au moment de l'exécution, donc il serait préférable de préciser le nombre de valeurs analysées de façon explicite, l'analyse syntaxique donc échouera pour une mauvaise entrée . L'utilisation ci-dessous _1, _2 et _3 pour faire référence à la première, deuxième et troisième valeur de correspondance:

(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
    qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];

Ceci est un exemple artificiel, mais devrait vous donner l'idée. J'ai trouvé phoenix actions sémantiques vraiment utiles dans la construction d'objets complexes directement à partir de l'entrée; cela est possible parce que vous pouvez appeler les constructeurs et les fonctions membres dans les actions sémantiques.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top