aumentar espírito semântica parâmetros de ação
-
28-09-2019 - |
Pergunta
neste artigo sobre o boost espírito semântica ações é mencionado que
Na verdade, existem mais de 2 argumentos sendo passado:o analisador de contexto e um referência a um boolean 'hit' o parâmetro.O analisador de contexto é só fará sentido se a semântica de ação é ligado em algum lugar à direita lado de uma regra.Vamos ver mais informações sobre isso em breve.O valor booleano pode ser definida como false no interior da semântica da ação invalida o jogo em retrospectiva, fazendo com que o analisador de falhar.
Tudo bem, mas eu tenho tentado encontrar um exemplo de passagem de um objeto de função como semântica ação que usa a outros parâmetros (analisador de contexto e bater boolean), mas eu ainda não encontrei nenhuma.Eu gostaria de ver um exemplo utilizando funções regulares ou função de objetos, como eu mal pode grok o phoenix voodoo
Solução
Este realmente uma boa pergunta (e também uma lata de vermes), porque chega na interface do qi e phoenix.Eu não tenha visto um exemplo, por isso, vou estender o artigo um pouco nesta direção.
Como você diz, funções para semântica ações pode levar até três parâmetros
- Correspondência atributo coberto no artigo
- Contexto - contém o qi-phoenix interface
- Correspondência bandeira - manipular a correspondência estado
Correspondência bandeira
Como diz o artigo, o segundo parâmetro não é significativa, a menos que a expressão é parte de uma regra, então vamos começar com o terceiro.Um espaço reservado para o segundo parâmetro é ainda necessária e, para isso, usar boost::fusion::unused_type
.Assim, uma função modificada de artigo para usar o terceiro parâmetro é:
#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;
}
saídas:
matched integer: '1234' match flag: 1 return: 0
Todos este exemplo faz é mudar o jogo para um não-jogo, o que se reflete no analisador de saída.De acordo com hkaiser, no impulso de 1.44 e até fazer uma partida sinalizador como false fará com que o jogo falha em modo normal.Se as alternativas são definidos, o analisador irá recuar e tentar combiná-los, como seria de esperar.No entanto, em impulso<=1.43 um Espírito bug impede o retrocesso, o que faz com que um comportamento estranho.Para ver isso, adicione phoenix incluem boost/spirit/include/phoenix.hpp
e mudar a expressão para
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
Seria de esperar que, quando o qi::int analisador de falha, a alternativa qi::dígito para coincidir com o início da entrada em "1", mas o resultado é:
matched integer: '1234' match flag: 1 6 return: 1
O 6
é o primeiro dígito do segundo int na entrada que indica a alternativa foi tirada usando o capitão e sem retrocesso.Observe também que a partida é considerada bem sucedida, com base na alternativa.
Uma vez impulso de 1,44 está fora da partida bandeira será útil para a aplicação de critérios de correspondência, que podem ser difíceis de expressar em um analisador de sequência.Nota-se que o combate bandeira pode ser manipulado em phoenix expressões usando o _pass
espaço reservado.
O parâmetro de contexto
O mais interessante do parâmetro e o segundo, que contém o qi-phoenix interface, ou em qi linguagem, o contexto da semântica da ação.Para ilustrar isso, primeiro examinar uma regra:
rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>
O parâmetro de contexto encarna o Atributo, Arg1, ...ArgN, e qi::moradores modelo de parâmetros, envolto em um impulso::espírito::o contexto de tipo de modelo.Este atributo é diferente do parâmetro de função:o parâmetro da função de atributo é analisada valor, enquanto este atributo é o valor da regra em si.Uma semântica da ação deve mapa do primeiro para o segundo.Aqui está um exemplo de um possível tipo de contexto (phoenix expressão equivalentes indicado):
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)
>
>
Observação o retorno atributo e lista de argumento assumir a forma de um lisp-lista de estilo (um contras lista).Para ter acesso a estas variáveis dentro de uma função, o acesso a attribute
ou locals
os membros do context
estrutura do modelo com fusão::no<>().Por exemplo, para uma variável 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 o artigo exemplo, para utilizar o segundo argumento, altere a definição de função e phrase_parse chamadas:
...
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 é um exemplo muito simples que apenas mapas analisado o valor para o atributo de saída valor, mas as extensões devem ser bastante aparente.Basta fazer o contexto struct parâmetros do modelo correspondem à regra de saída, de entrada e de tipos de locais.Note que este tipo de uma correspondência direta entre analisado o tipo/valor para o tipo de saída/valor pode ser feito automaticamente usando o auto de regras, com um %=
em vez de um =
quando definir a regra:
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule %= qi::int_;
IMHO, escrever uma função para cada ação poderia ser um pouco entediante, em comparação com o breve e legível phoenix expressão equivalentes.Eu simpatizo com o voodoo ponto de vista, mas uma vez que você trabalhar com o phoenix, por um pouco, a semântica e a sintaxe não são terrivelmente difícil.
Editar:Aceder a regra de contexto w/ Phoenix
A variável de contexto só é definido quando o analisador é parte de uma regra.Pense em um analisador como sendo qualquer expressão que consome de entrada, onde uma regra traduz o analisador de valores (qi::_1) em uma regra de valor (qi::_val).A diferença é que, muitas vezes, não trivial, por exemplo, quando qi::val tem um tipo de Classe que precisa ser construído a partir de POD valores analisados.Abaixo está um exemplo simples.
Vamos dizer que parte da nossa entrada é uma sequência de três CSV números inteiros (x1, x2, x3
), e nós só a assistência de uma função aritmética desses três números inteiros (f = x0 + (x1+x2)*x3 ), onde x0 é um valor obtido em outros lugares.Uma opção é ler os números inteiros e calcular a função, ou, em alternativa, usar o phoenix para ambos.
Para este exemplo, use uma regra com um atributo de saída (o valor da função), e de entrada (x0), e local (para passar informações entre o indivíduo analisadores com a regra).Aqui está o exemplo 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;
}
Em alternativa, todos os ints poderia ser analisado como um vetor, e a função avaliado com uma única semântica da ação (o %
abaixo está a lista de operador e de elementos do vetor são acessados com 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
];
....
Para o acima, se a entrada está incorreta (dois ints, em vez de três), coisa ruim poderia acontecer em tempo de execução, então seria melhor para especificar o número de valores analisados explicitamente, portanto, a análise irá falhar para uma entrada incorreta.A seguir usa _1
, _2
, e _3
para fazer referência a primeira, a segunda e a terceira partida valor:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];
Este é um exemplo imaginário, mas deve dar-lhe a idéia.Eu encontrei phoenix semântica ações realmente útil na construção de objetos complexos directamente a partir da entrada;isso é possível porque você pode chamar de construtores e de funções membro dentro da semântica de ações.