Library: change DSL scheme to handle optional and repeated better

It seemed that using postfix-decorating operators would be a good fit
for the DSL. Exploring this idea further showed however, that such a scheme
is indeed a good fit from the implementation side, but leads to rather confusing
and hard to grasp DSL statements for many non-trivial syntax definition.
The reason is: such a postfix-decorator will by default work on ''everything defined''
up to that point; this is too much in many cases.

The other alternative would be a function-style definition, which has the benefit
to take the sub-clause directly as argument (so the scope is always explicit).
The downside is that argument arrangement is a bit more tricky for the repetition
combinator (there can be mis-matches, since we take the »SPEC« as free-template argument)
And, moreover, with function-style, having more top-level entrance points would
be helpful. Overall, no fundamental roadblock, just more technicalities in the setup
of the DSL functions.

With that re-arrangd structure, an optional combinator could be easily integrated,
and a solution was provided to pick up the parser function from a sub-expression
defined as Syntax object.
This commit is contained in:
Fischlurch 2025-01-23 19:48:30 +01:00
parent 5fed95b929
commit 089fafc1bd
3 changed files with 507 additions and 119 deletions

View file

@ -59,6 +59,10 @@ namespace util {
using StrView = std::string_view;
template<class PAR>
class Syntax;
/**
* Parse evaluation result
@ -72,6 +76,10 @@ namespace util {
size_t consumed{0};
};
/**
* Building block: parser function
* definition and connection element.
*/
template<class FUN>
struct Connex
: util::NonAssign
@ -86,6 +94,8 @@ namespace util {
{ }
};
/** »Null-Connex« which always successfully accepts the empty sequence */
auto
buildConnex(NullType)
{
@ -96,6 +106,15 @@ namespace util {
}
using NulP = decltype(buildConnex (NullType()));
/**
* Foundation: build a \ref Connex to accept a _terminal symbol._
* the actual parsing is delegated to a Regular Expression,
* which must match against the _beginning_ of the input sequence,
* possibly after skipping some whitespace. The defined parser
* returns an \ref Eval context, to hold a _Result Model_ and
* the number of characters matched by this terminal symbol.
*/
auto
buildConnex (regex rex)
{
@ -111,12 +130,14 @@ namespace util {
}
using Term = decltype(buildConnex (std::declval<regex>()));
/** build from a string with Regular-Epression spec */
Term
buildConnex (string const& rexDef)
{
return buildConnex (regex{rexDef});
}
/** copy-builder from an existing parser function */
template<class FUN>
auto
buildConnex (Connex<FUN> const& anchor)
@ -130,7 +151,21 @@ namespace util {
return Connex{move(anchor)};
}
template<class PAR>
auto
buildConnex (Syntax<PAR> const& anchor)
{
using Con = typename Syntax<PAR>::Connex;
return Con{anchor};
}
/**
* Adapt by applying a result-transforming function after a successful parse.
* @remark the purpose is to extract a custom data model immediately from the
* result; binding functors can be applied at any level of a Syntax,
* and thus the parse can be configured to produce custom result data.
*/
template<class CON, class BIND>
auto
adaptConnex (CON&& connex, BIND&& modelBinding)
@ -258,6 +293,7 @@ namespace util {
RES model;
};
/** Standard case : combinator of two model branches */
template<template<class...> class TAG, class R1, class R2 =void>
struct _Join
@ -325,7 +361,7 @@ namespace util {
}
/** accept sequence of two parse functions */
/** accept either one of two alternative parse functions */
template<class C1, class C2>
auto
branchedConnex (C1&& connex1, C2&& connex2)
@ -364,13 +400,14 @@ namespace util {
template<class C1, class C2>
auto
repeatedConnex (uint min, uint max
,C1&& bodyConnex, C2&& delimConnex)
,C1&& delimConnex
,C2&& bodyConnex)
{
using Res = typename decay_t<C1>::Result;
using Res = typename decay_t<C2>::Result;
using IterResult = IterModel<Res>;
using IterEval = Eval<IterResult>;
return Connex{[sep = forward<C2>(delimConnex)
,body = forward<C1>(bodyConnex)
return Connex{[sep = forward<C1>(delimConnex)
,body = forward<C2>(bodyConnex)
,min,max
]
(StrView toParse) -> IterEval
@ -402,11 +439,33 @@ namespace util {
}
template<class PAR>
class Syntax;
/** try to accept parse-function, backtracking if not successful. */
template<class CNX>
auto
optionalConnex (CNX&& connex)
{
using Res = typename decay_t<CNX>::Result;
using OptResult = optional<Res>;
using OptEval = Eval<OptResult>;
return Connex{[body = forward<CNX>(connex)
]
(StrView toParse) -> OptEval
{
auto eval = body.parse (toParse);
size_t consumed{eval.result? eval.consumed : 0};
return OptEval{OptResult{eval.result? move(eval.result) : std::nullopt}
,consumed
};
}};
}
/**
* A Parser function to match and accept some syntax.
* This is a typing- and interface-adapter, wrapping a Connex.
*/
template<class CON>
class Parser
: public CON
@ -418,13 +477,12 @@ namespace util {
using Connex = CON;
using Result = typename CON::Result;
using Sigi = typename _Fun<PFun>::Sig;
//lib::test::TypeDebugger<Sigi> buggi;
//lib::test::TypeDebugger<Result> guggi;
static_assert (has_Sig<PFun, Eval<Result>(StrView)>()
,"Signature of the parse-function not suitable");
/**
* Parse-Function operator: test input and yield Eval record
*/
Eval<Result>
operator() (StrView toParse)
{
@ -435,17 +493,9 @@ using Sigi = typename _Fun<PFun>::Sig;
Parser (SPEC&& spec)
: CON{buildConnex (forward<SPEC> (spec))}
{ }
// template<class PAR>
// Parser (Syntax<PAR> const& anchor)
// : CON{anchor}
// { }
// template<class PAR>
// Parser (CON const& anchor)
// : CON{anchor}
// { }
};
/* === Deduction guide : how to construct a Parser === */
Parser(NullType) -> Parser<NulP>;
Parser(regex &&) -> Parser<Term>;
Parser(regex const&) -> Parser<Term>;
@ -453,11 +503,42 @@ using Sigi = typename _Fun<PFun>::Sig;
template<class FUN>
Parser(Connex<FUN> const&) -> Parser<Connex<FUN>>;
//
// template<class PAR>
// Parser(Syntax<PAR> const&) -> Parser<typename PAR::Connex>;
template<class PAR>
Parser(Syntax<PAR> const&) -> Parser<typename PAR::Connex>;
/** @internal meta-helper : detect if parser can be built from a given type */
template<typename SPEC, typename SEL =void>
struct is_usableSpec : std::false_type{ };
template<typename SPEC>
struct is_usableSpec<SPEC, std::void_t<decltype(Parser{std::declval<SPEC>()})>>
: std::true_type
{ };
template<typename SPEC>
using if_acceptableSpec = lib::meta::enable_if<is_usableSpec<SPEC>>;
template<typename SPEC1, typename SPEC2>
using if_acceptableSpecs = lib::meta::enable_if<is_usableSpec<SPEC1>
,lib::meta::enable_if<is_usableSpec<SPEC2>>>;
/***********************************************************************//**
* A Syntax clause with a parser and result state.
* An instance of this class embodies a (possibly complex)
* _expected syntactical structure;_ the [parse function](\ref parse)
* analyses a given input text for compliance with this expected structure.
* After the parse, result state has been set
* - indicating if the parse was successful
* - possibly with an failure message (TODO 1/25)
* - the number of characters covered by this match
* - a _Result Model,_ as a structured term holding
* result components from each part / sub-clause
*/
template<class PAR>
class Syntax
: public Eval<typename PAR::Result>
@ -468,12 +549,6 @@ using Sigi = typename _Fun<PFun>::Sig;
using Connex = typename PAR::Connex;
using Result = typename PAR::Result;
bool success() const { return bool(Syntax::result); }
bool hasResult() const { return bool(Syntax::result); }
size_t consumed() const { return Eval<Result>::consumed;}
Result& getResult() { return * Syntax::result; }
Result&& extractResult(){ return move(getResult()); }
Syntax()
: parse_{NullType()}
{ }
@ -484,11 +559,21 @@ using Sigi = typename _Fun<PFun>::Sig;
{ }
explicit
operator bool() const
{
return success();
}
operator bool() const { return success();}
operator Connex&() { return parse_; }
operator Connex const&() const { return parse_; }
bool success() const { return bool(Syntax::result); }
bool hasResult() const { return bool(Syntax::result); }
size_t consumed() const { return Eval<Result>::consumed;}
Result& getResult() { return * Syntax::result; }
Result&& extractResult() { return move(getResult()); }
/********************************************//**
* Core API : parse against this syntax clause
*/
Syntax&&
parse (StrView toParse)
{
@ -496,74 +581,41 @@ using Sigi = typename _Fun<PFun>::Sig;
return move(*this);
}
Connex const&
getConny() const
{
return parse_;
}
/** ===== Syntax clause builder DSL ===== */
template<typename SPEC>
auto
seq (SPEC&& clauseDef)
{
return accept(
sequenceConnex (move(parse_)
,Parser{forward<SPEC> (clauseDef)}));
}
auto seq (SPEC&& clauseDef);
template<typename SPEC>
auto
alt (SPEC&& clauseDef)
{
return accept(
branchedConnex (move(parse_)
,Parser{forward<SPEC> (clauseDef)}));
}
auto
repeat(uint cnt =uint(-1))
{
return repeat (1,cnt, NullType{});
}
auto alt (SPEC&& clauseDef);
template<typename SPEC>
auto
repeat (SPEC&& delimDef)
{
return repeat (1,uint(-1), forward<SPEC> (delimDef));
}
auto opt (SPEC&& clauseDef);
template<typename SPEC1, typename SPEC2>
auto repeat (uint min, uint max, SPEC1&& delimDef, SPEC2&& clauseDef);
template<typename SPEC1, typename SPEC2>
auto repeat (uint cnt, SPEC1&& delimDef, SPEC2&& clauseDef);
template<typename SPEC1, typename SPEC2>
auto repeat (SPEC1&& delimDef, SPEC2&& clauseDef);
template<typename SPEC>
auto
repeat (uint cnt, SPEC&& delimDef)
{
return repeat (cnt,cnt, forward<SPEC> (delimDef));
}
template<typename SPEC>
auto
repeat (uint min, uint max, SPEC&& delimDef)
{
if (max<min)
throw err::Invalid{_Fmt{"Invalid repeated syntax-spec: min:%d > max:%d"}
% min % max };
if (max == 0)
throw err::Invalid{"Invalid repeat with max ≡ 0 repetitions"};
return accept(
repeatedConnex (min,max
,move(parse_)
,Parser{forward<SPEC> (delimDef)}));
}
auto repeat (SPEC&& clauseDef);
private:
Eval<Result>& eval() { return *this;}
};
/** ===== Syntax clause builder DSL ===== */
/** build a Syntax clause from anything usable as parser-spec. */
template<typename SPEC>
auto
accept (SPEC&& clauseDef)
@ -571,16 +623,183 @@ using Sigi = typename _Fun<PFun>::Sig;
return Syntax{Parser{forward<SPEC> (clauseDef)}};
}
/** empty syntax clause to start further definition */
/** empty Syntax clause to start further definition */
auto accept() { return Syntax<Parser<NulP>>{}; }
/** start Syntax clause with an optional syntax part */
template<typename SPEC>
auto
accept_opt (SPEC&& clauseDef)
{
return accept(
optionalConnex (Parser{forward<SPEC> (clauseDef)}));
}
/**
* Start Syntax clause with a repeated sub-clause,
* with separator and repetition limit; repetitions [min..max]
* The separator will be expected _between_ instances of the repeated sub-clause
* and will by itself produce no model. The result model is an instance of \ref IterModel,
* which implies it is a vector (uses heap storage); if min 0, the model can be empty.
*/
template<typename SPEC1, typename SPEC2>
auto
accept_repeated (uint min, uint max, SPEC1&& delimDef, SPEC2&& clauseDef)
{
if (max<min)
throw err::Invalid{_Fmt{"Invalid repeated syntax-spec: min:%d > max:%d"}
% min % max };
if (max == 0)
throw err::Invalid{"Invalid repeat with max ≡ 0 repetitions"};
return accept(
repeatedConnex (min,max
,Parser{forward<SPEC1> (delimDef)}
,Parser{forward<SPEC2> (clauseDef)}));
}
/** \param cnt exact number of repetitions expected */
template<typename SPEC1, typename SPEC2, typename =if_acceptableSpecs<SPEC1,SPEC2>>
auto
accept_repeated (uint cnt, SPEC1&& delimDef, SPEC2&& clauseDef)
{
return accept_repeated (cnt,cnt, forward<SPEC1>(delimDef), forward<SPEC2>(clauseDef));
}
/** start Syntax with an arbitrarily repeated sub-clause, with separator */
template<typename SPEC1, typename SPEC2, typename =if_acceptableSpecs<SPEC1,SPEC2>>
auto
accept_repeated (SPEC1&& delimDef, SPEC2&& clauseDef)
{
return accept_repeated (1,uint(-1), forward<SPEC1>(delimDef), forward<SPEC2>(clauseDef));
}
template<typename SPEC>
auto
accept_repeated (uint min, uint max, SPEC&& clauseDef)
{
return accept_repeated (min, max, NullType{}, forward<SPEC>(clauseDef));
}
template<typename SPEC>
auto
accept_repeated (uint cnt, SPEC&& clauseDef)
{
return accept_repeated (cnt, NullType{}, forward<SPEC>(clauseDef));
}
template<typename SPEC>
auto
accept_repeated (SPEC&& clauseDef)
{
return accept_repeated (NullType{}, forward<SPEC>(clauseDef));
}
/**
* Combinator: extend this Syntax clause by expecting a further sub-clause
* behind the part of the input matched by the already defined part of this Syntax.
* The result model will be a \SeqModel, which essentially is a tuple of the
* result models of all sequenced parts.
* @return Syntax clause instance accepting the extended structure.
* @warning the old syntax is invalidated by moving the parse-function out.
*/
template<class PAR>
template<typename SPEC>
auto
Syntax<PAR>::seq (SPEC&& clauseDef)
{
return accept(
sequenceConnex (move(parse_)
,Parser{forward<SPEC> (clauseDef)}));
}
/**
* Combinator: extend this Syntax by adding an _alternative branch_.
* So either the already defined part of this Syntax matches the input,
* or the alternative clause is probed from the start of the input. At least
* one branch must match for the parse to be successful; however, further
* branches are not tested after finding a matching branch (short-circuit).
* The result model is a _Sum Type,_ implemented as a custom variant record
* of type \ref SubModel. It provides a branch selector field to detect which
* branch of the syntax did match. And it allows to retrieve the result model
* of this successful branch which however requires that the invoking code
* precisely knows the model type to expect.
*/
template<class PAR>
template<typename SPEC>
auto
Syntax<PAR>::alt (SPEC&& clauseDef)
{
return accept(
branchedConnex (move(parse_)
,Parser{forward<SPEC> (clauseDef)}));
}
/**
* Combinator: extend this Syntax with a further sequenced sub-clause,
* which however is _only optional_ and the match succeed without it.
* The result model is (as always for \ref seq ) a tuple; the result
* from the optional part is packaged into a std::optional.
*/
template<class PAR>
template<typename SPEC>
auto
Syntax<PAR>::opt (SPEC&& clauseDef)
{
return seq (accept_opt (forward<SPEC> (clauseDef)));
}
/**
* Combinator: extend this Syntax with a further sequenced sub-clause,
* which in this case accepts a repeated sequence, with delimiter.
* @see accept_sequenced()
*/
template<class PAR>
template<typename SPEC1, typename SPEC2>
auto
Syntax<PAR>::repeat (uint min, uint max, SPEC1&& delimDef, SPEC2&& clauseDef)
{
return seq (accept_repeated (min,max
,forward<SPEC1>(clauseDef)
,forward<SPEC2>(clauseDef)));
}
template<class PAR>
template<typename SPEC1, typename SPEC2>
auto
Syntax<PAR>::repeat (uint cnt, SPEC1&& delimDef, SPEC2&& clauseDef)
{
return seq (accept_repeated (cnt
,forward<SPEC1>(clauseDef)
,forward<SPEC2>(clauseDef)));
}
template<class PAR>
template<typename SPEC1, typename SPEC2>
auto
Syntax<PAR>::repeat (SPEC1&& delimDef, SPEC2&& clauseDef)
{
return seq (accept_repeated (forward<SPEC1>(clauseDef)
,forward<SPEC2>(clauseDef)));
}
template<class PAR>
template<typename SPEC>
auto
Syntax<PAR>::repeat (SPEC&& clauseDef)
{
return seq (accept_repeated (forward<SPEC>(clauseDef)));
}
}// namespace parse
using parse::accept;
using parse::accept_opt;
using parse::accept_repeated;
}// namespace util
namespace lib {
}// namespace lib
#endif/*LIB_PARSE_H*/

View file

@ -35,7 +35,9 @@ namespace parse{
namespace test {
using lib::test::showType;
using lib::meta::typeSymbol;
using lib::meta::is_Tuple;
using std::decay_t;
using std::get;
// using util::join;
// using util::isnil;
@ -79,6 +81,8 @@ namespace test {
acceptSequential();
acceptAlternatives();
acceptIterWithDelim();
acceptOptionally();
acceptBracketed();
}
@ -117,16 +121,14 @@ namespace test {
CHECK (not syntax2.hasResult());
syntax2.parse (toParse);
CHECK (not syntax2.success());
string bye{"cruel world"};
syntax2.parse (bye);
CHECK (syntax2.success());
CHECK (syntax2.getResult()[1] == "cruel"_expect);
// going full circle: extract parser def from syntax
// using Conn = decltype(syntax2)::Connex;
// Conn conny{syntax2};
// auto parse2 = Parser{conny};
auto parse2 = Parser{syntax2.getConny()};
// Going full circle: extract Parser definition from syntax
auto parse2 = Parser{syntax2};
CHECK (eval.result->str(1) == "vile");
eval = parse2 (toParse);
CHECK (not eval.result);
@ -343,8 +345,10 @@ namespace test {
/** @test TODO define repetitive sequence with delimiter
/** @test define repetitive sequence with delimiter
* - demonstrate how actually to accept such a flexible sequence
* - cover integration into the syntax clause DSL
* - repetition count and delimiter
*/
void
acceptIterWithDelim()
@ -405,7 +409,7 @@ namespace test {
//______________________________________________
// DSL parse clause builder: iterative sequence...
auto syntax1 = accept(term).repeat(",");
auto syntax1 = accept_repeated(",", term);
// Perform the same parse as demonstrated above....
CHECK (not syntax1.hasResult());
@ -423,8 +427,8 @@ namespace test {
CHECK (res1[1].str() == "extort" );
CHECK (res1[2].str() == "profit" );
auto syntax2 = accept(term).repeat(1,2,",");
auto syntax3 = accept(term).repeat(4,",");
auto syntax2 = accept_repeated(1,2,",", term);
auto syntax3 = accept_repeated( 4,",", term);
syntax2.parse(s2);
syntax3.parse(s2);
CHECK ( syntax2);
@ -441,7 +445,7 @@ namespace test {
CHECK (syntax3.getResult()[2].str() == "profit" );
CHECK (syntax3.getResult()[3].str() == "dump" );
auto syntax4 = accept(term).repeat();
auto syntax4 = accept_repeated(term);
syntax4.parse(s1);
CHECK (syntax4.success());
CHECK (syntax4.getResult().size() == 2);
@ -449,6 +453,94 @@ namespace test {
CHECK (syntax4.getResult()[1].str() == "umschlungen" );
CHECK (s1.substr(syntax4.consumed()) == ", Millionen");
}
/** @test define compound syntax with optional sub-clause
* - use the DSL to construct a complex syntax
* - by default, several parts are implicitly sequenced
* - here we combine repeated parts with an optional clause
* - which in turn is again a compound syntax clause
* - the produced model reflects the structure of this syntax
* - result model of the optional clause is wrapped into `std::optional`
* - terminal elements produce a `std::smatch` (RegExp matcher object)
*/
void
acceptOptionally()
{
auto syntax = accept_repeated(",", "\\w+") // first we look for comma separated words
.opt(accept("and") // then (implicitly sequenced) an optional clause
.repeat("\\w+")); // ... comprising "and" followed by several words
using Model = decay_t<decltype(syntax.getResult())>;
string s1{"fearmongering, scapegoating, intimidation"};
string s2{"charisma and divine blessing"};
CHECK (not syntax.hasResult());
syntax.parse(s1);
CHECK (syntax.success());
Model res1 = syntax.getResult();
CHECK (typeSymbol(res1) == "SeqModel");
CHECK (typeSymbol(res1.get<0>()) == "IterModel");
CHECK (typeSymbol(res1.get<1>()) == "optional");
CHECK (res1.N == 2); // 2-component tuple at top
CHECK (res1.get<0>().size() == 3); // sequence in 1st component matched 3 elements
CHECK (res1.get<0>()[0].str() == "fearmongering"); // elements in the sequence...
CHECK (res1.get<0>()[1].str() == "scapegoating");
CHECK (res1.get<0>()[2].str() == "intimidation");
CHECK (res1.get<1>() == std::nullopt); // the optional clause did not match
syntax.parse(s2);
CHECK (syntax.success());
Model res2 = syntax.getResult();
CHECK (typeSymbol(res2) == "SeqModel"); // Syntax SeqModel
CHECK (typeSymbol(res2.get<0>()) == "IterModel"); // repeat(word) opt IterModel optional
CHECK (typeSymbol(res2.get<1>()) == "optional"); // | |
CHECK (typeSymbol(*res2.get<1>()) == "SeqModel"); // Syntax SeqModel
CHECK (typeSymbol(res2.get<1>()->get<0>()) == "match_results"); // "and" repeat(word) Terminal IterModel
CHECK (typeSymbol(res2.get<1>()->get<1>()) == "IterModel"); //
CHECK (res2.get<0>().size() == 1);
CHECK (res2.get<0>()[0].str() == "charisma");
CHECK (res2.get<1>() != std::nullopt);
CHECK (res2.get<1>()->N == 2);
CHECK (res2.get<1>()->get<0>().str() == "and");
CHECK (res2.get<1>()->get<1>().size() == 2 );
CHECK (res2.get<1>()->get<1>()[0].str() == "divine" );
CHECK (res2.get<1>()->get<1>()[1].str() == "blessing" );
string s3{s1+" , "+s2};
syntax.parse(s3);
CHECK (syntax.success());
Model res3 = syntax.getResult();
CHECK (typeSymbol(res3) == "SeqModel");
CHECK (res3.get<0>().size() == 4);
CHECK (res3.get<0>()[0].str() == "fearmongering");
CHECK (res3.get<0>()[1].str() == "scapegoating");
CHECK (res3.get<0>()[2].str() == "intimidation");
CHECK (res3.get<0>()[3].str() == "charisma");
CHECK (res3.get<1>() != std::nullopt);
CHECK (res3.get<1>()->N == 2);
CHECK (res3.get<1>()->get<0>().str() == "and");
CHECK (res3.get<1>()->get<1>().size() == 2);
CHECK (res3.get<1>()->get<1>()[0].str() == "divine");
CHECK (res3.get<1>()->get<1>()[1].str() == "blessing");
}
/** @test
*
*/
void
acceptBracketed()
{
UNIMPLEMENTED ("bracketed");
}
};
LAUNCHER (Parse_test, "unit common");

View file

@ -55198,10 +55198,9 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#dcd8bb" CREATED="1737571008910" ID="ID_1051885182" MODIFIED="1737571674702" TEXT="umgekehrte Postfix-Notation">
<linktarget COLOR="#fef8c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="483;279;" ID="Arrow_ID_1143426887" SOURCE="ID_1134276650" STARTARROW="None" STARTINCLINATION="460;21;"/>
<linktarget COLOR="#fef8c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="483;279;" ID="Arrow_ID_1290165292" SOURCE="ID_543391021" STARTARROW="None" STARTINCLINATION="554;61;"/>
<icon BUILTIN="ksmiletris"/>
<node BACKGROUND_COLOR="#c8b99a" COLOR="#690f14" CREATED="1737571008910" HGAP="54" ID="ID_1051885182" MODIFIED="1737603574855" TEXT="umgekehrte Postfix-Notation" VSHIFT="-2">
<linktarget COLOR="#fed3c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="751;246;" ID="Arrow_ID_1143426887" SOURCE="ID_1134276650" STARTARROW="None" STARTINCLINATION="515;24;"/>
<icon BUILTIN="button_cancel"/>
<node CREATED="1737571129595" ID="ID_1227830528" MODIFIED="1737571145596" TEXT="Posfix-Operator nimmt stets die ganze Syntax-Klausel"/>
<node CREATED="1737571146527" ID="ID_1480801160" MODIFIED="1737571157199" TEXT="man definiert also von innen nach au&#xdf;en"/>
<node CREATED="1737571212401" ID="ID_1700130675" MODIFIED="1737571228018" TEXT="wichtige Modifier">
@ -55209,6 +55208,37 @@
<node CREATED="1737571233558" ID="ID_1190994671" MODIFIED="1737571238865" TEXT="optional()"/>
<node CREATED="1737571257818" ID="ID_779919773" MODIFIED="1737571292552" TEXT="bracket(spec)"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1737602996904" ID="ID_231296474" MODIFIED="1737603535989" TEXT="bew&#xe4;hrt sich nicht in der Praxis">
<arrowlink COLOR="#8c4b4d" DESTINATION="ID_1220798311" ENDARROW="Default" ENDINCLINATION="14;-34;" ID="Arrow_ID_300013837" STARTARROW="None" STARTINCLINATION="-167;-19;"/>
<icon BUILTIN="closed"/>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1737603396873" HGAP="19" ID="ID_1220798311" MODIFIED="1737603570376" TEXT="stattdessen..." VSHIFT="11">
<linktarget COLOR="#8c4b4d" DESTINATION="ID_1220798311" ENDARROW="Default" ENDINCLINATION="14;-34;" ID="Arrow_ID_300013837" SOURCE="ID_231296474" STARTARROW="None" STARTINCLINATION="-167;-19;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="forward"/>
<node BACKGROUND_COLOR="#ecdcb4" COLOR="#491354" CREATED="1737603423470" ID="ID_1829725832" LINK="#ID_172468776" MODIFIED="1737643392381" STYLE="fork">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
top-Level-Einstiege bieten &#10233; Pr&#228;fix&#160;<font face="Monospaced" color="#681d21">accept_</font>
</p>
</body>
</html>
</richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
</node>
<node BACKGROUND_COLOR="#ecdcb4" COLOR="#491354" CREATED="1737603413919" ID="ID_1979527720" MODIFIED="1737603564463" STYLE="fork" TEXT="implizite Sequenz als default-Junktor">
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
</node>
<node CREATED="1737603449227" ID="ID_1769998683" MODIFIED="1737603460317" TEXT="f&#xfc;r">
<node CREATED="1736719956201" ID="ID_1631746484" MODIFIED="1737603479603" TEXT="repeat(N, delim, body)"/>
<node CREATED="1737571233558" ID="ID_463545162" MODIFIED="1737646748974" TEXT="opt(syntax)"/>
<node CREATED="1737571257818" ID="ID_1977661543" MODIFIED="1737603488865" TEXT="bracket(spec, syntax)"/>
</node>
</node>
</node>
<node CREATED="1736724867345" ID="ID_1842306489" MODIFIED="1736724871350" TEXT="Model">
@ -55430,7 +55460,8 @@
</node>
</node>
</node>
<node CREATED="1736896942694" ID="ID_50956958" MODIFIED="1736896957680" TEXT="Kreis schlie&#xdf;en: Parser wieder aus Syntax bauen">
<node CREATED="1736896942694" ID="ID_50956958" MODIFIED="1737649641200" TEXT="Kreis schlie&#xdf;en: Parser wieder aus Syntax bauen">
<linktarget COLOR="#de5868" DESTINATION="ID_50956958" ENDARROW="Default" ENDINCLINATION="-792;1631;" ID="Arrow_ID_1971370194" SOURCE="ID_950065344" STARTARROW="None" STARTINCLINATION="-1469;98;"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1736896960412" ID="ID_281482378" MODIFIED="1736896967137" TEXT="will nicht recht funktionieren">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1736896977066" ID="ID_773020678" MODIFIED="1736897010857" TEXT="das l&#xe4;uft auf ein clone-construct des Connex hinaus">
@ -55451,6 +55482,16 @@
<node CREATED="1736897078444" ID="ID_1084979494" MODIFIED="1736897089814" TEXT="h&#xe4;ngt wohl mit der Overload-Resolution zusammen"/>
<node CREATED="1736897090523" ID="ID_142574777" MODIFIED="1736897102445" TEXT="wenn man direkt ein Objekt konstruiert, wird er verwendet"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#1b6b58" CREATED="1737656539272" ID="ID_613317824" MODIFIED="1737656638137" TEXT="mu&#xdf; den ganzen Pfad lenken">
<icon BUILTIN="forward"/>
<node CREATED="1737656557199" ID="ID_1766627630" MODIFIED="1737656570026" TEXT="Deduction-Guide f&#xfc;r Parser{Syntax}">
<node CREATED="1737656579333" ID="ID_1683176379" MODIFIED="1737656595775" TEXT="hier Typedef Syntax::Connex zu Hilfe nehmen">
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1737656598432" ID="ID_1946066798" MODIFIED="1737656609012" TEXT="brauche buildConnex (Syntax)"/>
<node CREATED="1737656610177" ID="ID_1186453687" MODIFIED="1737656620354" TEXT="Syntax mu&#xdf; conversion-Operator bieten"/>
</node>
</node>
<node CREATED="1736897170512" ID="ID_28739070" MODIFIED="1736897191019" TEXT="Fazit: mu&#xdf; sinnvollerweise direkt aus Syntax heraus angesto&#xdf;en werden">
<node CREATED="1736897207538" ID="ID_953670876" MODIFIED="1736897215717" TEXT="das ist wohl akzeptabel"/>
@ -55471,7 +55512,8 @@
</node>
</node>
</node>
<node CREATED="1736897354542" ID="ID_1609713458" MODIFIED="1736897365189" TEXT="Sequenz-Kombinator anlegen">
<node COLOR="#338800" CREATED="1736897354542" ID="ID_1609713458" MODIFIED="1737649511943" TEXT="Sequenz-Kombinator anlegen">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1736897787716" ID="ID_663293705" MODIFIED="1737234458887" TEXT="Basis-Mechanik">
<icon BUILTIN="button_ok"/>
<node CREATED="1736897797339" ID="ID_1097974753" MODIFIED="1736897811257" TEXT="brauche Zugang zu den Parse-Funktoren"/>
@ -56853,9 +56895,8 @@
<node CREATED="1737560320472" ID="ID_89163254" MODIFIED="1737560334666" TEXT="viel lesbarer, da Syntax immer durch das Stichwort &quot;accept&quot; eingeleitet wird"/>
<node CREATED="1737560341821" ID="ID_713190827" MODIFIED="1737560350519" TEXT="spart eine Menge repetitive definitionen"/>
</node>
<node CREATED="1737569770813" ID="ID_858035606" MODIFIED="1737569832356" TEXT="noch besser: postfix repeat()">
<icon BUILTIN="yes"/>
<icon BUILTIN="yes"/>
<node COLOR="#5b280f" CREATED="1737569770813" ID="ID_858035606" MODIFIED="1737602936297" TEXT="noch besser: postfix repeat()">
<icon BUILTIN="button_cancel"/>
<node CREATED="1737569852750" ID="ID_1574259355" MODIFIED="1737571535674" TEXT="Begr&#xfc;ndung: sonst bekommen wir stets eine implizite Sequenz mit Tupel auf Model-Ebene">
<richcontent TYPE="NOTE"><html>
<head>
@ -56880,9 +56921,39 @@
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#fef8c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="483;279;" ID="Arrow_ID_1143426887" STARTARROW="None" STARTINCLINATION="460;21;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="forward"/>
<arrowlink COLOR="#fed3c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="751;246;" ID="Arrow_ID_1143426887" STARTARROW="None" STARTINCLINATION="515;24;"/>
<linktarget COLOR="#b60c19" DESTINATION="ID_1134276650" ENDARROW="Default" ENDINCLINATION="205;10;" ID="Arrow_ID_497998726" SOURCE="ID_543391021" STARTARROW="None" STARTINCLINATION="79;-13;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="13"/>
<icon BUILTIN="stop-sign"/>
</node>
<node COLOR="#a3020d" CREATED="1737603165552" ID="ID_477286805" MODIFIED="1737603199554" TEXT="diese erzwingt in der Praxis unn&#xf6;tig viele Klammerungen">
<font NAME="SansSerif" SIZE="11"/>
</node>
</node>
<node BACKGROUND_COLOR="#ecdcb4" COLOR="#491354" CREATED="1737603246542" ID="ID_1239316504" MODIFIED="1737603354469" TEXT="implizite Sequenz als Default ist doch gar nicht so schlecht">
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1737603322428" ID="ID_172468776" MODIFIED="1737603344117" TEXT="also doch top-Level">
<icon BUILTIN="yes"/>
<node CREATED="1737643237418" ID="ID_1852967082" MODIFIED="1737643310172">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
stets mit Pr&#228;fix <font face="Monospaced" size="3" color="#542c2c">accept_</font>
</p>
</body>
</html>
</richcontent>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1737649521902" ID="ID_950065344" MODIFIED="1737649641200" TEXT="mu&#xdf; Parser aus Syntax erzeugen k&#xf6;nnen">
<arrowlink COLOR="#de5868" DESTINATION="ID_50956958" ENDARROW="Default" ENDINCLINATION="-792;1631;" ID="Arrow_ID_1971370194" STARTARROW="None" STARTINCLINATION="-1469;98;"/>
<icon BUILTIN="flag-pink"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1737603327947" ID="ID_366774797" MODIFIED="1737603344113" TEXT="und lokal als implizite Sequenz">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
@ -56895,7 +56966,8 @@
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1737515386507" ID="ID_1671829221" MODIFIED="1737515396597" TEXT="optionaler Kombinator">
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1737515386507" ID="ID_1671829221" MODIFIED="1737591861198" TEXT="optionaler Kombinator">
<icon BUILTIN="pencil"/>
<node CREATED="1737515398692" ID="ID_1478704437" MODIFIED="1737515413091" TEXT="der ist einfach zu realisieren und sehr n&#xfc;tzlich"/>
<node CREATED="1737515413799" ID="ID_1235063232" MODIFIED="1737515424929" TEXT="das Model wird in einen std::optional gewickelt"/>
<node BACKGROUND_COLOR="#e5d4c6" COLOR="#435e98" CREATED="1737515765304" ID="ID_692646776" MODIFIED="1737571714725" TEXT="per postfix-Operator darstellbar?">
@ -56914,16 +56986,20 @@
<node CREATED="1737552779748" ID="ID_1979253709" MODIFIED="1737552797867" TEXT="das Model wird in einen Optional gepackt"/>
<node CREATED="1737552798647" ID="ID_845579574" MODIFIED="1737552821888" TEXT="Scheitern des Parsers f&#xfc;hrt nur zu Backtracking"/>
</node>
<node CREATED="1737552824569" ID="ID_1896891774" MODIFIED="1737552864018" TEXT="zur Lesbarkeit beide DSL-Varianten bieten">
<node CREATED="1737552866525" ID="ID_849718453" MODIFIED="1737552892406" TEXT="Postfix geht immer wirkt aber auf die ganze Klausel">
<node CREATED="1737570726608" ID="ID_543391021" MODIFIED="1737571674702" TEXT="Postfix-takes-all-Logik">
<arrowlink COLOR="#fef8c9" DESTINATION="ID_1051885182" ENDARROW="Default" ENDINCLINATION="483;279;" ID="Arrow_ID_1290165292" STARTARROW="None" STARTINCLINATION="554;61;"/>
<node CREATED="1737552824569" ID="ID_1896891774" MODIFIED="1737603097508" TEXT="Lesbarkeit der DSL-Varianten....">
<node CREATED="1737552866525" ID="ID_849718453" MODIFIED="1737603117259" TEXT="Postfix geht immer &#x2014; wirkt aber auf die ganze Klausel">
<node CREATED="1737570726608" ID="ID_543391021" MODIFIED="1737603076848" TEXT="Postfix-takes-all-Logik">
<arrowlink COLOR="#b60c19" DESTINATION="ID_1134276650" ENDARROW="Default" ENDINCLINATION="205;10;" ID="Arrow_ID_497998726" STARTARROW="None" STARTINCLINATION="79;-13;"/>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1737603121280" ID="ID_1882624112" MODIFIED="1737603160042" TEXT="Scope meist viel zu gro&#xdf;">
<icon BUILTIN="broken-line"/>
</node>
</node>
<node CREATED="1737552896306" ID="ID_978425545" MODIFIED="1737552964929" TEXT="optional(parse) - Funktionsschreibweise bisweilsen klarer">
<node CREATED="1737552896306" ID="ID_978425545" MODIFIED="1737603209963" TEXT="optional(parse) - Funktionsschreibweise klarer">
<node CREATED="1737571480380" ID="ID_330911197" MODIFIED="1737571485431" TEXT="mu&#xdf; dann aber top-level sein"/>
<node CREATED="1737571488227" ID="ID_479972357" MODIFIED="1737571542441" TEXT="denn sonst widerspricht es der Syntax-Klausel-Logik">
<node CREATED="1737571488227" ID="ID_479972357" MODIFIED="1737603236739" TEXT="oder eine Sequenz implizieren">
<arrowlink COLOR="#9091a5" DESTINATION="ID_1574259355" ENDARROW="Default" ENDINCLINATION="185;6;" ID="Arrow_ID_1603942836" STARTARROW="None" STARTINCLINATION="128;-10;"/>
</node>
</node>
@ -56990,8 +57066,9 @@
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1737557515000" ID="ID_717799418" MODIFIED="1737557531293" TEXT="Parse-Mechanismus skizzieren"/>
<node COLOR="#435e98" CREATED="1737557534805" ID="ID_1997532643" MODIFIED="1737557551147" TEXT="Akzeptieren und Backtracking"/>
<node COLOR="#435e98" CREATED="1737509774878" ID="ID_1818283954" MODIFIED="1737557632090" TEXT="Kombinator per DSL">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1737509774878" ID="ID_1818283954" MODIFIED="1737603375589" TEXT="Kombinator per DSL">
<linktarget COLOR="#69a19e" DESTINATION="ID_1818283954" ENDARROW="Default" ENDINCLINATION="341;-27;" ID="Arrow_ID_1906636741" SOURCE="ID_909696958" STARTARROW="None" STARTINCLINATION="-269;35;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node COLOR="#435e98" CREATED="1737581111703" ID="ID_1277454723" MODIFIED="1737581129805" TEXT="mit vorgegebener Anzahl"/>
<node COLOR="#435e98" CREATED="1737581124685" ID="ID_196113271" MODIFIED="1737581129806" TEXT="ohne Delimiter"/>