From f6a2a641df0611739bccfa642cff8e664a59703a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 2 Feb 2025 03:49:13 +0100 Subject: [PATCH] Invocation: tricky pipeline to unfold the repetition-abbreviation This is a nice little goodie: allow to write repeated arguments with the shorthand notation known from lisp and logic programming. For multi-channel media, structurally similar wirings for each channel will be quite common.... --- src/lib/iter-explorer.hpp | 2 +- src/lib/several-builder.hpp | 8 +- src/steam/engine/proc-node.cpp | 80 ++++++-- tests/core/steam/engine/node-meta-test.cpp | 6 + wiki/thinkPad.ichthyo.mm | 204 ++++++++++++++------- 5 files changed, 221 insertions(+), 79 deletions(-) diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index 261c8737d..cfefcb2da 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -1885,7 +1885,7 @@ namespace lib { * - it is bound to play well with the other layers; especially it needs to be aware of `expandChildren()` calls, * which for the consumer side behave like `iterNext()` calls. If a layer needs to do something special for * `iterNext()`, it needs to perform a similar action for `expandChildren()`. - * - it must be behave like a default-constructible, copyable value object + * - it must allow to be handled as default-constructible, copyable value object * @return augmented IterExplorer, incorporating and adapting the injected layer */ template class LAY> diff --git a/src/lib/several-builder.hpp b/src/lib/several-builder.hpp index 947c772e4..deb45add4 100644 --- a/src/lib/several-builder.hpp +++ b/src/lib/several-builder.hpp @@ -783,14 +783,18 @@ namespace lib { void probeMoveCapability() { - if (not (is_same_v or is_trivially_copyable_v)) + using TVal = typename lib::meta::Strip::TypeReferred; + using EVal = typename lib::meta::Strip::TypeReferred; + + if (not (is_same_v or is_trivially_copyable_v)) lock_move = true; } bool canWildMove() { - return is_trivially_copyable_v and not lock_move; + using EVal = typename lib::meta::Strip::TypeReferred; + return is_trivially_copyable_v and not lock_move; } bool diff --git a/src/steam/engine/proc-node.cpp b/src/steam/engine/proc-node.cpp index 76beabbf5..560c31f4a 100644 --- a/src/steam/engine/proc-node.cpp +++ b/src/steam/engine/proc-node.cpp @@ -63,6 +63,7 @@ namespace engine { using util::parse::accept_repeated; using util::parse::expectResult; using lib::meta::NullType; + using std::regex_match; using std::regex; const regex SPEC_CONTENT{R"_([^,\\\(\)\[\]{}<>"]+)_", regex::optimize}; @@ -103,6 +104,70 @@ namespace engine { ) .bindMatch(); + + const regex REPEAT_SPEC {R"_(^(.+)\s*/(\d+)\s*$)_", regex::optimize}; + + /** + * Helper to expand an abbreviated repetition of arguments. + * Implemented as custom-processing layer for IterExplorer, + * by adapting the »State Core« interface + * @remark Repetition is indicated by a trailing "/NUM" + */ + template + struct RepetitionExpander + : lib::IterStateCore + { + using Core = lib::IterStateCore; + + mutable uint repeat_{0}; + mutable std::smatch mat_; + + StrView + yield() const + { + if (not repeat_) + { // check if the next string ends with repetition marker /NUM + if (not regex_match (*Core::srcIter(), mat_, REPEAT_SPEC)) + return *Core::srcIter(); // no repetition -> pass through + + // setup repetition by extracting the repetition count + repeat_ = boost::lexical_cast(mat_.str(2)); + } + return StrView(& *mat_[1].first, mat_[1].length()); + } // several repetitions created from same source + + void + iterNext() + { // hold iteration until all repetitions were delivered + if (repeat_) + --repeat_; + if (not repeat_) + ++ Core::srcIter(); + } + + using Core::Core; + }; + + /** Argument-Spec processing pipeline; + * possibly expands repetition abbreviation, + * collects all argument strings into a lib::Several + */ + lib::Several + evaluateArgSeq (std::vector& parsedArgTerms) + { + auto several = lib::makeSeveral(); + lib::explore (parsedArgTerms) + .processingLayer() + .foreach([&](StrView s){ several.emplace(s); }); + return several.build(); + } + + auto + emptyArgSeq() + { + return lib::Several(); + } + } // (END) Details... @@ -262,18 +327,11 @@ namespace engine { .opt(argListSyntax) .bind([](auto model) -> ProcID::ArgModel { - auto packageAsSeveral = [](std::vector& parsedTerms) - { - return lib::makeSeveral() - .appendAll(parsedTerms); - }; - auto [list1,list2] = model; - auto elms1 = packageAsSeveral(list1); - auto elms2 = list2? packageAsSeveral(*list2) - : lib::makeSeveral(); - - return ProcID::ArgModel{elms1.build(), elms2.build()}; + if (list2) + return {evaluateArgSeq(list1), evaluateArgSeq(*list2)}; + else + return {emptyArgSeq(), evaluateArgSeq(list1)}; }); argSpecSyntax.parse (argLists_); diff --git a/tests/core/steam/engine/node-meta-test.cpp b/tests/core/steam/engine/node-meta-test.cpp index 358ea3d29..11d266695 100644 --- a/tests/core/steam/engine/node-meta-test.cpp +++ b/tests/core/steam/engine/node-meta-test.cpp @@ -88,6 +88,12 @@ SHOW_EXPR(join (arg2.iArg)) SHOW_EXPR(join (arg2.oArg)) SHOW_EXPR(join (arg3.iArg)) SHOW_EXPR(join (arg3.oArg)) + CHECK (join (arg1.iArg) == ""_expect ); + CHECK (join (arg1.oArg) == "arg"_expect ); // only one argument list -> used for output + CHECK (join (arg2.iArg) == ""_expect ); + CHECK (join (arg2.oArg) == "a1, a2"_expect ); + CHECK (join (arg3.iArg) == "in, in, in"_expect ); // repetition-abbreviation of arguments unfolded + CHECK (join (arg3.oArg) == "o1, o2, o2"_expect ); UNIMPLEMENTED ("parse and evaluate"); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index b67944fce..bf4a8cbdd 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -105473,7 +105473,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -105582,35 +105583,80 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

da schreibt man so eine Monster-Syntax einfach hin

- -
+
- - - +

und der Parser funktioniert auf Anhieb

- -
+
+ + + + + + + + + + + + + + + + + + + + +

+ ...wenn man's genau bedenkt: wäre praktisch äquivalent +

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

+ in einer LL-Syntax kann man nicht mit einer optionalen Struktur beginnen, die sich erst im Rückblick auf die ganze Zeile aufklärt +

+ + +
+
+
@@ -105659,55 +105705,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- - - - - -

- ....und jetzt hab ich einen echten LL-Parser, der diese Aufgabe direkt und ohne Implementierungstricks handhaben kann -

- -
-
- - - - + - - - - - - - - - - - -

- ...deshalb habe ich ja auch die Node-Connectivity beibehalten (obwohl zum Rendern nur die Port-Connectivity gebraucht wird). Zusätzlich könnten noch Attribute in der ProcID eine Rolle spielen. Also insgesamt ehr kein separates Parsing -

- -
-
-
- - - - -

- ⟹ also so implementieren wie's am einfachsten geht -

- -
-
-
- - - - @@ -105744,6 +105743,84 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +

+ doch! jetzt bietet sich tatsächlich eine +

+

+ Pipeline zum Aufbereiten der Argumente an +

+ + +
+ + + +

+ und zwar zum Ausfalten der Abkürzungs-Syntax /# +

+

+ +

+

+ Aber trotzdem immer noch nicht der leiseste Hauch von Erinnerung, was ich damals gemeint habe.... +

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

+ ....und jetzt hab ich einen echten LL-Parser, der diese Aufgabe direkt und ohne Implementierungstricks handhaben kann +

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

+ ...deshalb habe ich ja auch die Node-Connectivity beibehalten (obwohl zum Rendern nur die Port-Connectivity gebraucht wird). Zusätzlich könnten noch Attribute in der ProcID eine Rolle spielen. Also insgesamt ehr kein separates Parsing +

+ +
+
+
+ + + + +

+ ⟹ also so implementieren wie's am einfachsten geht +

+ +
+ +
+
@@ -107336,16 +107413,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

weil dies auf einem Array beruht, also im Speicher kompakt liegen muß

- -
+
@@ -107538,8 +107612,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- +