Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
/*
|
|
|
|
|
|
PARSE.hpp - helpers for parsing textual specifications
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2024, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
|
|
|
|
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file parse.hpp
|
|
|
|
|
|
** Convenience wrappers and definitions for parsing structured definitions.
|
|
|
|
|
|
** Whenever a specification syntax entails nested structures, extracting contents
|
|
|
|
|
|
** with regular expressions alone becomes tricky. Without much sophistication, a
|
|
|
|
|
|
** directly implemented simple recursive descent parser is often less brittle and
|
|
|
|
|
|
** 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.
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
**
|
|
|
|
|
|
** 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.
|
2025-01-29 23:51:13 +01:00
|
|
|
|
**
|
|
|
|
|
|
** ## Functionality
|
|
|
|
|
|
** This framework supports the construction of a recursive-descent parser without
|
|
|
|
|
|
** underlying lexer. Rather, _terminal expressions_ are delegated to a regular
|
|
|
|
|
|
** expression matcher, which allows for some additional leeway, like a negative
|
|
|
|
|
|
** match, or using lookahead-assertions. The implementation relies on parser
|
|
|
|
|
|
** _functions_ and the _combinator technique_ to combine building blocks — yet
|
|
|
|
|
|
** those functions and combinators are wrapped into a syntax-clause builder DSL.
|
|
|
|
|
|
** Each parse yields a result, depending on the structure of the parse-function.
|
|
|
|
|
|
** The base building block, a parser to accept a regular expression, will yield
|
|
|
|
|
|
** the C++ matcher object as result — and thus essentially some pointers into
|
|
|
|
|
|
** the original sequence, which has to be passed in as C++ string_view.
|
|
|
|
|
|
**
|
|
|
|
|
|
** An essential concept of this parsing support framework is that each parser
|
|
|
|
|
|
** can be decorated by a _model transformation functor,_ which gets the result
|
|
|
|
|
|
** of the wrapped parser and can return _any arbitrary value object._ In essence,
|
|
|
|
|
|
** this framework does not provide the notion of an _abstract syntax tree_ — yet
|
|
|
|
|
|
** the user is free to build a custom syntax tree, relying on these model-bindings.
|
|
|
|
|
|
**
|
|
|
|
|
|
** \par Syntax building blocks
|
|
|
|
|
|
** - `accept(SPEC)` builds a clause to accept anything the given SPEC for
|
|
|
|
|
|
** a parser function would accept and to yield its return model.
|
|
|
|
|
|
** The `SPEC` argument might be...
|
|
|
|
|
|
** + a string describing regular expression syntax
|
|
|
|
|
|
** + a C++ regexp object
|
|
|
|
|
|
** + any hand-written functor `string_view ⟼ model`
|
|
|
|
|
|
** + an existing syntax clause (either by RValue or LValue)
|
|
|
|
|
|
** - `accept_opt` builds a clause to optionally accept in accordance
|
|
|
|
|
|
** to the given definition; if no match is found, parsing backtracks.
|
|
|
|
|
|
** - `accept_repeated` builds a clause to accept a repetition of the
|
|
|
|
|
|
** structure accepted by its argument, optionally with an explicit delimiter
|
|
|
|
|
|
** and possibly with a limited number of instances. The result values are
|
|
|
|
|
|
** obviously all from the same type and will be collected into a IterModel,
|
|
|
|
|
|
** which essentially is a std::vector<RES> (note: heap storage!).
|
|
|
|
|
|
** - `accept_bracket` builds a clause to accept the structure of the
|
|
|
|
|
|
** given argument, but enclosed in parentheses, or an explicitly defined
|
|
|
|
|
|
** pair of delimiters. Variants are provided to accept optional bracketing.
|
|
|
|
|
|
** - `<syntax>.seq(SPEC)` extends a given syntax clause to accept the structure
|
|
|
|
|
|
** described by the SPEC _after_ the structure already described by the syntax.
|
|
|
|
|
|
** Both parts must succeed for the parse to be successful. The result value
|
|
|
|
|
|
** is packaged into a parse::SeqModel, which essentially is a tuple; when
|
|
|
|
|
|
** attaching several .seq() specifications, it can become a N-ary tuple.
|
|
|
|
|
|
** - `<syntax>.alt(SPEC)` adds an _alternative branch_ to the existing syntax.
|
|
|
|
|
|
** Either part alone is sufficient for a successful parse. First the existing
|
|
|
|
|
|
** branch(es) are tried, and only if those do not succeed, backtracking is
|
|
|
|
|
|
** performed and then the alternative branch is tried. Once some match is
|
|
|
|
|
|
** found, further branches will _not be attempted._ (short-circuit).
|
|
|
|
|
|
** Thus there is _always one_ result model, is placed into an AltModel,
|
|
|
|
|
|
** which is a _variant data type_ with a common inline result buffer.
|
|
|
|
|
|
** The _selector field_ must be checked to find out which branch of the
|
|
|
|
|
|
** syntax succeeded, and then the result must be handled with its appropriate
|
|
|
|
|
|
** type, because the various branches can possibly yield entirely different
|
|
|
|
|
|
** result value types.
|
|
|
|
|
|
** - `<syntax>.repeat()` _sequentially adds_ a repeated clause be accepted
|
|
|
|
|
|
** _after_ what the existing syntax accepts. The result is thus a SeqModel.
|
|
|
|
|
|
** - `<syntax>.bracket()` _sequentially adds_ a bracketing clause to be
|
|
|
|
|
|
** accepted _after_ parsing with the existing syntax. Again, the result
|
|
|
|
|
|
** is a SeqModel, with the result-model from the repetition in the last
|
|
|
|
|
|
** tuple element. The repetition itself yields an IterModel.
|
|
|
|
|
|
** - `<syntax>.bind(FUN)` is a postfix-operator and decorates the existing
|
|
|
|
|
|
** syntax with a result-binding functor `FUN`: The syntax's result value
|
|
|
|
|
|
** is passed into this functor and whatever this functor returns will
|
|
|
|
|
|
** become the result value of the compound.
|
|
|
|
|
|
** - `<syntax>.bindMatch(n)`: a convenience shortcut to bind to the complete
|
|
|
|
|
|
** input substring accepted by the underlying parser, or (when directly
|
|
|
|
|
|
** applied to a reg-exp terminal) it can also extract a match-sub-group.
|
|
|
|
|
|
** The result value of the resulting syntax clause will thus be a string.
|
|
|
|
|
|
**
|
|
|
|
|
|
** ### Recursion
|
|
|
|
|
|
** A _recursive syntax definition_ is what unleashes the parsing technique's
|
|
|
|
|
|
** full strength; but recursive grammars can be challenging to master at times
|
|
|
|
|
|
** and may in fact lead to deadlock due to unlimited recursion. Since this
|
|
|
|
|
|
** framework is focused on ease of use in simple situations, recursion is
|
|
|
|
|
|
** considered an advanced usage and thus supported in a way that requires
|
|
|
|
|
|
** some preparation and help by the user. In essence...
|
|
|
|
|
|
** - a syntax clause to be referred recursively _must be pre-declared_
|
|
|
|
|
|
** - this pre-declaration gives it a known, fixed result type and will
|
|
|
|
|
|
** internally use a `std::function` as parse function, initially empty.
|
|
|
|
|
|
** - later the full syntax must be defined, and supplemented with a binding
|
|
|
|
|
|
** to yield precisely the result-model type as pre-declared
|
|
|
|
|
|
** - finally this definition is _assigned_ to the pre-declared syntax object.
|
|
|
|
|
|
** One point to note is that internally a _reference_ to the pre-declared
|
|
|
|
|
|
** `std::function` is stored — implying that the pre-declared syntax object
|
|
|
|
|
|
** must **remain in scope and can not be moved**. Other than requiring
|
|
|
|
|
|
** such a special setup, recursive syntax can be used without limits.
|
|
|
|
|
|
**
|
|
|
|
|
|
** ## Structure
|
|
|
|
|
|
** The util::parse::Syntax object is what the user handles and interacts with.
|
|
|
|
|
|
** It both holds a result record of type util::parse::Eval, and a parser object
|
|
|
|
|
|
** of type util::parse::Parser, while also acting as a move-style DSL builder.
|
|
|
|
|
|
** Here, _move-style_ implies that invoking a DSL extension operator will
|
|
|
|
|
|
** _move the embedded parser function_ into a new Syntax object with different
|
|
|
|
|
|
** result type. However, when starting with some of the top-level function-style
|
|
|
|
|
|
** builders, a given syntax object will be copied. This is important to keep
|
|
|
|
|
|
** in mind when building complex expressions. Another point worth mentioning
|
|
|
|
|
|
** is that the basic parse relies on std::string_view, which implies that the
|
|
|
|
|
|
** original input sequence must remain valid until the parse result is built.
|
|
|
|
|
|
**
|
|
|
|
|
|
** At the heart of the combinator mechanics is the class util::part::Connex,
|
|
|
|
|
|
** which is essentially a parser function with additional configuration, and
|
|
|
|
|
|
** can be passed to one of the _combinator functions_ `buildXXXConnex(...)`.
|
|
|
|
|
|
** The construction of compound model results relies on a set of overloaded
|
|
|
|
|
|
** `_Join` templates, which specify the way how models can be combined and
|
|
|
|
|
|
** sequences can be extended.
|
|
|
|
|
|
**@see parse-test.cpp
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_PARSE_H
|
|
|
|
|
|
#define LIB_PARSE_H
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-22 22:31:25 +01:00
|
|
|
|
#include "lib/error.hpp"
|
2025-01-20 23:55:42 +01:00
|
|
|
|
#include "lib/branch-case.hpp"
|
2025-01-22 22:31:25 +01:00
|
|
|
|
#include "lib/format-string.hpp"
|
2025-01-01 22:02:08 +01:00
|
|
|
|
#include "lib/meta/variadic-rebind.hpp"
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
#include "lib/meta/function.hpp"
|
|
|
|
|
|
#include "lib/meta/trait.hpp"
|
|
|
|
|
|
#include "lib/regex.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
|
|
#include <utility>
|
2025-01-19 23:11:25 +01:00
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <array>
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
|
|
|
|
|
namespace util {
|
|
|
|
|
|
namespace parse {
|
2025-01-22 22:31:25 +01:00
|
|
|
|
namespace err = lumiera::error;
|
2025-01-20 20:22:16 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
using std::move;
|
|
|
|
|
|
using std::forward;
|
|
|
|
|
|
using std::optional;
|
|
|
|
|
|
using lib::meta::_Fun;
|
|
|
|
|
|
using lib::meta::has_Sig;
|
2025-01-01 22:02:08 +01:00
|
|
|
|
using lib::meta::_Vari;
|
2025-06-02 17:46:40 +02:00
|
|
|
|
using lib::meta::Nil;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
using std::decay_t;
|
|
|
|
|
|
using std::tuple;
|
2025-01-19 23:11:25 +01:00
|
|
|
|
using std::array;
|
2025-01-22 22:31:25 +01:00
|
|
|
|
using util::_Fmt;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
|
|
|
|
|
using StrView = std::string_view;
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
template<class PAR>
|
|
|
|
|
|
class Syntax;
|
|
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
template<class CON>
|
|
|
|
|
|
class Parser;
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-19 23:11:25 +01:00
|
|
|
|
/**
|
2025-01-22 22:31:25 +01:00
|
|
|
|
* Parse evaluation result
|
|
|
|
|
|
* @tparam RES model type to bind
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
*/
|
|
|
|
|
|
template<class RES>
|
|
|
|
|
|
struct Eval
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = RES;
|
|
|
|
|
|
optional<RES> result;
|
2025-01-18 00:20:24 +01:00
|
|
|
|
size_t consumed{0};
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Building block: parser function
|
|
|
|
|
|
* definition and connection element.
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class FUN>
|
|
|
|
|
|
struct Connex
|
|
|
|
|
|
{
|
|
|
|
|
|
using PFun = FUN;
|
|
|
|
|
|
PFun parse;
|
|
|
|
|
|
|
|
|
|
|
|
using Result = typename _Fun<PFun>::Ret::Result;
|
|
|
|
|
|
|
Library: lay out foundation for recursive clauses
In accordance to the plan drafted yesterday, I will try to integrate
this essential capability into the framework established thus far by a trick,
requiring only minimal adjustment, but some work by the user.
Since the parse function is defined as a (unqualified) template argument,
it is possible to emplace either a `std::function`, or a reference thereto.
For this to work, the user is required to pre-define the expected result type,
and, furthermore, must later on assign a fully specified clause, which
also has a model transformation binding attached to yield this predeclared
result type
2025-01-26 15:55:01 +01:00
|
|
|
|
Connex (FUN pFun)
|
2025-01-26 23:54:38 +01:00
|
|
|
|
: parse{pFun}
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
/** special setup to be pre-declared and then used recursively */
|
|
|
|
|
|
template<class RES>
|
|
|
|
|
|
using OpaqueConnex = Connex<std::function<Eval<RES>(StrView)>>;
|
|
|
|
|
|
|
|
|
|
|
|
template<class RES>
|
|
|
|
|
|
using ForwardConnex = Connex<std::function<Eval<RES>(StrView)>&>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
/** »Null-Connex« which always successfully accepts the empty sequence */
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-06-02 17:46:40 +02:00
|
|
|
|
buildConnex(Nil)
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{
|
2025-06-02 17:46:40 +02:00
|
|
|
|
return Connex{[](StrView) -> Eval<Nil>
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{
|
2025-06-02 17:46:40 +02:00
|
|
|
|
return {Nil()};
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
}};
|
|
|
|
|
|
}
|
2025-06-02 17:46:40 +02:00
|
|
|
|
using NulP = decltype(buildConnex (Nil()));
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-04-26 23:59:29 +02:00
|
|
|
|
* Foundation: build a \ref Connex to accept a _terminal symbol_.
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
* 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.
|
|
|
|
|
|
*/
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
buildConnex (regex rex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Connex{[regEx = move(rex)]
|
|
|
|
|
|
(StrView toParse) -> Eval<smatch>
|
2025-01-18 22:18:44 +01:00
|
|
|
|
{ // skip leading whitespace...
|
|
|
|
|
|
size_t pre = leadingWhitespace (toParse);
|
|
|
|
|
|
toParse = toParse.substr(pre);
|
2025-01-18 00:20:24 +01:00
|
|
|
|
auto result{matchAtStart (toParse,regEx)};
|
2025-01-18 22:18:44 +01:00
|
|
|
|
size_t consumed = result? pre+result->length() : 0;
|
2025-01-18 00:20:24 +01:00
|
|
|
|
return {move(result), consumed};
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
using Term = decltype(buildConnex (std::declval<regex>()));
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** build from a string with Regular-Epression spec */
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline Term
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
buildConnex (string const& rexDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return buildConnex (regex{rexDef});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** copy-builder from an existing parser function */
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class FUN>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
buildConnex (Connex<FUN> const& anchor)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Connex{anchor};
|
|
|
|
|
|
}
|
|
|
|
|
|
template<class FUN>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
buildConnex (Connex<FUN> && anchor)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Connex{move(anchor)};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
template<class PAR>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
buildConnex (Syntax<PAR> const& anchor)
|
|
|
|
|
|
{
|
|
|
|
|
|
using Con = typename Syntax<PAR>::Connex;
|
|
|
|
|
|
return Con{anchor};
|
|
|
|
|
|
}
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
/** special setup to attach to a pre-defined clause for recursive syntax
|
|
|
|
|
|
* @note works in concert with the Parser deduction guide, so that the
|
|
|
|
|
|
* resulting ForwardConnex holds a _reference_ to std::function,
|
|
|
|
|
|
* and thus gets to see the full definition reassigned later. */
|
|
|
|
|
|
template<class RES>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-26 23:54:38 +01:00
|
|
|
|
buildConnex (Syntax<Parser<OpaqueConnex<RES>>> & refClause)
|
|
|
|
|
|
{
|
|
|
|
|
|
OpaqueConnex<RES>& refConnex = refClause;
|
|
|
|
|
|
return ForwardConnex<RES>{refConnex.parse};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
|
2025-01-25 03:40:41 +01:00
|
|
|
|
namespace {
|
|
|
|
|
|
/** helper to detect return type of a possibly generic λ */
|
|
|
|
|
|
template<typename ARG, typename FUN, typename SEL =void>
|
|
|
|
|
|
struct _ProbeFunReturn
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert (!sizeof(ARG),
|
|
|
|
|
|
"Model binding must accept preceding model result.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<typename ARG, typename FUN>
|
|
|
|
|
|
struct _ProbeFunReturn<ARG,FUN, std::void_t<decltype(std::declval<FUN>() (std::declval<ARG>()))>>
|
|
|
|
|
|
{ // probe the λ with ARG to force template instantiation
|
|
|
|
|
|
using Ret = decltype(std::declval<FUN>() (std::declval<ARG>()));
|
|
|
|
|
|
};
|
2025-01-26 23:54:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class FUN>
|
|
|
|
|
|
inline bool
|
|
|
|
|
|
_boundFun(FUN const& fun)
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (std::is_constructible<bool, FUN const&>())
|
|
|
|
|
|
return bool(fun);
|
|
|
|
|
|
else
|
|
|
|
|
|
return std::is_invocable<FUN, StrView>();
|
|
|
|
|
|
}
|
2025-01-25 03:40:41 +01:00
|
|
|
|
}
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class CON, class BIND>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
adaptConnex (CON&& connex, BIND&& modelBinding)
|
|
|
|
|
|
{
|
2025-01-18 00:20:24 +01:00
|
|
|
|
using RX = typename CON::Result;
|
2025-01-25 03:40:41 +01:00
|
|
|
|
using Arg = std::add_rvalue_reference_t<RX>;
|
|
|
|
|
|
using AdaptedRes = typename _ProbeFunReturn<Arg,BIND>::Ret;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
return Connex{[origConnex = forward<CON>(connex)
|
|
|
|
|
|
,binding = forward<BIND>(modelBinding)
|
|
|
|
|
|
]
|
|
|
|
|
|
(StrView toParse) -> Eval<AdaptedRes>
|
|
|
|
|
|
{
|
|
|
|
|
|
auto eval = origConnex.parse (toParse);
|
|
|
|
|
|
if (eval.result)
|
2025-01-25 17:00:51 +01:00
|
|
|
|
return {binding (move (*eval.result))
|
|
|
|
|
|
,eval.consumed};
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
else
|
|
|
|
|
|
return {std::nullopt};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-25 17:00:51 +01:00
|
|
|
|
template<class CON>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-25 17:00:51 +01:00
|
|
|
|
toStringConnex (CON&& connex, uint part)
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = typename CON::Result;
|
|
|
|
|
|
return Connex([baseConnex = forward<CON>(connex)
|
|
|
|
|
|
,part
|
|
|
|
|
|
]
|
|
|
|
|
|
(StrView toParse) -> Eval<string>
|
|
|
|
|
|
{
|
2025-01-26 01:24:10 +01:00
|
|
|
|
auto eval = baseConnex.parse (toParse);
|
|
|
|
|
|
if (eval.result)
|
|
|
|
|
|
if constexpr (lib::meta::is_basically<Result,smatch>())
|
2025-01-25 17:00:51 +01:00
|
|
|
|
return {eval.result->str(part)
|
|
|
|
|
|
,eval.consumed
|
|
|
|
|
|
};
|
2025-01-26 01:24:10 +01:00
|
|
|
|
else
|
|
|
|
|
|
{ // defensive fall-back: ignore model, return accepted input part
|
|
|
|
|
|
size_t pre = leadingWhitespace (toParse);
|
2025-01-29 23:51:13 +01:00
|
|
|
|
return {string{toParse.substr (pre, eval.consumed - pre)}
|
2025-01-26 01:24:10 +01:00
|
|
|
|
,eval.consumed
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-01-25 17:00:51 +01:00
|
|
|
|
else
|
2025-01-26 01:24:10 +01:00
|
|
|
|
return {std::nullopt};
|
2025-01-25 17:00:51 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-25 03:40:41 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
/* ===== building structured models ===== */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Product Model : results from a conjunction of parsing clauses,
|
|
|
|
|
|
* which are to be accepted in sequence, one after the other.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename...RESULTS>
|
|
|
|
|
|
struct SeqModel
|
|
|
|
|
|
: tuple<RESULTS...>
|
|
|
|
|
|
{
|
2025-01-22 16:42:28 +01:00
|
|
|
|
static constexpr size_t N = sizeof...(RESULTS);
|
2025-06-07 18:04:59 +02:00
|
|
|
|
using Seq = lib::meta::Types<RESULTS...>;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
using Tup = std::tuple<RESULTS...>;
|
|
|
|
|
|
|
2025-01-18 00:20:24 +01:00
|
|
|
|
SeqModel() = default;
|
|
|
|
|
|
|
|
|
|
|
|
template<typename...XS, typename XX>
|
|
|
|
|
|
SeqModel (SeqModel<XS...>&& seq, XX&& extraElm)
|
|
|
|
|
|
: Tup{std::tuple_cat (seq.extractTuple()
|
2025-01-28 20:23:28 +01:00
|
|
|
|
,std::make_tuple (forward<XX> (extraElm)) )}
|
2025-01-18 00:20:24 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X1, typename X2>
|
|
|
|
|
|
SeqModel (X1&& res1, X2&& res2)
|
|
|
|
|
|
: Tup{move(res1), move(res2)}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
Tup&& extractTuple() { return move(*this); }
|
2025-01-22 16:42:28 +01:00
|
|
|
|
|
|
|
|
|
|
template<size_t i>
|
|
|
|
|
|
auto get() { return std::get<i> (*this); }
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Sum Model : results from a disjunction of parsing clauses,
|
|
|
|
|
|
* which are are tested and accepted as alternatives, one at least.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename...CASES>
|
|
|
|
|
|
struct AltModel
|
2025-01-21 03:53:29 +01:00
|
|
|
|
: lib::BranchCase<CASES...>
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{
|
2025-01-01 22:02:08 +01:00
|
|
|
|
using Alt = lib::BranchCase<CASES...>;
|
2025-01-26 01:24:10 +01:00
|
|
|
|
static constexpr size_t N = sizeof...(CASES);
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
template<typename EXTRA>
|
|
|
|
|
|
using Additionally = AltModel<CASES...,EXTRA>;
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
template<typename EXTRA>
|
|
|
|
|
|
Additionally<EXTRA>
|
|
|
|
|
|
addBranch() ///< mark-up existing model to add a further branch-case
|
|
|
|
|
|
{
|
|
|
|
|
|
Additionally<EXTRA>& upFaked = reinterpret_cast<Additionally<EXTRA>&> (*this);
|
|
|
|
|
|
return {move (upFaked)};
|
|
|
|
|
|
} // this trick works due to similar storage layout
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
/* === Builder functions to mark which side of the combinator to pick === */
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
using SubSeq = typename _Vari<AltModel, CASES...>::Prefix; ///< a nested sub-model to extend
|
|
|
|
|
|
using Penult = typename _Vari<AltModel, CASES...>::Penult; ///< plain value expected for left-branch
|
|
|
|
|
|
using Ultima = typename _Vari<AltModel, CASES...>::Ultima; ///< plain value expected for right-branch
|
2025-01-21 03:53:29 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
static AltModel
|
|
|
|
|
|
mark_left (SubSeq&& leftCases)
|
2025-01-20 23:55:42 +01:00
|
|
|
|
{
|
2025-01-22 01:11:05 +01:00
|
|
|
|
return {leftCases.template addBranch<Ultima>()};
|
2025-01-20 23:55:42 +01:00
|
|
|
|
}
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
static AltModel
|
|
|
|
|
|
mark_left (Penult&& leftCase)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {Alt::TOP-1, move(leftCase)};
|
|
|
|
|
|
}
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
static AltModel
|
|
|
|
|
|
mark_right (Ultima&& rightCase)
|
|
|
|
|
|
{
|
|
|
|
|
|
return {Alt::TOP, move(rightCase)};
|
|
|
|
|
|
}
|
2025-01-01 22:02:08 +01:00
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
private:
|
|
|
|
|
|
template<typename INIT>
|
|
|
|
|
|
AltModel (size_t branchID, INIT&& init)
|
|
|
|
|
|
: Alt{branchID, forward<INIT> (init)}
|
2025-01-01 22:02:08 +01:00
|
|
|
|
{ }
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Special case Product Model to represent iterative sequence */
|
|
|
|
|
|
template<typename RES>
|
|
|
|
|
|
struct IterModel
|
2025-01-22 22:31:25 +01:00
|
|
|
|
: std::vector<RES>
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{
|
2025-01-22 22:31:25 +01:00
|
|
|
|
RES& get (size_t i) { return this->at(i); }
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Marker-Tag for the result from a sub-expression, not to be joined */
|
|
|
|
|
|
template<typename RES>
|
|
|
|
|
|
struct SubModel
|
2025-01-29 23:51:13 +01:00
|
|
|
|
{ /* for metaprogramming only */ };
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
/** Standard case : combinator of two model branches */
|
|
|
|
|
|
template<template<class...> class TAG, class R1, class R2 =void>
|
|
|
|
|
|
struct _Join
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = TAG<R1,R2>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-01-22 01:11:05 +01:00
|
|
|
|
/** Generic case : extend a structured model by further branch */
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<template<class...> class TAG, class...RS, class R2>
|
|
|
|
|
|
struct _Join<TAG,TAG<RS...>,R2>
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = TAG<RS...,R2>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Special Case : absorb sub-expression without flattening */
|
|
|
|
|
|
template<template<class...> class TAG, class R1, class R2>
|
|
|
|
|
|
struct _Join<TAG,SubModel<R1>,R2>
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = TAG<R1,R2>;
|
|
|
|
|
|
};
|
|
|
|
|
|
template<template<class...> class TAG, class R1, class R2>
|
|
|
|
|
|
struct _Join<TAG,R1, SubModel<R2>>
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = TAG<R1,R2>;
|
|
|
|
|
|
};
|
|
|
|
|
|
template<template<class...> class TAG, class R1, class R2>
|
|
|
|
|
|
struct _Join<TAG,SubModel<R1>,SubModel<R2>>
|
|
|
|
|
|
{
|
|
|
|
|
|
using Result = TAG<R1,R2>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-18 22:18:44 +01:00
|
|
|
|
/** accept sequence of two parse functions */
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class C1, class C2>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
sequenceConnex (C1&& connex1, C2&& connex2)
|
|
|
|
|
|
{
|
|
|
|
|
|
using R1 = typename decay_t<C1>::Result;
|
|
|
|
|
|
using R2 = typename decay_t<C2>::Result;
|
|
|
|
|
|
using ProductResult = typename _Join<SeqModel, R1, R2>::Result;
|
|
|
|
|
|
using ProductEval = Eval<ProductResult>;
|
|
|
|
|
|
return Connex{[conL = forward<C1>(connex1)
|
|
|
|
|
|
,conR = forward<C2>(connex2)
|
|
|
|
|
|
]
|
|
|
|
|
|
(StrView toParse) -> ProductEval
|
|
|
|
|
|
{
|
|
|
|
|
|
auto eval1 = conL.parse (toParse);
|
|
|
|
|
|
if (eval1.result)
|
|
|
|
|
|
{
|
2025-01-18 00:20:24 +01:00
|
|
|
|
size_t posAfter1 = eval1.consumed;
|
|
|
|
|
|
StrView restInput = toParse.substr(posAfter1);
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
auto eval2 = conR.parse (restInput);
|
|
|
|
|
|
if (eval2.result)
|
|
|
|
|
|
{
|
2025-01-18 00:20:24 +01:00
|
|
|
|
uint consumedOverall = posAfter1 + eval2.consumed;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
return ProductEval{ProductResult{move(*eval1.result)
|
2025-01-18 00:20:24 +01:00
|
|
|
|
,move(*eval2.result)}
|
|
|
|
|
|
,consumedOverall
|
|
|
|
|
|
};
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return ProductEval{std::nullopt};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** accept either one of two alternative parse functions */
|
2025-01-22 02:21:39 +01:00
|
|
|
|
template<class C1, class C2>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-22 02:21:39 +01:00
|
|
|
|
branchedConnex (C1&& connex1, C2&& connex2)
|
|
|
|
|
|
{
|
|
|
|
|
|
using R1 = typename decay_t<C1>::Result;
|
|
|
|
|
|
using R2 = typename decay_t<C2>::Result;
|
|
|
|
|
|
using SumResult = typename _Join<AltModel, R1, R2>::Result;
|
|
|
|
|
|
using SumEval = Eval<SumResult>;
|
|
|
|
|
|
return Connex{[conL = forward<C1>(connex1)
|
|
|
|
|
|
,conR = forward<C2>(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};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-22 22:31:25 +01:00
|
|
|
|
/** repeatedly accept parse-function, optionally delimited. */
|
|
|
|
|
|
template<class C1, class C2>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-22 22:31:25 +01:00
|
|
|
|
repeatedConnex (uint min, uint max
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
,C1&& delimConnex
|
|
|
|
|
|
,C2&& bodyConnex)
|
2025-01-22 22:31:25 +01:00
|
|
|
|
{
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
using Res = typename decay_t<C2>::Result;
|
2025-01-22 22:31:25 +01:00
|
|
|
|
using IterResult = IterModel<Res>;
|
|
|
|
|
|
using IterEval = Eval<IterResult>;
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
return Connex{[sep = forward<C1>(delimConnex)
|
|
|
|
|
|
,body = forward<C2>(bodyConnex)
|
2025-01-22 22:31:25 +01:00
|
|
|
|
,min,max
|
|
|
|
|
|
]
|
|
|
|
|
|
(StrView toParse) -> IterEval
|
|
|
|
|
|
{
|
2025-01-24 01:41:55 +01:00
|
|
|
|
size_t consumed{0};
|
2025-01-22 22:31:25 +01:00
|
|
|
|
IterResult results;
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
uint offset{0};
|
|
|
|
|
|
if (not results.empty())
|
|
|
|
|
|
{ // look for delimiter within sequence
|
|
|
|
|
|
auto delim = sep.parse (toParse);
|
|
|
|
|
|
if (not delim.result)
|
|
|
|
|
|
break;
|
|
|
|
|
|
offset += delim.consumed;
|
|
|
|
|
|
}
|
|
|
|
|
|
auto eval = body.parse (toParse.substr(offset));
|
|
|
|
|
|
if (not eval.result)
|
|
|
|
|
|
break;
|
|
|
|
|
|
offset += eval.consumed;
|
|
|
|
|
|
results.emplace_back (move(*eval.result));
|
|
|
|
|
|
toParse = toParse.substr(offset);
|
|
|
|
|
|
consumed += offset;
|
|
|
|
|
|
}
|
|
|
|
|
|
while (results.size() < max);
|
|
|
|
|
|
return results.size() >= min? IterEval{move(results), consumed}
|
|
|
|
|
|
: IterEval{std::nullopt};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** try to accept parse-function, backtracking if not successful. */
|
|
|
|
|
|
template<class CNX>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-24 01:41:55 +01:00
|
|
|
|
/** accept some structure enclosed into a bracketing construct.
|
|
|
|
|
|
* \param isOptional if the bracketing can be omitted */
|
|
|
|
|
|
template<class C1, class C2, class C3>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
bracketedConnex (C1&& openingConnex
|
|
|
|
|
|
,C2&& closingConnex
|
|
|
|
|
|
,C3&& bodyConnex
|
|
|
|
|
|
,bool isOptional)
|
|
|
|
|
|
{
|
|
|
|
|
|
using Res = typename decay_t<C3>::Result;
|
|
|
|
|
|
return Connex{[opening = forward<C1>(openingConnex)
|
|
|
|
|
|
,closing = forward<C2>(closingConnex)
|
|
|
|
|
|
,body = forward<C3>(bodyConnex)
|
|
|
|
|
|
,isOptional
|
|
|
|
|
|
]
|
|
|
|
|
|
(StrView toParse) -> Eval<Res>
|
|
|
|
|
|
{
|
|
|
|
|
|
auto bracket = opening.parse (toParse);
|
|
|
|
|
|
if (bracket.result or isOptional)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t consumed = bracket.consumed;
|
|
|
|
|
|
bool expectClose{bracket.result};
|
|
|
|
|
|
auto eval = body.parse (toParse.substr(consumed));
|
|
|
|
|
|
if (eval.result)
|
|
|
|
|
|
{
|
|
|
|
|
|
consumed += eval.consumed;
|
|
|
|
|
|
if (expectClose)
|
|
|
|
|
|
bracket = closing.parse (toParse.substr(consumed));
|
|
|
|
|
|
if (bracket.result or not expectClose)
|
|
|
|
|
|
{
|
|
|
|
|
|
consumed += bracket.consumed;
|
|
|
|
|
|
return Eval<Res>{move (*eval.result)
|
|
|
|
|
|
,consumed
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return Eval<Res>{std::nullopt};
|
|
|
|
|
|
}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* A Parser function to match and accept some syntax.
|
|
|
|
|
|
* This is a typing- and interface-adapter, wrapping a Connex.
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class CON>
|
|
|
|
|
|
class Parser
|
|
|
|
|
|
: public CON
|
|
|
|
|
|
{
|
|
|
|
|
|
using PFun = typename CON::PFun;
|
|
|
|
|
|
static_assert (_Fun<PFun>(), "Connex must define a parse-function");
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using Connex = CON;
|
|
|
|
|
|
using Result = typename CON::Result;
|
|
|
|
|
|
|
|
|
|
|
|
static_assert (has_Sig<PFun, Eval<Result>(StrView)>()
|
|
|
|
|
|
,"Signature of the parse-function not suitable");
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Parse-Function operator: test input and yield Eval record
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
Eval<Result>
|
|
|
|
|
|
operator() (StrView toParse)
|
|
|
|
|
|
{
|
2025-01-26 23:54:38 +01:00
|
|
|
|
REQUIRE (_boundFun (CON::parse), "unbound recursive syntax");
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
return CON::parse (toParse);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
|
|
|
|
|
Parser (SPEC&& spec)
|
|
|
|
|
|
: CON{buildConnex (forward<SPEC> (spec))}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/* === Deduction guide : how to construct a Parser === */
|
2025-06-02 17:46:40 +02:00
|
|
|
|
Parser(Nil) -> Parser<NulP>;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
Parser(regex &&) -> Parser<Term>;
|
|
|
|
|
|
Parser(regex const&) -> Parser<Term>;
|
|
|
|
|
|
Parser(string const&) -> Parser<Term>;
|
|
|
|
|
|
|
|
|
|
|
|
template<class FUN>
|
2025-01-26 23:54:38 +01:00
|
|
|
|
Parser(Connex<FUN>) -> Parser<Connex<FUN>>;
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
2025-01-26 23:54:38 +01:00
|
|
|
|
Parser(Syntax<PAR>) -> Parser<typename PAR::Connex>;
|
|
|
|
|
|
|
|
|
|
|
|
template<class RES>
|
|
|
|
|
|
Parser(Syntax<Parser<OpaqueConnex<RES>>>) -> Parser<ForwardConnex<RES>>;
|
|
|
|
|
|
// bind to recursive syntax by reference
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** @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
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<class PAR>
|
|
|
|
|
|
class Syntax
|
|
|
|
|
|
: public Eval<typename PAR::Result>
|
|
|
|
|
|
{
|
|
|
|
|
|
PAR parse_;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using Connex = typename PAR::Connex;
|
|
|
|
|
|
using Result = typename PAR::Result;
|
|
|
|
|
|
|
|
|
|
|
|
Syntax()
|
2025-06-02 17:46:40 +02:00
|
|
|
|
: parse_{Nil()}
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
explicit
|
|
|
|
|
|
Syntax (PAR&& parser)
|
|
|
|
|
|
: parse_{move (parser)}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
explicit
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
operator bool() const { return success();}
|
|
|
|
|
|
|
|
|
|
|
|
operator Connex&() { return parse_; }
|
|
|
|
|
|
operator Connex const&() const { return parse_; }
|
|
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
bool success() const { return bool(Syntax::result); }
|
|
|
|
|
|
bool hasResult() const { return bool(Syntax::result); }
|
|
|
|
|
|
bool canInvoke() const { return _boundFun(parse_.parse);}
|
|
|
|
|
|
size_t consumed() const { return Eval<Result>::consumed; }
|
|
|
|
|
|
Result& getResult() { return * Syntax::result; }
|
|
|
|
|
|
Result&& extractResult() { return move(getResult()); }
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/********************************************//**
|
|
|
|
|
|
* Core API : parse against this syntax clause
|
|
|
|
|
|
*/
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
Syntax&&
|
|
|
|
|
|
parse (StrView toParse)
|
|
|
|
|
|
{
|
|
|
|
|
|
eval() = parse_(toParse);
|
|
|
|
|
|
return move(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
template<class PX>
|
|
|
|
|
|
Syntax&
|
|
|
|
|
|
operator= (Syntax<PX> refSyntax)
|
|
|
|
|
|
{
|
|
|
|
|
|
using ConX = typename PX::Connex;
|
|
|
|
|
|
ConX& refConnex = refSyntax;
|
|
|
|
|
|
parse_.parse = move(refConnex.parse);
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
|
|
|
|
|
/** ===== Syntax clause builder DSL ===== */
|
|
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<typename SPEC>
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
auto seq (SPEC&& clauseDef);
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-01-22 02:21:39 +01:00
|
|
|
|
template<typename SPEC>
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
auto alt (SPEC&& clauseDef);
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
auto opt (SPEC&& clauseDef);
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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);
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
auto repeat (SPEC&& clauseDef);
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
2025-01-24 01:41:55 +01:00
|
|
|
|
template<typename SPEC1, typename SPEC2, typename SPEC3>
|
|
|
|
|
|
auto bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef);
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
|
|
|
|
|
auto bracket (string bracketSpec, SPEC&& bodyDef);
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
|
|
|
|
|
auto bracket (SPEC&& bodyDef);
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
|
|
|
|
|
auto bracketOpt (string bracketSpec, SPEC&& bodyDef);
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
|
|
|
|
|
auto bracketOpt (SPEC&& bodyDef);
|
|
|
|
|
|
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
template<class FUN>
|
|
|
|
|
|
auto bind (FUN&& modelAdapt);
|
|
|
|
|
|
|
|
|
|
|
|
auto bindMatch (uint group =0);
|
|
|
|
|
|
|
2025-01-22 22:31:25 +01:00
|
|
|
|
private:
|
|
|
|
|
|
Eval<Result>& eval() { return *this;}
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
};
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** ===== Syntax clause builder DSL ===== */
|
|
|
|
|
|
|
|
|
|
|
|
/** build a Syntax clause from anything usable as parser-spec. */
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
accept (SPEC&& clauseDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Syntax{Parser{forward<SPEC> (clauseDef)}};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
/** empty Syntax clause to start further definition */
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto accept() { return Syntax<Parser<NulP>>{}; }
|
2025-01-22 22:31:25 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
2025-06-07 23:59:57 +02:00
|
|
|
|
/** start Syntax clause with an optional syntax part */
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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,
|
2025-06-07 23:59:57 +02:00
|
|
|
|
* which implies it is a vector (uses heap storage); if min ≡ 0, the model can be empty.
|
|
|
|
|
|
*/
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
template<typename SPEC1, typename SPEC2>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
accept_repeated (SPEC1&& delimDef, SPEC2&& clauseDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept_repeated (1,uint(-1), forward<SPEC1>(delimDef), forward<SPEC2>(clauseDef));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
accept_repeated (uint min, uint max, SPEC&& clauseDef)
|
|
|
|
|
|
{
|
2025-06-02 17:46:40 +02:00
|
|
|
|
return accept_repeated (min, max, Nil{}, forward<SPEC>(clauseDef));
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
accept_repeated (uint cnt, SPEC&& clauseDef)
|
|
|
|
|
|
{
|
2025-06-02 17:46:40 +02:00
|
|
|
|
return accept_repeated (cnt, Nil{}, forward<SPEC>(clauseDef));
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
accept_repeated (SPEC&& clauseDef)
|
|
|
|
|
|
{
|
2025-06-02 17:46:40 +02:00
|
|
|
|
return accept_repeated (Nil{}, forward<SPEC>(clauseDef));
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
}
|
2025-01-24 01:41:55 +01:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-04-26 23:59:29 +02:00
|
|
|
|
* Start Syntax with a sub-clause enclosed into a _bracketing construct_.
|
|
|
|
|
|
* The »bracket« is defined as syntax for the _open marker_ and _close marker_.
|
2025-01-24 01:41:55 +01:00
|
|
|
|
* These are consumed without generating model elements. The parse fails unless
|
|
|
|
|
|
* the full sequence `open body close` can be matched.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename SPEC1, typename SPEC2, typename SPEC3>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
accept_bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept(
|
|
|
|
|
|
bracketedConnex (Parser{forward<SPEC1>(openDef) }
|
|
|
|
|
|
,Parser{forward<SPEC2>(closeDef)}
|
|
|
|
|
|
,Parser{forward<SPEC3>(bodyDef) }
|
|
|
|
|
|
,false // bracket mandatory, not optional
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Start Syntax with a bracketed sub-clause, with given single-char delimiters.
|
|
|
|
|
|
* \param bracketSpec a 2-char string, e.g. "{}" to expect curly braces.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
accept_bracket (string bracketSpec, SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bracketSpec.size() != 2)
|
|
|
|
|
|
throw err::Invalid{"Bracket spec with opening and closing character expected"};
|
|
|
|
|
|
return accept(
|
|
|
|
|
|
bracketedConnex (Parser{"\\"+bracketSpec.substr(0,1)}
|
|
|
|
|
|
,Parser{"\\"+bracketSpec.substr(1,1)}
|
|
|
|
|
|
,Parser{forward<SPEC>(bodyDef) }
|
|
|
|
|
|
,false // bracket mandatory, not optional
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Start Syntax with a sub-clause enclosed in parentheses */
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
accept_bracket (SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept_bracket ("()", forward<SPEC>(bodyDef));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Start Syntax with a sub-clause, _optionally_ enclosed into brackets. */
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
accept_bracketOpt (string bracketSpec, SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bracketSpec.size() != 2)
|
|
|
|
|
|
throw err::Invalid{"Bracket spec with opening and closing character expected"};
|
|
|
|
|
|
return accept(
|
|
|
|
|
|
bracketedConnex (Parser{"\\"+bracketSpec.substr(0,1)}
|
|
|
|
|
|
,Parser{"\\"+bracketSpec.substr(1,1)}
|
|
|
|
|
|
,Parser{forward<SPEC>(bodyDef) }
|
|
|
|
|
|
,true // bracket optional, can be omitted
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
accept_bracketOpt (SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept_bracketOpt ("()", forward<SPEC>(bodyDef));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-26 23:54:38 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Setup an assignable, recursive Syntax clause, initially empty.
|
|
|
|
|
|
* @remark this provides the foundation for recursive syntax clauses;
|
|
|
|
|
|
* initially, an empty std::function with the pre-declared return
|
|
|
|
|
|
* type is embedded. Together with a special Parser deduction guide,
|
|
|
|
|
|
* later on a full syntax clause can be built, taking a _reference_
|
|
|
|
|
|
* to this function; finally the definition prepared here should be
|
|
|
|
|
|
* _re-assigned_ with the fully defined syntax, which is handled
|
|
|
|
|
|
* by the assignment operator in class Syntax to re-assign a
|
|
|
|
|
|
* working parser function into the std::function holder.
|
|
|
|
|
|
* @tparam RES the result model type to be expected; it is necessary
|
|
|
|
|
|
* to augment the full definition explicitly by a model-binding
|
|
|
|
|
|
* to produce this type — which typically also involves writing
|
|
|
|
|
|
* actual code to deal with the possibly open structure enable
|
|
|
|
|
|
* through a recursive syntax definition
|
|
|
|
|
|
* @see Parse_test::verify_recursiveSyntax()
|
|
|
|
|
|
*/
|
Library: lay out foundation for recursive clauses
In accordance to the plan drafted yesterday, I will try to integrate
this essential capability into the framework established thus far by a trick,
requiring only minimal adjustment, but some work by the user.
Since the parse function is defined as a (unqualified) template argument,
it is possible to emplace either a `std::function`, or a reference thereto.
For this to work, the user is required to pre-define the expected result type,
and, furthermore, must later on assign a fully specified clause, which
also has a model transformation binding attached to yield this predeclared
result type
2025-01-26 15:55:01 +01:00
|
|
|
|
template<typename RES>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: lay out foundation for recursive clauses
In accordance to the plan drafted yesterday, I will try to integrate
this essential capability into the framework established thus far by a trick,
requiring only minimal adjustment, but some work by the user.
Since the parse function is defined as a (unqualified) template argument,
it is possible to emplace either a `std::function`, or a reference thereto.
For this to work, the user is required to pre-define the expected result type,
and, furthermore, must later on assign a fully specified clause, which
also has a model transformation binding attached to yield this predeclared
result type
2025-01-26 15:55:01 +01:00
|
|
|
|
expectResult()
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept (Connex{std::function<Eval<RES>(StrView)>{}});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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.
|
2025-04-26 23:59:29 +02:00
|
|
|
|
* The result model will be a \ref SeqModel, which essentially is a tuple of the
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
* 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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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
|
2025-01-26 23:54:38 +01:00
|
|
|
|
* branches are not tested after finding a matching branch (short-circuit).
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
* 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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
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>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
Syntax<PAR>::repeat (SPEC1&& delimDef, SPEC2&& clauseDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_repeated (forward<SPEC1>(clauseDef)
|
|
|
|
|
|
,forward<SPEC2>(clauseDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
Syntax<PAR>::repeat (SPEC&& clauseDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_repeated (forward<SPEC>(clauseDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-24 01:41:55 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Combinator: extend this Syntax with a further sequenced sub-clause in brackets.
|
|
|
|
|
|
* @see accept_bracket()
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC1, typename SPEC2, typename SPEC3>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
Syntax<PAR>::bracket (SPEC1&& openDef, SPEC2&& closeDef, SPEC3&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_bracket (forward<SPEC1>(openDef)
|
|
|
|
|
|
,forward<SPEC2>(closeDef)
|
|
|
|
|
|
,forward<SPEC3>(bodyDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
Syntax<PAR>::bracket (string bracketSpec, SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_bracket (move (bracketSpec)
|
|
|
|
|
|
,forward<SPEC>(bodyDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
Syntax<PAR>::bracket (SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_bracket (forward<SPEC>(bodyDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
Syntax<PAR>::bracketOpt (string bracketSpec, SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_bracketOpt (move (bracketSpec)
|
|
|
|
|
|
,forward<SPEC>(bodyDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<typename SPEC>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
2025-01-24 01:41:55 +01:00
|
|
|
|
Syntax<PAR>::bracketOpt (SPEC&& bodyDef)
|
|
|
|
|
|
{
|
|
|
|
|
|
return seq (accept_bracketOpt (forward<SPEC>(bodyDef)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
template<class PAR>
|
|
|
|
|
|
template<class FUN>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
Syntax<PAR>::bind (FUN&& modelAdapt)
|
|
|
|
|
|
{
|
|
|
|
|
|
return accept(
|
|
|
|
|
|
adaptConnex (move(parse_)
|
|
|
|
|
|
,forward<FUN>(modelAdapt)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
2025-02-01 02:21:34 +01:00
|
|
|
|
inline auto
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
Syntax<PAR>::bindMatch (uint group)
|
|
|
|
|
|
{
|
2025-01-25 17:00:51 +01:00
|
|
|
|
return accept(
|
|
|
|
|
|
toStringConnex (move(parse_), group));
|
Library: investigate how to approach recursion
Allowing free recursion in grammars is the key enabling feature,
which allows to accept arbitrary complex structures (like numeric expressions).
It is however also the element which makes the task of parsing a challenging endeavour;
after weighting the arguments, I decided ''not to place the focus on advanced usage,''
yet to open a pathway towards representation of such grammars.
Essentially, I consider it acceptable to require some additional work by the user,
if arbitrary recursive grammars are desired; because this design relies on explicitly
given parse functions, we need to introduce some kind of indirection interface,
to allow ''declaring'' a recursive rule first and later to ''supply the definition,''
which obviously then will involve other rules (or itself) recursively.
This leads to a very ''nifty approach'' towards recursion: we require the user
to provide an ''explicit model type'' beforehand, which implies that this is a
simple type, that can be spelled out (no λ) — and so the user is also
''forced to augment the actual rule with a model-binding,'' thereby reducing
the structured return types from the parse into something simple and uniform.
The user ''has to do the hard work,'' but can ''exploit additional knowledge''
related to the specific use case.
All this framework needs to do then is to supply a `std::function`, using the
explicit return type given; everything else will still work as implemented,
since a `std::function` can always stand-in for any arbitrary λ.
2025-01-25 02:48:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
}// namespace parse
|
|
|
|
|
|
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
using parse::accept;
|
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.
2025-01-23 19:48:30 +01:00
|
|
|
|
using parse::accept_opt;
|
|
|
|
|
|
using parse::accept_repeated;
|
2025-02-01 02:21:34 +01:00
|
|
|
|
using parse::accept_bracket;
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
|
|
|
|
|
|
}// namespace util
|
2025-01-26 01:24:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace std { // Specialisation to support C++ »Tuple Protocol« and structured bindings.
|
|
|
|
|
|
|
|
|
|
|
|
/** determine compile-time fixed size of a SeqModel */
|
|
|
|
|
|
template<typename...ELMS>
|
|
|
|
|
|
struct tuple_size<util::parse::SeqModel<ELMS...> >
|
|
|
|
|
|
: tuple_size<typename util::parse::SeqModel<ELMS...>::Tup >
|
|
|
|
|
|
{ };
|
|
|
|
|
|
|
|
|
|
|
|
/** type of the I-th element of a SeqModel -> based on tuple type */
|
|
|
|
|
|
template<size_t I, typename...ELMS>
|
|
|
|
|
|
struct tuple_element<I, util::parse::SeqModel<ELMS...> >
|
|
|
|
|
|
: tuple_element<I, typename util::parse::SeqModel<ELMS...>::Tup >
|
|
|
|
|
|
{ };
|
|
|
|
|
|
}
|
Library: need support for specification parsing
Unfortunately, there are some common syntactic structures, which can not easily be dissected by regular expressions alone, since they entail nested subexpressions. While it is possible to get beyond those fundamental limitations with some trickery, doing so remains precisely that, ''trickery.''
After fighting some inner conflicts, since ''I do know how to write a parser'' —
in the end I have brought myself to just do it.
And indeed, as you'd might expect, I have looked into existing library solutions,
and I would not like to have any one of them as part of the project.
* I do not want a ''parser engine'' or ''parser generator''
* I want the directness of recursive-descent, but combined with Regular Expressions as terminal
* I want to see the structure of the used grammar at the definition site of the custom parser function
* I want deep integration of ''model bindings'' into the parse process, i.e. binding-λ
* I do not want to write model-dissecting or pattern-matching code after the parse
* I do not want to expose ''Monads'' as an interface, since they tend to spread unhealthy structure to surrounding code
* I do not want to leak technicalities of the parse mechanics into the using code
* I do not want to impose hard to remember specific conventions onto the user
Thus I've set the following aims:
* The usage should require only a single header include (ideally header-only)
* The entrance point should be a small number of DSL-starter functions
* The parser shall be implemented by recursive-descent, using the parser-combinator technique
* But I want that wrapped into a DSL, to be able to control what is (not) provided or exposed.
* I want a stateful, applicative logic, since parsing, by its very nature, is stateful!
* I want complete compile-time typing, visible to the optimiser, without a virtual »Parser« interface
And last but not least, ''I do not want to create a ticket, since I do not know if those goals can be achieved...''
2025-01-17 18:40:44 +01:00
|
|
|
|
#endif/*LIB_PARSE_H*/
|