From 6dc2561262b0428bfd4b18f070e50d99259cb2c2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 22 Jan 2025 16:42:28 +0100 Subject: [PATCH] Library: draft mechanics for repetitive sequence --- src/lib/parse.hpp | 6 +- tests/library/parse-test.cpp | 72 ++++++++++++- wiki/thinkPad.ichthyo.mm | 191 ++++++++++++++++++++++++++++++++++- 3 files changed, 264 insertions(+), 5 deletions(-) diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 8634a2917..734e77459 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -158,7 +158,7 @@ namespace util { struct SeqModel : tuple { - static constexpr size_t SIZ = sizeof...(RESULTS); + static constexpr size_t N = sizeof...(RESULTS); using Seq = lib::meta::TySeq; using Tup = std::tuple; @@ -176,6 +176,9 @@ namespace util { { } Tup&& extractTuple() { return move(*this); } + + template + auto get() { return std::get (*this); } }; @@ -188,6 +191,7 @@ namespace util { : lib::BranchCase { using Alt = lib::BranchCase; + static constexpr size_t N = Alt::TOP; template using Additionally = AltModel; diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 643ef933d..a6ee688b2 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -79,6 +79,7 @@ namespace test { acceptTerminal(); acceptSequential(); acceptAlternatives(); + acceptIterWithDelim(); } @@ -222,7 +223,7 @@ namespace test { - /** @test TODO WIP define alternative syntax structures to match by parse. + /** @test define alternative syntax structures to match by parse. * - first demonstrate how a model with alternative branches can be * populated and gradually extended while searching for a match. * - then show explicitly the logic to check and select branches @@ -329,7 +330,7 @@ namespace test { CHECK (not syntax2.success()); syntax2.parse(s2); CHECK (syntax2.success()); - CHECK (syntax2.getResult().TOP == 2); // Note: further branch has been folded into an extended AltModel + CHECK (syntax2.getResult().N == 2); // Note: further branch has been folded into an extended AltModel CHECK (syntax2.getResult().selected() == 0); // ... string s2 still matched the same branch (#0) CHECK (syntax2.getResult().get<0>().str() == "brazen"); @@ -340,6 +341,73 @@ namespace test { CHECK (syntax2.getResult().get<2>().str() == "smarmy saviour"); CHECK (syntax2.getResult().get<2>().str(1) == "saviour"); } // Note: syntax for this branch #2 captured an additional word + + + + /** @test TODO define repetitive sequence with delimiter + * - demonstrate how actually to accept such a flexible sequence + */ + void + acceptIterWithDelim() + { //_______________________________________________ + // Demonstration: how repetitive sequence works.... + auto sep = buildConnex (","); + auto term = buildConnex ("\\w+"); + auto parseSeq = [&](StrView toParse) + { + using Res = decltype(term)::Result; + using IterResult = std::vector; + using IterEval = Eval; + uint consumed{0}; + IterResult results; + auto hasResults = [&]{ return not results.empty(); }; + while (true) + { + uint offset{0}; + if (hasResults()) + { + auto delim = sep.parse (toParse); + if (not delim.result) + break; + offset += delim.consumed; + } + auto eval = term.parse (toParse.substr(offset)); + if (not eval.result) + break; + offset += eval.consumed; + results.emplace_back (move(*eval.result)); + toParse = toParse.substr(offset); + consumed += offset; + } + return hasResults()? IterEval{move(results), consumed} + : IterEval{std::nullopt}; + }; + string s1{"Seit umschlungen, Millionen"}; + string s2{"beguile, extort, profit"}; + + auto e1 = parseSeq(s1); + CHECK (e1.result); + CHECK (e1.result->size() == 1); + CHECK (e1.result->at(0).str() == "Seit"); + CHECK (e1.result->at(0).suffix() == " umschlungen, Millionen"); + CHECK (e1.consumed == 4); + + auto e2 = parseSeq(s2); + CHECK (e2.result); + CHECK (e2.result->size() == 3); + CHECK (e2.result->at(0).str() == "beguile"); + CHECK (e2.result->at(1).str() == "extort" ); + CHECK (e2.result->at(2).str() == "profit" ); + CHECK (e2.result->at(0).suffix() == ", extort, profit"); + CHECK (e2.result->at(1).suffix() == ", profit"); + CHECK (e2.result->at(2).suffix() == "" ); + CHECK (e2.consumed == s2.length()); + + + //______________________________________________ + // DSL parse clause builder: iterative sequence... + auto syntax1 = accept("brazen").alt("bragging"); + } }; LAUNCHER (Parse_test, "unit common"); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f7da36608..602b441e7 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -56729,12 +56729,191 @@ - + + + + + + + + + + + +

+ wenn am Ende keine weitere Iteration akzeptiert werden kann, ist das kein Fehler, sondern wir stehen hinter der zuletzt akzeptierten Iteration +

+ + +
+
+ + + + + + +

+ 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 +

+ + +
+ + + + + + + + + + + + +

+ man kann sich Situationen denken.... aber dann hätte man auch stets dieses Ergebnis-Tupel zu handhaben. +

+ + +
+ + +
+ + + +
+
+ + + + + + + + + + + + + + + +

+ 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. +

+ + +
+
+ + + + + + +

+ 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 +

+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ der Implementierung nach ist es ein Dekorator +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -56762,11 +56941,19 @@ - + + + + + + + + +