From fb78c10996e12e8d0571446cacb6c695b048d3b8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Jan 2025 00:20:24 +0100 Subject: [PATCH] Library: add generic chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * need to pass the parse end-point in the Eval-Result to allow composed models * this also prepares for support of generic model-binding-λ With the help of the model-joining case definitions it is then possible to handle sequence extension. Deliberately I do not engage into fine grained signature checking, since this would lead to very technical code and moreover this is an implementation feature and we control all invocations (with signatures guaranteed to be correct) --- src/lib/parse.hpp | 34 ++++++++--- tests/library/parse-test.cpp | 20 ++++++- wiki/thinkPad.ichthyo.mm | 108 +++++++++++++++++++++++++++++++---- 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 1ea929043..4b07d8745 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -59,6 +59,7 @@ namespace util { { using Result = RES; optional result; + size_t consumed{0}; }; template @@ -91,7 +92,9 @@ namespace util { return Connex{[regEx = move(rex)] (StrView toParse) -> Eval { - return {matchAtStart (toParse,regEx)}; + auto result{matchAtStart (toParse,regEx)}; + size_t consumed = result? result->length() : 0; + return {move(result), consumed}; }}; } using Term = decltype(buildConnex (std::declval())); @@ -120,9 +123,9 @@ namespace util { auto adaptConnex (CON&& connex, BIND&& modelBinding) { - using R1 = typename CON::Result; + using RX = typename CON::Result; using Arg = lib::meta::_FunArg; - static_assert (std::is_constructible_v, + static_assert (std::is_constructible_v, "Model binding must accept preceding model result."); using AdaptedRes = typename _Fun::Ret; return Connex{[origConnex = forward(connex) @@ -153,7 +156,20 @@ namespace util { using Seq = lib::meta::TySeq; using Tup = std::tuple; - using Tup::Tup; + SeqModel() = default; + + template + SeqModel (SeqModel&& seq, XX&& extraElm) + : Tup{std::tuple_cat (seq.extractTuple() + ,make_tuple (forward (extraElm)) )} + { } + + template + SeqModel (X1&& res1, X2&& res2) + : Tup{move(res1), move(res2)} + { } + + Tup&& extractTuple() { return move(*this); } }; /** @@ -237,14 +253,16 @@ namespace util { auto eval1 = conL.parse (toParse); if (eval1.result) { - uint end1 = eval1.result->length();/////////////////////////OOO pass that via Eval - StrView restInput = toParse.substr(end1); + size_t posAfter1 = eval1.consumed; + StrView restInput = toParse.substr(posAfter1); auto eval2 = conR.parse (restInput); if (eval2.result) { - uint end2 = end1 + eval2.result->length(); + uint consumedOverall = posAfter1 + eval2.consumed; return ProductEval{ProductResult{move(*eval1.result) - ,move(*eval2.result)}}; + ,move(*eval2.result)} + ,consumedOverall + }; } } return ProductEval{std::nullopt}; diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 5bbe609f7..6788f2dc1 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -146,20 +146,23 @@ namespace test { auto eval1 = term1.parse (toParse); if (eval1.result) { - uint end1 = eval1.result->length(); + uint end1 = eval1.consumed; StrView restInput = toParse.substr(end1); auto eval2 = term2.parse (restInput); if (eval2.result) { - uint end2 = end1 + eval2.result->length(); + uint consumedOverall = end1 + eval2.consumed; return ProductEval{ProductResult{move(*eval1.result) - ,move(*eval2.result)}}; + ,move(*eval2.result)} + ,consumedOverall + }; } } return ProductEval{std::nullopt}; }; string s1{"hello millions"}; string s2{"helloworld"}; + string s3{"helloworldtrade"}; auto e1 = parseSeq(s1); CHECK (not e1.result); @@ -182,6 +185,17 @@ namespace test { SeqRes seqModel = syntax.getResult(); CHECK (get<0>(seqModel).str() == "hello"_expect); CHECK (get<1>(seqModel).str() == "world"_expect); + + auto syntax2 = syntax.seq("trade"); + CHECK (not syntax2.hasResult()); + syntax2.parse(s2); + CHECK (not syntax2.success()); + syntax2.parse(s3); + CHECK (syntax2.success()); + auto seqModel2 = syntax2.getResult(); + CHECK (get<0>(seqModel2).str() == "hello"_expect); + CHECK (get<1>(seqModel2).str() == "world"_expect); + CHECK (get<2>(seqModel2).str() == "trade"_expect); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f9102277b..a6a59c0c4 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55511,10 +55511,12 @@ - - - - + + + + + + @@ -55902,22 +55904,106 @@ - - + + - - - + + + + + + + + + + + + +

+ ...weil wir die Typen ja permanent umbauen; +

+
    +
  • +  man müßte hier massiv mit Typsequenzen programmieren, um eine präzise, erwartete Signatur zu konstruieren; +
  • +
  • + alternativ könnte man in die _Join - Rebinder-Templates noch eine explizite cons-Funktion mit aufnehmen (dann hätte man aber das gleiche Problem hier) +
  • +
  • + oder man könnte alle Argumente durch eine lift()-Hilfsfunktion schieben, die dann aber mit den möglichen Models verkoppelt wäre +
  • +
+ + +
+
+ + + + + + +

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

+
    +
  • + Im Fall des SeqModel gibt es noch ein zusätzliches Sicherheitsnetz, da wir von einem explizit getypten Tupel erben. +
  • +
  • + Im Fall des AltModel gibt es kein direktes Sicherheitsnetz, weil wir »blind« in eine Variant-Storage schreiben werden. Man sollte dann noch einen Check auf den Selektor realisieren +
  • +
+ + +
+ +
+ + + + +
- - + +
+ + + + + + + + + + + + + + + + + + + + + + +

+ oder mit \\b +

+ + +
+
+