Domanda

In diverse domande ho visto raccomandazioni per Spirito framework del generatore di parser da boost.org , ma poi nei commenti c'è un brontolio da parte della gente usando lo Spirito che non è felice. Per favore, queste persone si faranno avanti e spiegheranno al resto di noi quali sono gli svantaggi o gli svantaggi dell'uso dello Spirito?

È stato utile?

Soluzione

È un'idea abbastanza interessante, e mi è piaciuta; è stato particolarmente utile imparare davvero come usare i modelli C ++.

Ma la loro documentazione raccomanda l'uso dello spirito per parser di piccole e medie dimensioni. Un parser per una lingua completa richiederebbe anni per essere compilato. Elencherò tre motivi.

  • Analisi senza scanner. Sebbene sia abbastanza più semplice, quando è richiesto il backtracking può rallentare il parser. È facoltativo però: un lexer potrebbe essere integrato, vedi il preprocessore C costruito con Spirit. Una grammatica di ~ 300 righe (inclusi i file .h e .cpp) viene compilata (non ottimizzata) in un file di 6M con GCC. Le ottimizzazioni integrate e massime si riducono a ~ 1,7 milioni.

  • Analisi lenta: non esiste un controllo statico della grammatica, né per suggerire un eccessivo lookahead richiesto, né per verificare errori di base, come ad esempio l'uso della ricorsione a sinistra (che porta a una ricorsione infinita in discesa ricorsiva parsers LL grammars). La ricorsione a sinistra non è un bug davvero difficile da rintracciare, tuttavia, un lookahead eccessivo potrebbe causare tempi di analisi esponenziali.

  • Utilizzo intenso del modello: sebbene ciò presenti alcuni vantaggi, ciò influisce sui tempi di compilazione e sulla dimensione del codice. Inoltre, la definizione grammaticale deve essere normalmente visibile a tutti gli altri utenti, con un impatto ancora maggiore sui tempi di compilazione. Sono stato in grado di spostare le grammatiche nei file .cpp aggiungendo istanze esplicite del modello con i parametri giusti, ma non è stato facile.

AGGIORNAMENTO: la mia risposta è limitata alla mia esperienza con Spirit classic, non con Spirit V2. Mi aspetterei comunque che Spirit sia fortemente basato su modelli, ma ora sto solo indovinando.

Altri suggerimenti

In boost 1.41 viene rilasciata una nuova versione di Spirit, che batte di pantaloni fuori dallo spirito :: classic:

  

Dopo molto tempo in beta (più di 2   anni con Spirit 2.0), Spirit 2.1   sarà finalmente rilasciato con il   prossima uscita Boost 1.41. Il codice   è molto stabile ora ed è pronto per   codice di produzione. Stiamo lavorando sodo   al termine della documentazione in tempo   per Boost 1.41. Puoi dare un'occhiata al   stato attuale della documentazione   Qui. Attualmente puoi trovare il codice   e documentazione in Boost SVN   tronco. Se hai un nuovo progetto   coinvolgendo lo Spirito, consigliamo vivamente   a partire da Spirit 2.1 ora. Permettimi   per citare il post di OvermindDL dal   Mailing list di Spirit:

     
    
      

Potrei iniziare a sembrare un bot con       quanto spesso lo dico, ma       Spirit.Classic è antico, dovresti       passare a Spirit2.1, può fare       tutto quello che hai fatto sopra un ottimo affare       più facile, molto meno codice e così via       esegue più velocemente. Per esempio,       Spirit2.1 può costruire l'intero AST       in linea, nessuna sovrascrittura bizzarra, nessuna necessità       per costruire le cose in seguito, ecc ...,       tutto come un passo bello e veloce. tu       ho davvero bisogno di aggiornare. Vedi l'altro       post del giorno passato per link a       documenti e simili per Spirit2.1. Spirit2.1       è attualmente in Boost Trunk, ma lo farà       essere rilasciato ufficialmente con Boost 1.41,       ma è altrimenti completo.

    
  

Per me, il problema più grande è che le espressioni in Spirit, viste dal compilatore o dal debugger, sono piuttosto lunghe (ho copiato di seguito una parte di un'espressione in Spirit Classic). Queste espressioni mi spaventano. Quando lavoro su un programma che utilizza Spirit, ho paura di usare valgrind o di stampare backtrace in 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<

Ecco cosa non mi piace al riguardo:

  • la documentazione è limitata. C'è una grande pagina web in cui & Quot; tutto & Quot; è spiegato, ma le spiegazioni attuali mancano di dettagli.

  • scarsa generazione AST. Gli AST sono spiegati male e, anche dopo aver sbattuto la testa contro il muro per capire come funzionano i modificatori AST, è difficile ottenere un AST facile da manipolare (cioè uno che si adatti bene al dominio problematico)

  • Aumenta enormemente i tempi di compilazione, anche per grammatiche di dimensioni " medium "

  • La sintassi è troppo pesante. È un dato di fatto che in C / C ++ è necessario duplicare il codice (cioè tra dichiarazione e definizione). Tuttavia, sembra che in boost :: spirit, quando dichiari una grammatica & Lt; & Gt ;, devi ripetere alcune cose 3 volte: D (quando vuoi AST, che è quello che voglio: D)

Oltre a questo, penso che abbiano fatto un buon lavoro con il parser, dati i limiti del C ++. Ma penso che dovrebbero migliorarlo di più. La pagina della cronologia descrive che c'era un & Quot; dinamico & Quot; spirito prima dell'attuale " statico " spirito; Mi chiedo quanto più veloce e quanto migliore sintassi avesse.

Direi che il problema più grande è la mancanza di diagnosi o altro aiuto per problemi di grammatica. Se la tua grammatica è ambigua, il parser potrebbe non analizzare ciò che ti aspetti e non c'è modo di notarlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top