Ce vraiment une bonne question (et aussi un peut de vers) car il est à l'interface de qi et de phoenix. Je n'ai pas vu un exemple non plus, donc je vais étendre l'article un peu dans cette direction.
Comme vous le dites, les fonctions de la sémantique des actions peut prendre jusqu'à trois paramètres
- Appariés attribut - couvert dans l'article
- Contexte - contient le qi-phoenix interface
- Match de flag - manipuler le match de l'etat
Match drapeau
Comme le dit l'article, le deuxième paramètre n'est pas significatif à moins que l'expression est partie d'une règle, donc permet de démarrer avec le troisième. Un espace réservé pour le deuxième paramètre est toujours nécessaire et, pour cet usage, boost::fusion::unused_type
. Donc, une modification de la fonction 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;
}
sorties:
appariés entier: '1234'
match drapeau: 1
retour: 0
Tous cet exemple ne fait basculer le match à un non-match, qui se reflète dans l'analyseur de sortie. Selon hkaiser, dans boost de 1,44 et réglage du match de drapeau à faux, le match à l'échec de la façon normale. Si des alternatives sont définies, l'analyseur va revenir en arrière et essayer de les faire correspondre l'on pourrait attendre. Cependant, dans boost<=1.43 un Esprit bug empêche le retour en arrière, ce qui provoque un comportement étrange. Pour voir cela, ajouter phoenix comprennent boost/spirit/include/phoenix.hpp
et de modifier l'expression de
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
Vous attendons à ce que, lorsque le qi::int parser échoue, l'alternative qi::chiffres pour correspondre au début de l'entrée à "1", mais la sortie est:
appariés entier: '1234'
match drapeau: 1
6
retour: 1
L' 6
est le premier chiffre de la deuxième int dans l'entrée qui indique que l'alternative est prise avec le skipper et sans retour en arrière. Notez également que le match est considéré comme un succès, basé sur l'alternative.
Une fois boost de 1,44 est sorti, le match drapeau sera utile pour appliquer des critères de correspondance qui pourrait en être autrement difficile à exprimer dans un analyseur syntaxique de la séquence. Notez que le match drapeau peut être manipulé à phoenix expressions à l'aide de l' _pass
de l'espace réservé.
Paramètre de contexte
Le plus intéressant paramètre est la seconde, qui contient le qi-phoenix interface, ou de qi langage, le contexte de la sémantique de l'action. Pour illustrer cela, examinez d'abord 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::les habitants modèle paramétrée, enveloppé dans un boost::spirit::contexte type de modèle. Cet attribut diffère de la fonction paramètre: le paramètre de la fonction d'attribut est la valeur analysée, alors que cet attribut est la valeur de la règle elle-même. D'une sémantique de l'action doit carte de la première à la seconde. Voici un exemple possible du type de contexte (phoenix expression équivalents 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)
>
>
Remarque le retour de l'attribut et de la liste d'arguments prendre la forme d'un lisp-style de liste (un des inconvénients de la liste). Pour accéder à ces variables dans une fonction, l'accès à l' attribute
ou locals
des membres de l' context
struct modèle avec la fusion: a<>(). Par exemple, pour une variable de contexte con
//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'article exemple, pour utiliser le deuxième argument, de modifier la définition de la fonction et phrase_parse appels:
...
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;
....
C'est un exemple très simple que juste les cartes de la valeur analysée à la sortie de la valeur de l'attribut, mais les extensions devrait être assez évident. Il suffit de faire le contexte struct paramètres du modèle correspondent à la règle de sortie, d'entrée et de types locaux. Notez que ce type de correspondance directe entre analysé type/valeur pour le type de sortie/valeur peut être effectuée automatiquement à l'aide de l'auto de règles, avec un %=
au lieu de =
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 plutôt fastidieux, par rapport à la brève et lisible phoenix expression équivalents. Je compatis avec le voodoo point de vue, mais une fois que vous travaillez avec phoenix pour un peu de temps, la sémantique et la syntaxe ne sont pas terriblement difficile.
Edit: Accès à la règle contexte w/ Phoenix
La variable de contexte est défini uniquement lorsque l'analyseur est partie d'une règle. Pensez à un analyseur comme " toute expression qui consomme de l'entrée, où une règle se traduit par l'analyseur de valeurs (qi::_1) dans la valeur d'une règle (qi::_val). La différence est souvent non négligeable, par exemple lors de qi::val a un type de Classe qui doit être construit à partir de POD valeurs analysées. Ci-dessous est un exemple simple.
Disons partie de notre contribution est une séquence de trois CSV entiers (x1, x2, x3
), et on ne garde une fonction arithmétique de ces trois nombres entiers (f = x0 + (x1+x2)*x3 ), où x0 est une valeur obtenue ailleurs. Une option est de les lire dans les entiers et de calculer la fonction, ou vous pouvez également utiliser phoenix de faire les deux.
Pour cet exemple, utilisez une règle avec un attribut de sortie (la valeur de la fonction), et d'entrée (x0), et d'un local (pour transmettre les informations entre les différents analyseurs 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;
}
Par ailleurs, tous les services de renseignements pourrait être analysée comme un vecteur, et la fonction évaluée avec une seule sémantique de l'action (l' %
ci-dessous la liste de l'opérateur et les éléments du vecteur sont accessibles avec phoenix::à):
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 le haut, si l'entrée est incorrecte (deux ints au lieu de trois), une mauvaise chose pourrait se produire au moment de l'exécution, de sorte qu'il serait préférable de préciser le nombre de valeurs analysées explicitement, de sorte que l'analyse échoue pour une mauvaise entrée. Le ci-dessous utilise _1
, _2
, et _3
à la référence de la première, deuxième et troisième match de la valeur:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];
C'est un exemple artificiel, mais devrait vous donner l'idée. J'ai trouvé phoenix sémantique des actions très utile 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 de membre au sein de la sémantique des actions.