diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 19b393ae8..b77aaa781 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -20,6 +20,13 @@ ** easier to understand and maintain. With some helper abbreviations, notably ** a combinator scheme to work from building blocks, a hand-written solution ** can benefit from taking short-cuts, especially related to result bindings. + ** + ** So what is provided here is _not a parser library_ — yet aims at »making + ** simple things simple« and let you implement the complicated ones yourselves. + ** Several decisions were taken accordingly, like only supporting std::string_view + ** and automatically consuming any leading whitespace. And notably the focus was + ** _not placed_ on the challenging aspects of parsing — while still allowing a + ** pathway towards definition of arbitrarily recursive grammars, if so desired. */ @@ -160,6 +167,25 @@ namespace util { } + +// namespace { +// /** handle all regular "function-like" entities */ +// template +// struct FunDetector +// { +// using Sig = typename _Fun::Sig; +// }; +// +// /** handle a generic lambda, accepting a reference to the `SRC` iterator */ +// template +// struct FunDetector> > +// { +// using Arg = std::add_lvalue_reference_t; +// using Ret = decltype(std::declval() (std::declval())); +// using Sig = Ret(Arg); +// }; +// } + /** * Adapt by applying a result-transforming function after a successful parse. * @remark the purpose is to extract a custom data model immediately from the @@ -171,10 +197,12 @@ namespace util { adaptConnex (CON&& connex, BIND&& modelBinding) { using RX = typename CON::Result; - using Arg = lib::meta::_FunArg; - static_assert (std::is_constructible_v, - "Model binding must accept preceding model result."); - using AdaptedRes = typename _Fun::Ret; +// using Arg = lib::meta::_FunArg; +// static_assert (std::is_constructible_v, +// "Model binding must accept preceding model result."); +// using AdaptedRes = typename _Fun::Ret; + using Arg = std::add_lvalue_reference_t; + using AdaptedRes = decltype(modelBinding (std::declval() )); return Connex{[origConnex = forward(connex) ,binding = forward(modelBinding) ] @@ -663,6 +691,11 @@ namespace util { template auto bracketOpt (SPEC&& bodyDef); + template + auto bind (FUN&& modelAdapt); + + auto bindMatch (uint group =0); + private: Eval& eval() { return *this;} }; @@ -966,6 +999,26 @@ namespace util { return seq (accept_bracketOpt (forward(bodyDef))); } + template + template + auto + Syntax::bind (FUN&& modelAdapt) + { + return accept( + adaptConnex (move(parse_) + ,forward(modelAdapt))); + } + + template + auto + Syntax::bindMatch (uint group) + { + return bind ([group](smatch const& mat) + { + return mat.str(group); + }); + } + }// namespace parse diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 799327368..4202ade69 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -76,19 +76,21 @@ namespace test { virtual void run (Arg) { - simpleBlah(); + simpleUsage(); acceptTerminal(); acceptSequential(); acceptAlternatives(); acceptIterWithDelim(); acceptOptionally(); acceptBracketed(); + + verify_modelBinding(); } /** @test TODO just blah. */ void - simpleBlah () + simpleUsage () { } @@ -559,6 +561,32 @@ namespace test { 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 } + + + + /** @test define syntax with bracketed sub-expressions */ + void + verify_modelBinding() + { + auto word{"\\w+"}; + using Mod1 = SeqModel; + auto syntax1 = accept(word).seq(word) + .bind([](Mod1 res) + { +// auto& [a,b] = res; + return res.get<0>().str() +"-"+ res.get<1>().str(); + }); + + string s1{"ham actor"}; + CHECK (not syntax1.hasResult()); + syntax1.parse(s1); + CHECK (syntax1.success()); + auto res1 = syntax1.getResult(); +SHOW_TYPE(decltype(res1)) + CHECK (showType() == "string"); +SHOW_EXPR(res1) + CHECK (res == "ham-actor"_expect); + } }; LAUNCHER (Parse_test, "unit common"); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index af2ac61c5..4d7f91f35 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -56995,8 +56995,7 @@ ....muß dann aber durch die Entscheidungs-Logik sicherstellen, daß dann auch die zugehörige schließende Klammer entweder erwartet, oder übersprungen wird.

