diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 294a71a25..8634a2917 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -320,6 +320,41 @@ namespace util { } + /** accept sequence of two parse functions */ + template + auto + branchedConnex (C1&& connex1, C2&& connex2) + { + using R1 = typename decay_t::Result; + using R2 = typename decay_t::Result; + using SumResult = typename _Join::Result; + using SumEval = Eval; + return Connex{[conL = forward(connex1) + ,conR = forward(connex2) + ] + (StrView toParse) -> SumEval + { + auto eval1 = conL.parse (toParse); + if (eval1.result) + { + uint endBranch1 = eval1.consumed; + return SumEval{SumResult::mark_left (move(*eval1.result)) + ,endBranch1 + }; + } + auto eval2 = conR.parse (toParse); + if (eval2.result) + { + uint endBranch2 = eval2.consumed; + return SumEval{SumResult::mark_right (move(*eval2.result)) + ,endBranch2 + }; + } + return SumEval{std::nullopt}; + }}; + } + + template class Syntax; @@ -427,6 +462,15 @@ using Sigi = typename _Fun::Sig; ,Parser{forward (clauseDef)})); } + template + auto + alt (SPEC&& clauseDef) + { + return accept( + branchedConnex (move(parse_) + ,Parser{forward (clauseDef)})); + } + private: Eval& eval() diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 856c0c4f2..643ef933d 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -135,10 +135,17 @@ namespace test { } - /** @test define a sequence of syntax structures to match by parse. */ + /** @test define a sequence of syntax structures to match by parse. + * - first demonstrate explicitly how the consecutive parsing works + * and how both models are combined into a product model (tuple) + * - demonstrate how leading whitespace is skipped automatically + * - then perform the same parse with a Syntax clause build with + * the `seq()` builder-DSL + * - extend this Syntax by adding a further sequential clause. + */ void acceptSequential() - { + { //_______________________________________________ // Demonstration: how sequence combinator works.... auto term1 = buildConnex ("hello"); auto term2 = buildConnex ("world"); @@ -185,6 +192,7 @@ namespace test { CHECK (not term2.parse(" old ").result); + //___________________________________________________ // DSL parse clause builder: a sequence of terminals... auto syntax = accept("hello").seq("world"); @@ -213,9 +221,12 @@ namespace test { } + /** @test TODO WIP 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 + * and construct the corresponding sum-model (variant) */ void acceptAlternatives() @@ -293,7 +304,42 @@ namespace test { CHECK (e2.result->selected() == 0); // Selector-ID of the first matching branch (here #0) CHECK (e2.result->get<0>().str() == "brazen"); // We know that branch#0 holds a RegExp-Matcher (from term1) CHECK (e2.result->get<0>().suffix() == " dicktator"); - } + CHECK (e2.consumed == 6); + CHECK (s2.substr(e2.consumed) == " dicktator"); + + + //________________________________________________ + // DSL parse clause builder: alternative branches... + auto syntax = accept("brazen").alt("bragging"); + + // Perform the same parse as demonstrated above.... + CHECK (not syntax.hasResult()); + syntax.parse(s1); + CHECK (not syntax.success()); + syntax.parse(s2); + CHECK (syntax); + auto altModel = syntax.getResult(); + CHECK (altModel.selected() == 0); + CHECK (altModel.get<0>().str() == "brazen"); + + // can build extended clause from existing one + auto syntax2 = syntax.alt("smarmy (\\w+)"); + CHECK (not syntax2.hasResult()); + syntax2.parse(s1); + 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().selected() == 0); // ... string s2 still matched the same branch (#0) + CHECK (syntax2.getResult().get<0>().str() == "brazen"); + + syntax2.parse("smarmy saviour"); + CHECK (syntax2.success()); + auto altModel2 = syntax2.getResult(); + CHECK (syntax2.getResult().selected() == 2); // ... but another string can match the added branch #2 + 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 }; LAUNCHER (Parse_test, "unit common"); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 09883ed1d..f7da36608 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55532,8 +55532,9 @@ - - + + + @@ -55579,7 +55580,8 @@ - + + @@ -55590,7 +55592,7 @@ - + @@ -55745,14 +55747,15 @@ - - + + - + + @@ -55772,7 +55775,7 @@ - + @@ -55780,9 +55783,14 @@ + + - + + + + @@ -55820,12 +55828,14 @@ + + - - + + @@ -55882,9 +55892,42 @@ - + + + + + + + + + +

+ Das ist eine mehrstufige Kette +

+
    +
  • + aus der Aufrufstruktur ergibt sich die ID des Branches +
  • +
  • + gleichzeitig ergibt sich damit die Position des Typ-Arguments +
  • +
  • + dieses greifen wir uns per Typ-Sequenz-Manipulation +
  • +
  • + und verwenden es im generierten Konstruktor +
  • +
+

+ ⟹ Resultat: man kann in den jeweiligen «Slot» nur mit einem kompatiblen Typ rein, und Typsicherheit ist gewährleistet (im Parser; wenn ein Client falsch zugreift, ist er selber schuld) +

+ + +
+ +
@@ -55930,8 +55973,20 @@
- - + + + + + + + + + + + + + + @@ -55958,7 +56013,7 @@ - + @@ -56056,7 +56111,7 @@ - + @@ -56416,7 +56471,7 @@ - + @@ -56468,17 +56523,18 @@ - - + + - - - + + + + @@ -56612,9 +56668,14 @@ + + + - + + + @@ -56642,7 +56703,7 @@ - + @@ -56663,8 +56724,14 @@ - - + + + + + + + + @@ -56691,8 +56758,14 @@ - - + + + + + + + +