diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index cd7b524a8..2e188537b 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -40,7 +40,7 @@ namespace util { namespace parse { - + using std::move; using std::forward; using std::optional; @@ -67,92 +67,141 @@ namespace util { ,_MaxBufSiz::siz); }; - /** - * Data storage base for sum type with selector - */ - template - struct OpaqueSumType + template + class BranchCase { - size_t case_{0}; + public: + static constexpr auto TOP = sizeof...(TYPES) -1; + static constexpr auto SIZ = _MaxBufSiz::siz; + + template + using SlotType = std::tuple_element_t>; + + protected: + size_t branch_{0}; alignas(int64_t) std::byte buffer_[SIZ]; - template - T& + template + TX& emplace (INITS&&...inits) { - case_ = slot; - return * new(&buffer_) T(forward (inits)...); - } - - }; - - template - class SumType - : private OpaqueSumType<_MaxBufSiz::siz> - { - public: - static constexpr size_t TOP = sizeof...(TYPES); - static constexpr size_t SIZ = _MaxBufSiz::siz; - using _Opaque = OpaqueSumType; - - template> - SumType (INITS&& ...inits) - { - _Opaque::template emplace (forward (inits)...); + return * new(&buffer_) TX(forward (inits)...); } template TX& access () { - return * std::launder (reinterpret_cast (& _Opaque::buffer_[0])); + return * std::launder (reinterpret_cast (&buffer_[0])); } + /** apply generic functor to the currently selected branch */ + template + auto + selectBranch (FUN&& fun) + { + if constexpr (0 < idx) + if (branch_ < idx) + return selectBranch (forward(fun)); + return fun (get()); + } + + BranchCase() = default; + public: + template + auto + apply (FUN&& fun) + { + return selectBranch (forward (fun)); + } + + ~BranchCase() + { + apply ([this](auto& it) + { using Elm = decay_t; + access().~Elm(); + }); + } + + template + BranchCase (size_t idx, INITS&& ...inits) + { + branch_ = idx; + apply ([&,this](auto& it) + { using Elm = decay_t; + emplace (forward (inits)...); + }); + } + + BranchCase (BranchCase const& o) + { + branch_ = o.branch_; + BranchCase& unConst = const_cast (o); + unConst.apply ([this](auto& it) + { using Elm = decay_t; + this->emplace (it); + }); + } + + BranchCase (BranchCase && ro) + { + branch_ = ro.branch_; + ro.apply ([this](auto& it) + { using Elm = decay_t; + this->emplace (move (it)); + }); + } + + friend void + swap (BranchCase& o1, BranchCase o2) + { + using std::swap; + BranchCase tmp; + o1.apply ([&](auto& it) + { using Elm = decay_t; + tmp.emplace (move (o1.access())); + }); + swap (o1.branch_,o2.branch_); + o1.apply ([&](auto& it) + { using Elm = decay_t; + o1.emplace (move (o2.access())); + }); + o2.apply ([&](auto& it) + { using Elm = decay_t; + o2.emplace (move (tmp.access())); + }); + } + + BranchCase& + operator= (BranchCase ref) + { + swap (*this, ref); + return *this; + } + + template + auto + moveExtended() + { + using Extended = BranchCase; + Extended& upFaked = reinterpret_cast (*this); + return Extended (move (upFaked)); + } + + size_t selected() const { - return _Opaque::case_; + return branch_; } - template - using SlotType = std::tuple_element_t>; - - template - SlotType& + template + SlotType& get() { - return access>(); - } - - template - void - select (size_t slot, FUN&& fun) - { - REQUIRE (slot <= sizeof...(TS)); - if constexpr (sizeof...(TS)) - if (0 < slot) - select (slot-1, forward(fun)); - fun (access()); - } - - template - void - destroyIt (TX& it) - { - it.~TX(); - } - - void - destroy (size_t slot) - { - select (slot, [&](auto& it){ destroyIt(it); }); - } - - ~SumType() - { - destroy (_Opaque::case_); + return access>(); } }; diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 4750e4f6f..8f27bf1b4 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -216,15 +216,33 @@ namespace test { void acceptAlternatives() { - using Sum = SumType; -SHOW_EXPR(sizeof(Sum)); - Sum sumt{42}; -SHOW_EXPR(sumt.selected()); -SHOW_EXPR(sumt.SIZ); -SHOW_EXPR(sumt.TOP); -SHOW_TYPE(Sum::_Opaque) -SHOW_EXPR(sumt.get<1>()); -SHOW_EXPR(sumt.get<0>()); + using Branch = BranchCase; +SHOW_EXPR(sizeof(Branch)); + Branch b1{1, 42}; +SHOW_EXPR(b1.selected()); +SHOW_EXPR(b1.SIZ); +SHOW_EXPR(b1.TOP); +SHOW_EXPR(b1.get<1>()); +SHOW_EXPR(b1.get<0>()); + Branch b2{0,'x'}; +SHOW_EXPR(b2.selected()); +SHOW_EXPR(b2.get<1>()); +SHOW_EXPR(b2.get<0>()); + Branch b3{b1}; +SHOW_EXPR(b3.selected()); +SHOW_EXPR(b3.get<1>()); +SHOW_EXPR(b3.get<0>()); + b3 = b2; +SHOW_EXPR(b3.selected()); +SHOW_EXPR(b3.get<1>()); +SHOW_EXPR(b3.get<0>()); + auto bx = b1.moveExtended(); +SHOW_EXPR(sizeof(bx)) +SHOW_EXPR(bx.SIZ); +SHOW_EXPR(bx.TOP); +SHOW_EXPR(bx.selected()); +SHOW_EXPR(bx.get<1>()); +SHOW_EXPR(bx.get<0>()); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 38422a0dc..d579fe3cc 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55148,27 +55148,21 @@ - - - +

matchAtStart(str, regex) : partieller Match am Anfang verankert

- -
+ - - - +

wichtig für Text-Parsing, wo man bestimmte Tokens akzeptieren  möchte, aber durchaus ein Rest »dahinter« übrig bleibten darf.

- -
+
@@ -55250,9 +55244,7 @@ - - - +

not from first principbles... @@ -55279,9 +55271,7 @@ - - - +

Bekanntermaßen ist die Fehlerbehandlung die Stelle, an der jedes schöne Design eines Regelsystems zusammenbricht. Deshalb kümmern wir uns erst mal um die schönen Dinge. @@ -55291,9 +55281,7 @@ - - - +

anders als in einer funktionalen Sprache stellt sich aus Sicht der imperativen Verarbeitung sofort die Frage, wie die Beweglichkeit bzw die Vielgestaltigkeit verschiedener Elementar-Parser unter ein gemeinsames Verarbeitungsschema gebracht werden kann (das dann auch noch akzeptable Kosten hat). Da ich hier vor allem auf Gelegenheits-Gebrauch ziehle, also auf eine Situation, wo beiläufig gewisse syntaktische Strukturen bezeichnet werden sollen, betone ich vor allem die Optimierbarkeit der Syntax-Definition. Das führt zu der Forderung, Indirektionen so lange wie möglich zu vermeiden oder verzögern. @@ -55310,94 +55298,73 @@ - - - +

Es erscheint mir als Pedanterie, wenn ich nun einen extra terminal-Builder-Schritt einführen würde, nur um das "bin fertig mit der Definition" zu markieren. Dazu gibt es (cost) Variablen-Definitionen. Außerdem sehe ich schon das Thema mit den explizit konfigurierten Fehlermeldungen, und dafür würde ich noch eine separate Fehlerbehandlungs-Spec brauchen. Eben diese Art von weitverzweigtem, feingranularem Framework wollte ich vermeiden. Daher neige ich zu einem Design, in dem der Parser stateless ist, die Syntax-Spec aber stateful, weil sie dann auch gleich noch das Model enthalten kann, auf das sie ohnehin stringent getypt ist. Dafür nehme ich in Kauf, die Syntax-Definition (also die eigentlichen Parser-Combinators) mit dem Auswertungs-Zustand zu vermischen, grade auch, weil man auf diesem Weg wieder in die Definition von Sub-Klauseln einsteigen kann

- -
+
- - - +

Wenn man schon ein zustandsbehaftetes Objekt akzeptiert, könnte auch Zuweisbarkeit bequem sein (man muß dann ja ohnehin aufpassen). Allerdings steht das im Konflikt mit dem Ansatz einer fein-granularen Typisierung, welche die Modell-Struktur abbildet. Und die Entscheidung, die recursive-descent-Struktur als parse-λ einzubetten, verbietet explizit die Zuweisbarkeit, denn man kann (und darf) nicht wissen, was in der Closure steckt

- -
+
- - - +

...das ist eine Grundentscheidung, vor allem motiviert durch die einfachere Implementierbarkeit (aber auch unterstützt dadurch, daß ich mir in vielen relevanten Fällen eine einfachere, fluidere  Verwendung erhoffe). Das hat die wichtige Konsequenz, daß wir nicht mit Variadics arbeiten, sondern die Summen- und Produkttypen schrittweise aufbauen

- -
+
- - - +

Das heißt, es gibt keinen explizit gespeicherten »Binding-Funktor«, sondern stattdessen eine spezielle Kompositions-Mechanik, mit der man eine Transformation an die Parse-Funktion innerhalb des Optional  realisieren kann. Also ein monadisches Muster hier

- -
+
- - - +

Die Parser-Kombinatoren sollen also nicht direkt vom Benutzer angefaßt werden, wodurch sie auch keiner besonderen Absicherung bedürftig sind

- -
+
- - - +

separater Namespace sinnvoll (util::parse)

- -
+ - - - +

...da für den intendierten Nutzen typischerweise Syntax-Spezifikationen bei den Basis-Konstanten und Definitionen einer anderen Einrichtung mit abgelegt werden, und in den meisten Fällen der Einstieg erfolgen kann per util::accept

- -
+
@@ -55406,9 +55373,7 @@ - - - +

das ist mehr als eine Policy: Struktur-Bindeglied ⟹ Connex @@ -55423,9 +55388,7 @@ - - - +

das ist essentiell für den Vorgang des Parsens: es wird jeweils ein Präfix-Match gesucht, dann »akzeptiert« und mit dem Rest dahinter weitergemacht — wobei allerdings sich dieser »Rest« erst durch den Match überhaupt definiert. Leider bieten die Regex-Operationen nur ein find (mit beliebigem Match irgendwo) oder match (auf die ganze Sequenz). @@ -55435,9 +55398,7 @@ - - - +

das löst das Problem, ist aber keine gute Lösung; denn das ist ein subtiler Punkt, den man dem User überlassen muß. Man könnte noch versuchen, diese Verankerung am Anfang automatisch mit dazuzubauen, was aber nur geht, wenn die Regular-Expression als String-Definition geliefert wird. Also ungeschickt wie man's dreht @@ -55467,16 +55428,13 @@ - - - +

...woduch der Code ziemlich verwirrend wird  ⟹  idealerweise wäre die gesamte Combinator-Logik in den buildConnex()-Overloads

- -
+
@@ -55490,16 +55448,13 @@ - - - +

ist viel näher an der Grundidee aus der Funktionalen Programmierung, und dennoch in einer low-Level-Funktion, mit der man direkt nix anfangen kann. Denn darauf läuft das Design ja immer mehr hinaus: ein eingekappseltes Kombinator-Framework.

- -
+
@@ -55592,24 +55547,19 @@ - - - +

wenn das Vorgänger-Element eine passende Model-Variante ist (tuple, AltTypes, array), dann wird an dieses angebaut

- -
+
- - - +

Angenommen, die Syntax sieht wie folgt aus: @@ -55621,8 +55571,7 @@ Wenn der geklammerte Ausdruck als sub-Syntax formuliert ist, würden alle Model-Elemente in ein einziges 7-Tupel nivelliert, obwohl man eingentlich ein 3-Tupel mit verschachtelung wollte

- -
+
@@ -55633,16 +55582,13 @@ - - - +

...da wir keinen expliziten Binding-Funktor speichern, sondern ihn in die Parse-Funktion einarbeiten, kann auch der Ergebnis-Typ durch eine weitere solche Komposition geLIFTet werden.

- -
+
@@ -55653,16 +55599,13 @@ - - - +

Das heißt, die Operation zur inkrementellen Erweierung erkennt den gruppierenden Summen/Produkttyp im Parse-Ergebnis und wird demenstprechend, »Klammer schließen« oder »anhängen«. In der Implementierung der Verknüpfungs-Operation liegt also eine Fallunterscheidung oder ein double-Dispatch vor, analog zum Pattern-Match in der funktionalen Programmierung. Soweit so gut.

- -
+
@@ -55672,9 +55615,7 @@ - - - +

Beispiel @@ -55692,14 +55633,11 @@   ).seq("z")

- -
+
- - - +

⟹ wird zu... @@ -55714,14 +55652,11 @@       .seq("z")

- -
+
- - - +

⟹ wird zu... @@ -55733,14 +55668,11 @@   Syntax( 'x' 'y' 'z' | tuple<Model(x), Model(y), Model(z)> )

- -
+
- - - +

⟺ äquivalent zu... @@ -55758,8 +55690,7 @@      .seq("z")

- -
+
@@ -55774,53 +55705,41 @@ - - - +

...denn das wäre nahe an der Stelle, wo's zur Erkennung gebraucht wird, und ist damit auch ziemlich tief in der Implementierung verborgen

- -
+
- - - +

will sagen, wer ein solches zweimal verschachteltes accept(accept()) anfängt, und dann nicht in einen Produkt / Summenterm einsteigt, ist selber schuld

- -
+
- - - +

man könnte es sogar nur im Result-Typ verstecken

- -
+ - - - +

...was zwar auch etwas grenzwertig wäre, aber noch vertretbar, wenn es als eine Tagging-Subklasse realisiert ist. Dann ist zwar die Bedingung gebrochen, daß Connex::Result ≡ parse-Funktion-Result, aber ersteres wäre eine Subklasse und somit noch kompatibel

- -
+
@@ -55845,16 +55764,13 @@
- - - +

Ansatz: ModelJoin<R1,R2>::Result

- -
+
@@ -55885,16 +55801,13 @@ - - - +

nebenbei: namespace parse einführen

- -
+
@@ -55929,9 +55842,7 @@ - - - +

...weil wir die Typen ja permanent umbauen; @@ -55948,14 +55859,11 @@ - - + - - - +

Das wäre schon denkbar, da wir alle Aufrufe hier kontrollieren — und diese Model-Klassen nicht außerhalb des Parsers verwendet werden sollten, sondern nur in den Combinatoren selber und (lesend) in Binding-λ. @@ -55969,8 +55877,7 @@ - - + @@ -56004,16 +55911,13 @@ - - - +

oder mit \\b

- -
+
@@ -56101,29 +56005,23 @@ - - - +

bei der Sequenz wurde die gleiche Annahme gemacht, und daß der Optimiser das schon wuppen wird

- -
+
- - - +

warum? weil eine Folge zunehmend variantenreicherer Summen-Typen entsteht, und die Puffergröße ist das Maximum des benötigten Platzes

- -
+
@@ -56135,24 +56033,21 @@ - - - +

das ist ähnlich zu einer persistenten Datenstruktur: man fragt den jeweiligen Vorgänger-Datentyp mit dem dekrementierten Selektor an; ist der Selektor 0, wird der dort fest gecodete Typ-Zugriff verwendet

- -
+
- - + + @@ -56178,16 +56073,13 @@ - - - +

das heißt, die Operationen führt stets derjenige partielle Summen-Typ aus, bei dem der aktive Zweig auf der Top-Position (am Anfang) steht; der aktuelle Selektor-Wert legt also das »Stockwerk« fest, auf dem gearbeitet wird

- -
+
@@ -56204,16 +56096,13 @@ - - - +

...weil der Compiler ja nichts über den Selektor-Wert weiß, muß er in jedem Fall die gesamte rekursive Kette instantiieren; das Ende der Typ-Sequenz stellt also eine zweite Abbruchbedinung der Rekursion bereit, und diese muß so in die Kontrollstruktur eingebunden sein, daß die rekursiven Instantiierungen wirklich aufhören

- -
+
@@ -56239,16 +56128,13 @@ - - - +

Fazit: mit Einschränkumg umsetzbar...

- -
+
@@ -56267,9 +56153,7 @@ - - - +

Und zwar im Bezug auf allgemeine Handwerksregeln... @@ -56286,22 +56170,42 @@ - - + - - - +

Das fängt schon damit an, daß man doch noch getemplatete Funktionen für die Elementar-Operationen schreiben muß (zumindest in C++17). Dazu kommt dann diese doch einigermaßen trickreiche generische select-Operation

+
+
+ + + + +

+ ...weil praktisch alle Kern-Methoden nun in zweifacher Ausfertigung gecodet werden müssen: einmal rekursiv, und einmal für den Abschluß +

+
+ + + + +

+ ...denn mein Compiler weigert sich, eine Argumentliste für die Spezialisierung mit dem Parameter-Pack zu beginnen (ich versteh den Grund nicht wirklich, denn ich dachte, in anderen Fällen wäre das durchaus möglich gewesen, denn die Spezialisierungen wären eindeutig +

+ +
+
+ + +
@@ -56311,21 +56215,16 @@ - - - +

konkret stellt das zufälligerweise kein Problem dar

- -
+ - - - +

das erscheint zunächst wie »glückliche Umstände«; @@ -56334,30 +56233,24 @@ bei genauerer Überlegung wird aber klar, daß ich diese Einsicht bereits intuitiv angewendet hatte, als ich von Vornherein auch ein Selektor-Feld mit disponierte; es ist nämlich so, daß wir uns vermöge des Selektor-Feldes genau diese explizite Info zur Compile-Zeit beschaffen können, indem der aufrufende Code ein switch-case auf den Selector macht.

- -
+
- - - +

das im Prototyp entworfene select(λ) ist ein Variant-Visitor

- -
+
- - - +

template<typename TX, typename...TS, class FUN> @@ -56390,14 +56283,11 @@   }

- -
+ - - - +

Das funktioniert nur wenn man einige subtile Details richtig hinbekommt.... @@ -56417,8 +56307,7 @@ - - + @@ -56431,34 +56320,49 @@ - - - +

wenn man ohnehin eine bestimmte Syntax parsen möchte, ist es naheliegend und natürlich, auf den vorliegenden konkreten Zweig zu prüfen und zu verzweigen; in dieser Form erscheint das eine vertretbare und nicht weiter gefährliche Herangehensweise. Dieser Ansatz kann allerdings durchaus in einen schwer wartbaren Zustand abgleiten, wenn die Syntax komplex und heterogen strukturiert ist; dann muß man die Varianten mehr oder weniger im Kopf haben. Für dieses Problem sehe ich keine allgemeingültige Lösung...

- -
+
- - - +

Das ist eine schöne neue Möglichkeit, die sich durch die generischen Lambdas auftut; durchaus möglich, daß in manchen Fällen eine solche Formulierung eine drastische Vereinfachung darstellt, beispielsweise wenn alle Zweige jeweils einen RegExp-Match mit äquivalent angeordneten Capture-Groups beinhalten; die Einzelfälle würden damit sozusagen transparent verschmolzen.

- -
+
+ + + + + + + + + + + + + + + + + + + + +
@@ -103448,16 +103352,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- - - +

...also weiß, daß man nicht mutwillig mit Whitespace herumspielt, daß man eine stabile Spec am Besten generiert, daß man Unicode-Sonderzeichen vermeidet und sich generell um die Eindeutigeit der Syntax kümmert, selbst wenn einem niemand auf die Finger schaut

- -
+
@@ -103523,29 +103424,23 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

eine ID der Domain-Anbindung / Domain-Ontology / Library

- -
+
- - - +

high-Level-Beschreibung der Funktionalität dieser Node ... steht wahrscheinlich in Beziehung zur Asset-ID

- -
+
@@ -103668,23 +103563,18 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

Begründung: die Schwachstelle jedes Parsers liegt bei den Anknüpfungen. Eine Library wird unvermeidbar eine Objekt- oder Baumnotation mit sich bringen, und sei es bloß eine Konvention auf Basis von std::tuple. Eine gut ausgereifte und weit verbreitete Library wird vermutlich hier eine schwergewichtige Lösung bieten; rein nach Bauchgefühl erwarte ich, daß eine solche Lösung aufgebläht, schlecht in der Performence, extrem tricky, schwer lesbar ist, oder uns ungünstige Verhaltensmuster aufzwingt (Monaden!).

- -
+
- - - +

Demgegenüber erscheint mir eine minimalistische ad-hoc-Lösung viel attraktiver, sofern sie auf dem Niveau von »ein paar Abkürzungs-Notationen« bleibt. Das heißt, sie sollte die Mechanik des Parsens nicht zu verbergen versuchen. @@ -103696,53 +103586,43 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) Maßstab ist für mich der bereits etablierte Gebrauch von Regular-Expressions (header regexp.hpp): Im Grunde haben wir nur noch eine convenience-Verpackung eines Iterators, ein paar, Typ-Abkürzungen und Imports, sowie den Stil, jeweils im Implementierungs-Header die Grammatik in Form von Reg-Exp-Bausteienen zu definieren. Etwas Vergleichbares sollte auch für Parser-Kombinatoren möglich sein, wenn man sich diesem Gebrauch in mehreren Schritten nähert (und dabei Erfahrungen sammelt)

- -
+
- - - +

man könnte eine klammern-zählende Hilfsfunktion schreiben und dann den Inhalt der Klammern per RegExp zerlegen

- -
+
- - - +

Zielvorgabe: im Grunde zu Fuß machen (mit Abkürzugen)

- -
+
- - - +

...weil ich eigentlich keinen Aufwand in sowas stecken sollte, aber auch keine Bastel-Lösung an einer so zentralen Stelle möchte, und jetzt schon wieder zwei Tage daran herumprokrastiniere und den inneren Konflikt nicht lösen kann. Ja, es reizt mich, es besser zu lösen als all die Libraries, die ich gesehen habe und nicht im Projekt haben möchte. Und »Pragmatismus« empfinde ich als Kränkung und Niderlage hier

- -
+
@@ -103753,29 +103633,23 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

siehe text-template.cpp

- -
+ - - - +

der Aufwand zum Parsen eines gequoteten CSV ist im Regelfall kaum höher als die stupide Zerlegung an einem Escape-Zeichen; dafür aber ist die Notation als kommaseparierte Liste sehr intuitiv, was Testen und Diagnose erleichtern wird. Quotes müssen wir dann aber vorsehen, weil komplexe Typen möglicherweise Kommata enthalten können, und ich diese Tür nicht sofort schließen möchte (zwar wird es vermutlich darauf hinauslaufen, daß die Library-Binding-Plug-ins ihre eigenen, synthetischen Typ-Bezeichner einführen)

- -
+
@@ -103784,46 +103658,37 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

wir brauchen hier definitiv keinen Stack, sondern nur einen internen count-down

- -
+
- - - +

dafür wurde dieser Mechanismus ja grade geschaffen, und das State-Core-API ist leicht zu dekorieren

- -
+
- - - +

die Normalisierung hier nicht zu weit treiben; es ist ein low-Level-Interface und dient in 99% der Fälle dazu, Hash-Keys zu erzeugen

- -
+
@@ -152296,9 +152161,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

sondern verwenden einen Parser-Builder @@ -152313,9 +152176,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

ein sub-Parser wird akzeptiert, aber es ist kein Fehler wenn er übrhaupt nicht greift; partielle Anwendbarkeit aber ist ein Fehler @@ -152325,9 +152186,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

repetitive Strukturen @@ -152351,9 +152210,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

als Teil der Kombination wird auch Kombination der Ergebnisse  gehandhabt @@ -152376,9 +152233,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

Dazu gehören schon die sehr geläufigen Bausteine wie optional und sequenced-by. Mit erweiterter Vorschau und Modell-Interaktion ist man schnell nicht mehr kontextfrei. @@ -152388,9 +152243,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

Hier gibt es mehrere Faktoren zu beachten @@ -152417,9 +152270,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

die schöne Formulierung der Produktionen in der DSL täuscht meist darüber hinweg, daß es oft schwer verständlich ist, was der Parser-Code eigentlich macht, besonders wenn implizite Konventionen involviert sind. Fehlerbehandlung ist so schwer und mühsam wie stets im Parserbau, aber Fehlerbehandlung zusammen mit spezieller Steuerlogik in den Anknüpfungen führt schnell zu nahezu unwartbarem Code. Aber auch im anderen Extrem, bei einem Framework welches lediglich den Syntaxbaum als Algebraic-Type liefert, ist die dann folgende, eigentliche Auswertung per Tree-Walk oft extrem schwer zu verstehen, da man die gesamte Grammatik im Kopf haben muß @@ -152429,9 +152280,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

Der einzelne Parser im zusammengesetzten Parser-Ausdruck ist opaque und daher nicht der Optimierung zugänglich. Anders als für einen LR-Parser können verschiedene Alternativen nicht so einfach zugleich verfolgt werden. Dadurch entsteht viel Aufwand durch prüfen und Backtracking von Alternativen und die Kosten wachsen u.U. exponentiell in der Läge der Eingabe oder Umfang der Grammatik. Hinzu kommt, daß für ein Modell oft flexible Meta-Strukturen geschaffen werden müssen, nur um sie nach Traversierung wieder zu verwerfen