- - + @@ -57013,12 +57012,121 @@ - + - + + + + + + + + + + +

+ nebenbei abgefallen +

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

+ das muß tatsächlich ein Postfix-Operator sein +

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

+ Vorschlag: bind(FUN) +

+ + +
+
+ + + + + + +

+ damit klar gesagt wird, wenn die Funktion nicht den bisherigen Result-Typ nimmt +

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

+ Standard-Variante: bindMatch(n) +

+ + +
+ +
+
+ + + + + + + + + + + + + + @@ -57031,6 +57139,331 @@ + + + + + + + + + + +

+ ....weil eine rekursive Definition im Prinzip offen ist und im Extremfall auch tatsächlich nicht terminiert; in Haskell könnte man einen solchen Typ anschreiben, in C++ nicht (weil Typ-Ausdrücke eager aufgelöst werden) +

+ + +
+
+ + + + + + +

+ ...denn was wir abschneiden ist nur die komplett ausformulierte Struktur des Typs; an der Stelle, an der eine andere Klausel rekursiv eingebunden wird, greifen wir nur deren Ergebnis-Model auf. +

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

+ der Lambdas wegen +

+ + +
+
+ + + + + + +

+ das heißt, es würde eine Art late-Binding notwendig +

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

+ und später an diese zuweisen +

+ + +
+ + + + + + +

+ das impliziert Heap-Storage für das Parse-λ +

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

+ ...denn sonst dürfte es kaum möglich sein, einen explizit angebbaren Result-Typ zu konstituieren; in diesem Result-Binding steckt der eigentliche Ansatz, mit dem eine offen-rekursive Grammatik dennoch handhabbar wird, denn es muß eine Art Reduktion der Komplexität erfolgen, beispielsweise indem sofort ein Ergebnis ausgerechnet wird +

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

+ Konsequenz ⟹ das Einbinden in andere Syntax ist speziell zu behandeln +

+ + +
+ + + + + +

+ ....weil ja das Einbinden technisch nichts anderes ist, als eine Dekoration (und damit unterbunden würde) +

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

+ diese andere Syntax hätte dann aber auch einen anderen Typ und müßte in einer anderen Syntax-Variablen gespeichert werden; +

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

+ Variante-1 ist »filosofisch« und praktsich attraktiv +

+ + +
+ + + + + +

+ ...weil sie der „dann mach's halt nicht falsch“-Haltung entspricht, die diesem ganzen Parser-Framework zugrunde gelegt wurde; und ganz praktisch: man bekommt diese Variante geschenkt, alles funktioniert von selber so wie es soll — und wenn irgendjemand unbedingt dekorieren möchte, dann soll er halt +

+ + +
+
+ + + + + + +

+ Weil das, was man nun zusätzlich machen könnte, nur auf Basis der Implementierung verständlich ist, aber für jeden Benutzer ziemlich verwirrend +

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

+ da sie ohnehin das erlaubt was man machen sollte, aber Fehl-Verwendungen unterbindet +

+ + +
+
+ + + + + + +

+ da man ja dennoch irgendwie auf diese Funktion Bezug nehmen kann, indem man sie in andere Sytnax einbaut, ist die Abgrenzung zum »Dekorieren«  nicht klar +

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

+ ...sie bestünde darin, die Referenzen nach der Zuweisung zu materialisieren;  aber die Schwierigkeit besteht darin dieses Linken auszulösen, da die ganze DSL darauf abstellt, Funktionen beliebig ineinander zu verschachteln, und damit sehr viel zu kopieren; man müßte dann entweder eine komplette Link-Infrastruktur hochziehen (Parser-Funktionen wären speziell als noch ungelinkt markiert und es gäbe einen separaten Call-Chain), oder man müßt das Binden/Materialisieren beim ersten Aufruf machen, was in der Praxis nicht sonderlich hilfreich ist +

+ + +
+
+ + + + +
+
+
@@ -57076,13 +57509,21 @@
- + + + + + + + + +