Pergunta

Em várias perguntas que eu vi recomendações para a quadro parser gerador espírito de boost.org , mas depois nos comentários lá está reclamando de pessoas usando Espírito que não estão satisfeitos. Será que essas pessoas se levantassem adiante e explicar para o resto de nós, quais são as desvantagens ou desvantagens de usar Espírito?

Foi útil?

Solução

É uma idéia muito legal, e eu gostei; foi especialmente útil para realmente aprender como usar modelos C ++.

Mas a sua documentação recomenda o uso de espírito para pequenas e analisadores de médio porte. Um analisador para uma linguagem completa levaria idades para compilar. Vou listar três razões.

  • análise sintática sem varredura. Embora seja bastante simples, quando é necessária retrocesso pode abrandar o analisador. É opcional embora - um léxico pode ser integrado, consulte o pré-processador C construído com o Espírito. Uma gramática de ~ 300 linhas (incluindo ambos os arquivos e.cpp) compila (não otimizados) para um arquivo de 6M com o GCC. Inlining e otimizações máximas fica que até ~ 1,7m.

  • parsing lento - não há verificação estática da gramática, nem a dica sobre excessiva antecipação requerida, nem para verificar erros básicos, como para uso instância de recursão esquerda (o que leva a recursão infinita em recursiva-descida analisadores LL gramáticas). recursão esquerda não é um bug realmente difícil de rastrear, embora, mas lookahead excessiva pode causar tempos de análise exponenciais.

  • Uso modelo pesado - enquanto isso tem certas vantagens, isso impacta compilação vezes e tamanho do código. Além disso, a definição de gramática deve ser normalmente visível para todos os outros usuários, impactando ainda mais tempo de compilação. Eu tenho sido capaz de mover gramáticas para arquivos .cpp adicionando instantiations modelo explícita com os parâmetros corretos, mas não foi fácil.

UPDATE: a minha resposta é limitada a minha experiência com o Espírito clássico, não Espírito V2. Eu ainda esperaria Espírito ser fortemente baseada no modelo, mas agora estou apenas adivinhando.

Outras dicas

Em impulso 1,41 uma nova versão do Espírito está sendo lançado, e bate de calças de espírito :: clássico:

Depois de um longo tempo em beta (mais de 2 anos com Espírito 2.0), Espírito 2.1 será finalmente lançado com o próximo impulso 1,41 liberação. O código é muito estável e é agora pronto para código de produção. Estamos trabalhando duro em terminar a documentação no tempo para impulsionar 1,41. Você pode espreitar o estado atual da documentação aqui. Atualmente, você pode encontrar o código e documentação no impulso SVN tronco. Se você tem um novo projeto envolvendo Espírito, recomendamos começando com o Espírito 2.1 agora. Permita-me para citar o post de OvermindDL do lista de discussão Espírito:

I podem começar a soar como um bot com quantas vezes eu dizer isso, mas Spirit.Classic é antiga, você deve mudar para Spirit2.1, ele pode fazer tudo o que você fez acima de um negócio GRANDE mais fácil, um código menos muito, e ele executa mais rápido. Por exemplo, Spirit2.1 pode construir todo o seu AST em linha, nenhuma primordial estranho, não há necessidade para construir as coisas depois, etc ..., todos como um bom e passo rápido. Vocês realmente precisa de atualização. Veja o outro lugares do passado dia links para docs e tal para Spirit2.1. Spirit2.1 está atualmente em Trunk Boost, mas será ser formalmente lançado com impulso 1,41, mas de resto é completa.

Para mim, o maior problema é que as expressões em Espírito, como visto pelo compilador ou depurador, são bastante longa (eu copiei abaixo uma parte de uma expressão no Espírito Classic). Estas expressões assustar-me. Quando eu trabalho em um programa que usa Espírito, eu tenho medo de usar valgrind ou para imprimir backtrace no gdb.

boost::spirit::classic::parser_result<boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<optional_suffix_parser<char const*>, boost::spirit::classic::ref_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::ref_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > >, boost::spirit::classic::kleene_star<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > > > > > >, void ()(char const, char const*)>, boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> > >::type boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<

Aqui está o que eu não gosto sobre isso:

  • a documentação é limitada. Há uma página web grande, onde "tudo" é explicado, mas as explicações atuais falta em detalhes.

  • pobre geração AST. ASTs são mal explicadas e, mesmo depois de bater sua cabeça contra a parede para entender como o modificadores AST trabalho, é difícil obter um fácil de manipular AST (isto é, um que mapeia bem para o domínio do problema)

  • Ela aumenta tempo de compilação enormemente, mesmo para "médio" -sized gramáticas

  • A sintaxe é demasiado pesado. É um fato da vida que em C / C ++, você deve duplicar o código (ou seja, entre a declaração e definição). No entanto, parece que em boost :: espírito, quando você declarar uma gramática <>, você deve repetir algumas coisas 3 vezes: D (quando quiser ASTs, que é o que eu quero: D)

Além disso, eu acho que eles fizeram um bom trabalho muito com o analisador, dadas as limitações do C ++. Mas eu acho que eles devem melhorar mais. A página de história descreve que houve um espírito "dinâmico" antes do atual espírito "estático"; Eu estou querendo saber o quanto mais rápido e quanto melhor sintaxe que tinha.

Eu diria que o maior problema é a falta de qualquer diagnóstico ou outra ajuda para problemas de gramática. Se sua gramática é ambígua, o analisador pode não analisar o que você espera que ele, e não há nenhuma boa maneira de perceber isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top