c++ parsing semantics boost-spirit boost-spirit-qi

c++ - impulsar los parámetros de acción semántica del espíritu



parsing semantics (1)

Esta es una muy buena pregunta (y también una lata de gusanos) porque se pone en la interfaz de qi y phoenix. Tampoco he visto un ejemplo, por lo que extenderé el artículo un poco en esta dirección.

Como dices, las funciones para acciones semánticas pueden tomar hasta tres parámetros

  1. Atributo coincidente: cubierto en el artículo
  2. Contexto - contiene la interfaz qi-phoenix
  3. Bandera de coincidencia: manipule el estado del partido

Bandera del partido

Como dice el artículo, el segundo parámetro no es significativo a menos que la expresión sea parte de una regla, así que comencemos con el tercero. Sin embargo, todavía se necesita un marcador de posición para el segundo parámetro y para este uso boost::fusion::unused_type . Entonces, una función modificada del artículo para usar el tercer parámetro es:

#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; }

qué salidas:

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

Todo lo que hace este ejemplo es cambiar la coincidencia a una no coincidencia, que se refleja en la salida del analizador. De acuerdo con hkaiser, en el impulso 1.44 y el establecimiento de la bandera de partido en falso hará que la coincidencia falle de la manera normal. Si se definen alternativas, el analizador retrocederá e intentará unirlas como cabría esperar. Sin embargo, en boost <= 1.43, un error de Spirit evita el retroceso, lo que causa un comportamiento extraño. Para ver esto, agrega phoenix incluye boost/spirit/include/phoenix.hpp y cambia la expresión a

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

Es de esperar que, cuando el analizador qi :: int falle, el qi :: dígito alternativo coincida con el comienzo de la entrada en "1", pero el resultado sea:

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

El 6 es el primer dígito del segundo int en la entrada que indica que se toma la alternativa utilizando el patrón y sin retroceso. Tenga en cuenta también que el partido se considera exitoso, según la alternativa.

Una vez que se ha eliminado el impulso 1.44, la bandera de coincidencia será útil para aplicar criterios de coincidencia que de otro modo podrían ser difíciles de expresar en una secuencia de analizador. Tenga en cuenta que la bandera de partido se puede manipular en expresiones de fénix utilizando el marcador de posición _pass .

Parámetro de contexto

El parámetro más interesante es el segundo, que contiene la interfaz qi-phoenix, o en el lenguaje qi, el contexto de la acción semántica. Para ilustrar esto, primero examine una regla:

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

El parámetro de contexto incorpora los parámetros de la plantilla Attribute, Arg1, ... ArgN y qi :: locals, envueltos en un tipo de plantilla boost :: spirit :: context. Este atributo difiere del parámetro de función: el atributo de parámetro de función es el valor analizado, mientras que este atributo es el valor de la regla en sí. Una acción semántica debe mapear lo primero a lo último. Aquí hay un ejemplo de un posible tipo de contexto (equivalentes de expresión de Phoenix expresados):

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) > >

Tenga en cuenta que el atributo de retorno y la lista de argumentos toman la forma de una lista de estilo lisp (una lista de contras ). Para acceder a estas variables dentro de una función, acceda al attribute o miembros locals de la plantilla de estructura de context con fusion :: at <> (). Por ejemplo, para una variable de contexto 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;

Para modificar el ejemplo del artículo para usar el segundo argumento, cambie la definición de la función y las llamadas a 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; ....

Este es un ejemplo muy simple que simplemente asigna el valor analizado al valor del atributo de salida, pero las extensiones deberían ser bastante evidentes. Simplemente haga que los parámetros de la plantilla struct del contexto coincidan con la salida de la regla, la entrada y los tipos locales. Tenga en cuenta que este tipo de concordancia directa entre el tipo / valor analizado y el tipo / valor de salida se puede realizar automáticamente utilizando reglas automáticas, con un %= vez de a = al definir la regla:

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

En mi humilde opinión, escribir una función para cada acción sería bastante tedioso, en comparación con los breves y legibles equivalentes de expresión de fénix. Simpatizo con el punto de vista del vudú, pero una vez que trabajas con Fénix por un tiempo, la semántica y la sintaxis no son terriblemente difíciles.

Editar: accediendo al contexto de la regla con Phoenix

La variable de contexto solo se define cuando el analizador es parte de una regla. Piense en un analizador como cualquier expresión que consuma datos de entrada, donde una regla traduce los valores del analizador (qi :: _ 1) en un valor de regla (qi :: _ val). La diferencia a menudo no es trivial, por ejemplo, cuando qi :: val tiene un tipo de clase que debe construirse a partir de los valores analizados POD. A continuación se muestra un ejemplo simple.

Digamos que parte de nuestra entrada es una secuencia de tres enteros CSV ( x1, x2, x3 ), y solo cuidamos una función aritmética de estos tres enteros (f = x0 + (x1 + x2) * x3), donde x0 es un valor obtenido en otro lugar. Una opción es leer en los enteros y calcular la función, o alternativamente usar Phoenix para hacer ambas cosas.

Para este ejemplo, use una regla con un atributo de salida (el valor de la función), y una entrada (x0), y un local (para pasar información entre los analizadores individuales con la regla). Aquí está el ejemplo completo.

#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; }

Alternativamente, todas las entradas se pueden analizar como un vector, y la función se evalúa con una sola acción semántica (el % continuación es el operador de la lista y se accede a los elementos del vector con 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 ]; ....

Por lo anterior, si la entrada es incorrecta (dos entradas en lugar de tres), podría pasar algo malo en el tiempo de ejecución, por lo que sería mejor especificar el número de valores analizados explícitamente, por lo que el análisis fallará para una entrada incorrecta. El siguiente utiliza _1 , _2 y _3 para hacer referencia al primer, segundo y tercer valor de coincidencia:

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

Este es un ejemplo artificial, pero debería darle la idea. He encontrado que las acciones semánticas de Fénix son realmente útiles para construir objetos complejos directamente desde la entrada; esto es posible porque puede llamar constructores y funciones miembro dentro de acciones semánticas.

en este artículo sobre impulsar acciones semánticas espíritu se menciona que

En realidad, se están transfiriendo 2 argumentos más: el contexto del analizador y una referencia a un parámetro booleano ''hit''. El contexto del analizador es significativo solo si la acción semántica está unida al lado derecho de una regla. Veremos más información sobre esto en breve. El valor booleano se puede establecer como falso dentro de la acción semántica. Esto invalida la coincidencia en retrospectiva, lo que hace que el analizador falle.

Todo bien, pero he estado tratando de encontrar un ejemplo que pase un objeto de función como acción semántica que usa los otros parámetros (contexto del analizador y hit booleano) pero no he encontrado ninguno. Me encantaría ver un ejemplo con funciones regulares u objetos funcionales, ya que apenas puedo asimilar el vudú del fénix