Was sind die Nachteile des Geistes Parser-Generator Rahmen von boost.org?
-
08-07-2019 - |
Frage
In einigen Fragen habe ich Empfehlungen gesehen für die Geist Parser-Generator Rahmen von boost.org , aber dann in den Kommentaren murrt es von Menschen mit Geist, die nicht glücklich sind. Werden diese Leute vortreten bitte und an den Rest von uns erklären, was die Nachteile oder Nachteile sind Geist mit?
Lösung
Es ist eine ziemlich coole Idee, und es hat mir gefallen; es war besonders nützlich, um wirklich zu lernen, wie C ++ Vorlagen.
Aber ihre Dokumentation empfiehlt die Verwendung von Geist für kleine bis mittelgroßen Parser. Ein Parser für eine vollständige Sprache würde ewig dauern zu kompilieren. Ich werde drei Gründe aufzulisten.
-
abtastlosen Parsing. Es ist zwar ziemlich einfacher, wenn Rückzieher erforderlich ist, den Parser verlangsamen. Es ist allerdings optional - ein Lexer könnte integriert werden, finden Sie in der C-Präprozessor mit Geist gebaut. Eine Grammatik von ~ 300 Zeilen (einschließlich .h und CPP-Dateien) kompiliert (nicht optimiert) in eine Datei von 6M mit GCC. Inlining und maximal Optimierungen werden, dass bis auf ~ 1,7m.
-
Langsam Parsing - es gibt keine statische Überprüfung der Grammatik, weder über die übermäßige Look-Ahead andeuten erforderlich, noch grundlegende Fehler zu überprüfen, wie zum Beispiel Verwendung von Linksrekursion (die in rekursiven Abstieg zu unendlicher Rekursion führen Parser LL Grammatiken). Linksrekursion ist keine wirklich schwer Fehler aufzuspüren, obwohl, aber übermäßige Look-Ahead könnte exponentiell Parsing Zeiten führen.
-
Schwere Vorlage Nutzung - während diese bestimmte Vorteile hat, diese Auswirkungen Kompilierungszeiten und Codegröße. Darüber hinaus muss die Grammatik Definition normalerweise für alle anderen Benutzer sichtbar sein, noch mehr Kompilierungszeiten auswirkt. Ich habe in der Lage gewesen, Grammatiken, um Dateien .cpp durch explizite Template-Instanziierungen mit den richtigen Parametern angeben, aber es war nicht einfach.
UPDATE: meine Antwort auf meine Erfahrung mit Geist klassischen beschränkt ist, nicht Geist V2. Ich würde immer noch Geist erwarte stark Template-basierte, zu sein, aber jetzt bin ich nur raten.
Andere Tipps
In Boost 1.41 eine neue Version des Geistes freigegeben wird, und es schlägt die Hose des Geistes :: Klassiker:
Nach einer langen Zeit in der Beta (mehr als 2 Jahre mit Spirit 2.0), Geist 2.1 wird schließlich mit der freigegeben werden kommende Boost-1.41-Release. Der Code jetzt sehr stabil ist und bereit ist, für produktionscode. Wir arbeiten hart, auf die Dokumentation in der Zeit Finishing für Boost-1.41. Sie können an der peek aktuelle Stand der Dokumentation Hier. Derzeit können Sie den Code finden und Dokumentation im Boost-SVN Kofferraum. Wenn Sie ein neues Projekt Geist, werten wir empfehlen mit Geist beginnt jetzt 2.1. Erlaube mir OvermindDL der Beitrag zu zitieren aus der Geist-Mailingliste:
Ich mag wie ein Bot zu klingen beginnen mit wie oft ich sage dies, aber Spirit.Classic ist alt, sollten Sie Umschalten auf Spirit2.1, kann es tun tat alles, was Sie über ein hohes Maß einfacher, Code viel weniger, und es schneller ausgeführt. Zum Beispiel, Spirit2.1 kann Ihren gesamten AST bauen inline, nicht seltsam überwiegender, keine Notwendigkeit, danach, etc zu bauen Dinge ..., alle als eine schöne und schneller Schritt. Sie wirklich zu aktualisieren. Sehen Sie die andere Beiträge aus der Vergangenheit Tag für Links zu docs und solche für Spirit2.1. Spirit2.1 ist derzeit in Boost-Trunk, wird aber formal mit Boost-1.41 freigegeben werden, aber ansonsten abgeschlossen.
Für mich das größte Problem ist, dass Ausdrücke in Geist, wie Compiler oder Debugger gesehen, ziemlich lang ist (I kopiert unter einen Teil von einem Ausdruck in Spirit Classic). Diese Ausdrücke erschrecken mich. Als ich an einem Programm arbeiten, das Geist verwendet, habe ich Angst, valgrind zu verwenden oder Backtrace in gdb zu drucken.
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<
Hier ist das, was Ich mag nicht darüber:
-
Die Dokumentation ist begrenzt. Es gibt eine große Web-Seite, wo „alles“ erklärt, aber die aktuellen Erklärungen fehlen in Details.
-
arme AST Generation. Äste sind schlecht erklärt und auch nach dem Kopf gegen die Wand schlagen, um zu verstehen, wie die AST-Modifikatoren arbeiten, ist es schwierig, ein leicht zu manipulieren AST (das heißt eine, die gut zu dem Problembereich abbildet) zu erhalten
-
Es erhöht die Übersetzungszeiten enorm, auch für "mittel" -groß Grammatiken
-
Syntax ist zu Schwergewicht. Es ist eine Tatsache des Lebens, die in C / C ++ Sie Code (das heißt zwischen Deklaration und Definition) kopieren müssen. Allerdings scheint es, dass in boost :: spirit, wenn Sie eine Grammatik <> deklarieren, müssen Sie einige Dinge, 3-mal wiederholen: D (wenn Sie Äste wollen, das ist es, was ich will: D)
Andere als diese, ich glaube, sie einen ziemlich guten Job mit dem Parser hat, die Grenzen von C ++ gegeben. Aber ich denke, sie sollten es mehr verbessern. Die Geschichte Seite beschreibt, dass es ein „dynamischer“ Geist vor dem aktuellen „statisch“ Geist war; Ich frage mich, wie viel schneller und wie viel besser Syntax hatte.
Ich würde sagen, das größte Problem das Fehlen einer Diagnose oder anderer Hilfe für Grammatik Probleme ist. Wenn Ihre Grammatik mehrdeutig ist, kann der Parser nicht analysieren, was Sie es erwarten, und es gibt keine gute Möglichkeit, dass zu bemerken.