diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 90c7faa1d..19b393ae8 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -412,7 +412,7 @@ namespace util { ] (StrView toParse) -> IterEval { - uint consumed{0}; + size_t consumed{0}; IterResult results; do { @@ -460,6 +460,48 @@ namespace util { } + /** accept some structure enclosed into a bracketing construct. + * \param isOptional if the bracketing can be omitted */ + template + auto + bracketedConnex (C1&& openingConnex + ,C2&& closingConnex + ,C3&& bodyConnex + ,bool isOptional) + { + using Res = typename decay_t::Result; + return Connex{[opening = forward(openingConnex) + ,closing = forward(closingConnex) + ,body = forward(bodyConnex) + ,isOptional + ] + (StrView toParse) -> Eval + { + auto bracket = opening.parse (toParse); + if (bracket.result or isOptional) + { + size_t consumed = bracket.consumed; + bool expectClose{bracket.result}; + auto eval = body.parse (toParse.substr(consumed)); + if (eval.result) + { + consumed += eval.consumed; + if (expectClose) + bracket = closing.parse (toParse.substr(consumed)); + if (bracket.result or not expectClose) + { + consumed += bracket.consumed; + return Eval{move (*eval.result) + ,consumed + }; + } + } + } + return Eval{std::nullopt}; + }}; + } + + /** @@ -606,6 +648,21 @@ namespace util { template auto repeat (SPEC&& clauseDef); + template + auto bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef); + + template + auto bracket (string bracketSpec, SPEC&& bodyDef); + + template + auto bracket (SPEC&& bodyDef); + + template + auto bracketOpt (string bracketSpec, SPEC&& bodyDef); + + template + auto bracketOpt (SPEC&& bodyDef); + private: Eval& eval() { return *this;} }; @@ -696,7 +753,74 @@ namespace util { { return accept_repeated (NullType{}, forward(clauseDef)); } - + + /** + * Start Syntax with a sub-clause enclosed into a _bracketing construct._ + * The »bracket« is defined as syntax for the _open marker_ and _close marker._ + * These are consumed without generating model elements. The parse fails unless + * the full sequence `open body close` can be matched. + */ + template + auto + accept_bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef) + { + return accept( + bracketedConnex (Parser{forward(openDef) } + ,Parser{forward(closeDef)} + ,Parser{forward(bodyDef) } + ,false // bracket mandatory, not optional + )); + } + + /** + * Start Syntax with a bracketed sub-clause, with given single-char delimiters. + * \param bracketSpec a 2-char string, e.g. "{}" to expect curly braces. + */ + template + auto + accept_bracket (string bracketSpec, SPEC&& bodyDef) + { + if (bracketSpec.size() != 2) + throw err::Invalid{"Bracket spec with opening and closing character expected"}; + return accept( + bracketedConnex (Parser{"\\"+bracketSpec.substr(0,1)} + ,Parser{"\\"+bracketSpec.substr(1,1)} + ,Parser{forward(bodyDef) } + ,false // bracket mandatory, not optional + )); + } + + /** Start Syntax with a sub-clause enclosed in parentheses */ + template + auto + accept_bracket (SPEC&& bodyDef) + { + return accept_bracket ("()", forward(bodyDef)); + } + + /** Start Syntax with a sub-clause, _optionally_ enclosed into brackets. */ + template + auto + accept_bracketOpt (string bracketSpec, SPEC&& bodyDef) + { + if (bracketSpec.size() != 2) + throw err::Invalid{"Bracket spec with opening and closing character expected"}; + return accept( + bracketedConnex (Parser{"\\"+bracketSpec.substr(0,1)} + ,Parser{"\\"+bracketSpec.substr(1,1)} + ,Parser{forward(bodyDef) } + ,true // bracket optional, can be omitted + )); + } + + template + auto + accept_bracketOpt (SPEC&& bodyDef) + { + return accept_bracketOpt ("()", forward(bodyDef)); + } + + /** * Combinator: extend this Syntax clause by expecting a further sub-clause @@ -794,6 +918,54 @@ namespace util { return seq (accept_repeated (forward(clauseDef))); } + /** + * Combinator: extend this Syntax with a further sequenced sub-clause in brackets. + * @see accept_bracket() + */ + template + template + auto + Syntax::bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef) + { + return seq (accept_bracket (forward(openDef) + ,forward(closeDef) + ,forward(bodyDef))); + } + + template + template + auto + Syntax::bracket (string bracketSpec, SPEC&& bodyDef) + { + return seq (accept_bracket (move (bracketSpec) + ,forward(bodyDef))); + } + + template + template + auto + Syntax::bracket (SPEC&& bodyDef) + { + return seq (accept_bracket (forward(bodyDef))); + } + + template + template + auto + Syntax::bracketOpt (string bracketSpec, SPEC&& bodyDef) + { + return seq (accept_bracketOpt (move (bracketSpec) + ,forward(bodyDef))); + } + + template + template + auto + Syntax::bracketOpt (SPEC&& bodyDef) + { + return seq (accept_bracketOpt (forward(bodyDef))); + } + }// namespace parse diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 6798d791d..799327368 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -533,13 +533,31 @@ namespace test { - /** @test - * - */ + /** @test define syntax with bracketed sub-expressions */ void acceptBracketed() { - UNIMPLEMENTED ("bracketed"); + string word{"\\w+"}; + + CHECK (not accept(word).bracket(word) .parse("so sad")); + CHECK ( accept(word).bracketOpt(word).parse("so sad")); + CHECK ( accept(word).bracketOpt(word).parse("so (sad)")); + + CHECK (accept_bracket(word).parse(" ( again ) ").getResult().str() == "again"); + + CHECK (not accept_bracket(word) .parse("(again")); + CHECK (not accept_bracketOpt(word).parse("(again")); + CHECK ( accept_bracketOpt(word).parse("again)")); // just stops before the trailing ')' + CHECK ( accept_bracketOpt(word).parse("again)").consumed() == 5); + CHECK ( accept_bracketOpt(word).parse(" again")); // backtracks also over the whitespace + + CHECK (not accept_bracket("[]",word).parse("(again)")); + CHECK (not accept_bracket("[]",word).parse("[again)")); + CHECK (not accept_bracket("[]",word).parse("(again]")); + CHECK ( accept_bracket("[]",word).parse("[again]")); + CHECK ( accept_bracket("a","n","...").parse("again")); // arbitrary expressions for open / close + CHECK (not accept_bracket("a","n","...").parse(" gain")); // opening expression "a" missing + CHECK (not accept_bracket("a","n", word).parse("again")); // "\\w+" consumes eagerly => closing expression not found } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index fb173a754..af2ac61c5 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55219,16 +55219,13 @@ - - - +

top-Level-Einstiege bieten ⟹ Präfix accept_

- -
+
@@ -55461,7 +55458,7 @@ - + @@ -55952,9 +55949,7 @@ - - - +

Das ist eine mehrstufige Kette @@ -56589,9 +56584,7 @@ - - - +

...hier ist die Logik umgedreht @@ -56611,9 +56604,7 @@ - - - +

linker Zweig: ein sub-Model, in dem irgend ein Zweig gematched hat @@ -56642,9 +56633,7 @@ - - - +

Der Argument-Pack muß stets am Ende stehen @@ -56671,9 +56660,7 @@ - - - +

Grundidee: man baut die neue, umgebaute Typ-Sequenz in den variadischen Argumenten eines beliebigen Templates, das selbst als Template-Template-Parameter gegeben wird. Damit kann man unmittelbar in einem einzigen Zug das redefinierte Ziel-Template konstruieren, ohne erst in eine andere Verarbeitungs-Domäne (tuple, Typsequenz, Typliste) mappen zu müssen. Zudem kann das gleiche Verarbeitungs-Template auch Spezial-Belegungen für Hilfs-Operationen mit anbieten, und man kann gleich die häufigsten verwandten Tools in einer einzigen Definition zur Verfügung stellen. @@ -56693,9 +56680,7 @@ - - - +

da zeichnet sich ein Schema ab, das die bekannten Sequenz-Umordnungen sehr direkt ausführt, ohne erst in eine andere Repräsentation (wie Typelist) zu mappen. Trotzdem ist der Aufwand O(n), für das Umkehren der Sequenz sogar O(n²) @@ -56731,9 +56716,7 @@ - - - +

...Hinweis darauf ist der Umstand, daß ich gar nicht mehr viel auswerten / prüfen muß, sondern direkt der Match auf die Konstruktor-Argumente den Rest der Logik erledigt. @@ -56754,9 +56737,7 @@ - - - +

da geht nämlich eine Branch-ID ein, und die muß <= TOP sein @@ -56787,9 +56768,7 @@ - - - +

wenn am Ende keine weitere Iteration akzeptiert werden kann, ist das kein Fehler, sondern wir stehen hinter der zuletzt akzeptierten Iteration @@ -56799,9 +56778,7 @@ - - - +

ein Trenner muß nicht gegeben sein (dann wird lediglich der Rumpf iteriert); wenn aber ein Trenner gegeben ist, dann wird er beim 1.Mal explizit übersprungen (darf also nicht da sein), bei allen anderen Iterationen wird er zu Beginn der Iteration erwartet @@ -56816,9 +56793,7 @@ - - - +

man kann sich Situationen denken.... aber dann hätte man auch stets dieses Ergebnis-Tupel zu handhaben. @@ -56844,9 +56819,7 @@ - - - +

weil wir bisher keinen generischen Rekursions-Mechanismus vorsehen und ansonsten die Zahl der Iterationen erst in einem Post-Proecssing-Schritt geprüft werden könnte. @@ -56856,9 +56829,7 @@ - - - +

Es würde sich um einige Randfälle handeln, denn im Regelfall ist eine Iteration offen / abzählbar. Und wir müßten in eine derart performance-kritische Situation vorstoßen, in der eine Heap-Allokation prohibitiv wäre @@ -56899,9 +56870,7 @@ - - - +

dem inzwischen etablierten Schema der Syntax-Klauseln zufolge muß jeder Junktor etwas mit der aktuellen Syntax-Spec anknüpfen. Wenn nun ein iter()-Prädikat essentiell eine neue Syntax anfängt, wäre eine implizite Konvention zu treffen, was mit der bestehenden Klausel passiert. Naheliegend wäre eine Sequenz; dann besteht aber die Gefahr, daß in der Praxis oft eine leere Sequenz spezifiziert wird (wäre noch akzeptabel) — und man zur Model-Anknüpfung stets dieses Tupel aufmachen müßte (schröcklich) @@ -56912,9 +56881,7 @@ - - - +

dann muß man aber die Postfix-takes-all-Logik akzeptieren @@ -56932,27 +56899,24 @@ - + - - - +

stets mit Präfix accept_

- -
+
- - - + + + - + @@ -56961,22 +56925,21 @@ - + - - + + - + + - - - +

der Implementierung nach ist es ein Dekorator @@ -56987,7 +56950,8 @@ - + + @@ -56997,7 +56961,8 @@ - + + @@ -57006,8 +56971,10 @@ - - + + + + @@ -57015,6 +56982,41 @@ + + + + + + + + + +

+ ....muß dann aber durch die Entscheidungs-Logik sicherstellen, daß dann auch die zugehörige schließende Klammer entweder erwartet, oder übersprungen wird. +

+ + +
+
+ + + +
+ + + + + + + + + + + + + + + @@ -57066,13 +57068,21 @@ - + - + + + + + + + + +