diff --git a/README.md b/README.md index 62f7a1700..5be9de9b8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ X4 scales from quick prototypes to production parsers for DSLs, data formats, an - C++23 and C++26 - GCC 14 - Clang 21 -- MSVC 2022 and 2026 +- MSVC 2026 ## How to use X4 @@ -28,3 +28,193 @@ Edit your `CMakeLists.txt`: add_subdirectory(modules/x4) target_link_libraries(my_app PUBLIC Iris::X4) ``` + + +## Terminology + +### "attribute" + +An *attribute* is the value produced by a successful parse. It represents the semantic result of a parser after it consumes input, and is propagated through combinators, directives, and rules according to their transformation rules. Attributes may be primitive values, containers, or user-defined types, and can be constructed, transformed, or suppressed depending on the parser expression. + +Phrases like "the attribute type of a parser" usually refer to the value type of the attribute produced by that parser class. + +### "semantic action" + +A *semantic action* is a user-provided invocable object (usually a lambda) that is executed when a parser successfully matches its input. It is used to inspect, transform, or validate the parsed result, and may optionally influence control flow by accepting or rejecting the match. Semantic actions operate on the current parsing context and the attribute produced by the parser, allowing fine-grained post-processing of successful parses. + +The primary syntax for attaching a semantic action is `p.on_match(f)`, where `f` is invoked after `p` matches. The callable may observe the matched attribute, access contextual information, and optionally return a boolean to either accept the result or force the parser to treat the match as a failure, enabling backtracking when appropriate. + +The signature of a semantic action is `[](auto&& ctx) { /* ... */ }`. + +> While highly flexible and convenient, **it is generally discouraged to introduce semantic actions prematurely as part of a language's syntax definition,** since most grammars can be expressed purely through combinations of concrete parsers (typically `x4::rule` parsers) when they are properly structured. +> +> The primary intended use of semantic actions is to handle cases that require ad hoc transformation, such as constructing a binary operator object through a more complex algorithm like precedence climbing. + + +## Directory Structure + +Each parser header is organized into subdirectories as needed, but there are several special directories that contain very specific kinds of components. These are described below. + +#### `core/` + +The core components of X4. In contrast to the facilities in the `traits/` directory, the core components are not intended to be user-customizable. + +> **Note for contributors:** every non-detail header in this directory must start with `#include `. + +#### `traits/` + +Customizable type traits that allow user-defined types to participate in parser logic or attribute processing. A trait may alter parser semantics, type classification, compatibility checks, attribute propagation, storage, conversion, or transformation. + +#### `operator/` + +Directives or combinators that use overloaded C++ operators as their composition syntax. They form the fundamental grammar-composition vocabulary, including sequence, alternative, repetition, optionality, predicates, difference, and list composition. + +#### `directive/` + +Parser adapters or combinators that operate on one or more subject parsers and modify parsing behavior, context, control flow, repetition, or interpretation. They do not use special C++ operators for composition, but normally use subscript syntax such as `directive[p]` to express the combination. Facilities whose primary purpose is value production or attribute representation may instead belong to `attribute/`. + +#### Other Subdirectories + +Remaining facilities that do not fit the categories described above are organized into additional subdirectories when a clear semantic grouping warrants it. + + +## Quick Reference + +The descriptions below focus on recognition behavior and omit many details concerning attribute propagation and customization. + +### Operators + +| Syntax | Meaning | +| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `a \| b` | Try `a`. If it fails normally, restore the input position and try `b`. An expectation failure prevents the fallback to `b`. | +| `a >> b` | Parse `a`, followed by `b`. If either parser fails normally, restore the input position to the beginning of the sequence. | +| `a > b` | Parse `a`, followed by an expected `b`. Equivalent to `a >> expect[b]`. Failure of `b` records an expectation failure and prevents ordinary backtracking. | +| `*p` | Parse zero or more occurrences of `p`. | +| `+p` | Parse one or more occurrences of `p`. | +| `p % d` | Parse one or more occurrences of `p`, separated by `d`. | +| `a - b` | Parse `a` only when `b` does not match at the same input position. The test of `b` does not consume input. | +| `-p` | Parse zero or one occurrence of `p`. | +| `&p` | Succeed when `p` matches, without consuming input or producing its attribute. | +| `!p` | Succeed when `p` does not match, without consuming input or producing an attribute. | + +### Major Directives + +These facilities commonly appear in production language grammars. + +| Syntax | Meaning | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `p.on_match(f)` | After `p` matches, invoke the semantic action `f`. The action may inspect or modify the parsing context and may reject the match. | +| `expect[p]` | Parse `p`. If it fails, record an expectation failure that prevents ordinary backtracking and alternative recovery. Usually invoked indirectly via the `a > b` syntax. | +| `lexeme[p]` | Perform the normal pre-skip, then parse `p` with automatic skipping disabled inside it. | +| `with(value)[p]` | Bind `value` to the context id `ID` while parsing `p`.
The instance be fetched via `x4::get(ctx)` in semantic action. | +| `with_local[p]` | Create a value-initialized local value of type `T` for each invocation of `p` and bind it to the context id `ID`. If `ID` is omitted, the default local-variable context id (`x4::contexts::local_var`) is used.
The instance be fetched via `x4::get(ctx)` or `x4::_local_var(ctx)`, respectively, in semantic action. | + +### Minor Directives + +These facilities are used less frequently in ordinary language grammars. + +| Syntax | Meaning | +| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `omit[p]` | Parse `p`, but suppress its attribute. | +| `skip(s)[p]` | Parse `p` using `s` as its active skipper, replacing the previously active skipper within the directive. | +| `no_skip[p]` | Parse `p` without performing a pre-skip and with automatic skipping disabled inside it. Most ordinary contiguous-token parsing can instead use `lexeme[p]`. | +| `no_case[p]` | Apply case-insensitive character and string comparison while parsing `p`. | +| `matches[p]` | Attempt to parse `p` and expose the result as `bool`: `true` when `p` matches and `false` when it fails normally. | +| `repeat(n)[p]` | Parse exactly `n` occurrences of `p`. | +| `repeat(min, max)[p]` | Parse between `min` and `max` occurrences of `p`, inclusive. | +| `repeat(min, x4::repeat_inf)[p]` | Parse at least `min` occurrences of `p`, with no upper limit. | +| `without[p]` | Remove every context entry whose key is one of `IDs...` while parsing `p`.
Useful for sanitizing the context type correlated with the `x4::rule` type required by `IRIS_X4_INSTANTIATE`. | + +### Attribute Facilities + +| Syntax | Meaning | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `as(p)` | Force `p` to expose `T` as its attribute type. When the outer destination has another compatible type, parse into a temporary `T` and move the result into the destination. | +| `fixed_value(value)` | Always succeed without consuming input and copy the stored `value` into the exposed attribute. | +| `reset_value` | Always succeed without consuming input and reset the exposed attribute. Containers are cleared; other values are assigned a value-initialized instance. | +| `unique_ptr(p)` | Expose a `std::unique_ptr` attribute and parse `p` into its pointee. Deduce the pointee type from the attribute of `p`. | +| `unique_ptr(p)` | Expose a `std::unique_ptr` attribute and parse `p` into its pointee. An optional deleter type `D` may also be specified. | +| `shared_ptr(p)` | Expose a `std::shared_ptr` attribute and parse `p` into its pointee. Deduce the pointee type from the attribute of `p`. | +| `shared_ptr(p)` | Expose a `std::shared_ptr` attribute and parse `p` into its pointee. An optional deleter type `D` may also be specified. | + +### Primitives + +| Syntax | Meaning | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `eol` | Match and consume an end-of-line sequence: `"\n"`, `"\r"`, or `"\r\n"`. | +| `eoi` | Succeed only when the input has been exhausted. | +| `eps` | Always succeed without consuming input. | +| `eps(cond)` | Succeed without consuming input when `cond` is `true`. | +| `eps(f)` | Lazily invoke `f` and succeed without consuming input when it returns `true`. The callable may accept the parsing context or no arguments. | + +### Character and String Parsers + +| Syntax | Meaning | +| ----------------- | -------------------------------------------------------------------------------------------- | +| `lit('a')` | Match the character `'a'` without producing an attribute. | +| `lit("str")` | Match the exact string `"str"` without producing an attribute. | +| `char_` | Match any valid character and expose the matched character. | +| `char_('a')` | Match the character `'a'` and expose it. | +| `char_("abc")` | Match one character from the set `{'a', 'b', 'c'}` and expose it. | +| `char_("a-zA-Z")` | Match and expose one character from the specified character set. A hyphen defines an inclusive range, so this example matches an alphabet. | +| `char_('a', 'z')` | Match one character in the inclusive range `'a'` through `'z'` and expose it. | +| `~char_(...)` | Match and expose one character not accepted by the enclosed character parser, set, or range. | +| `string("str")` | Match the exact string `"str"` and expose the matched string. | + +### Numeric Parsers + +#### Boolean Parsers + +| Syntax | Meaning | +| -------- | ---------------------------------------------------------------------- | +| `bool_` | Match `"true"` or `"false"` and expose the corresponding `bool` value. | +| `true_` | Match `"true"` and expose `true`. | +| `false_` | Match `"false"` and expose `false`. | + +#### Signed Integer Parsers + +| Syntax | Meaning | +| ----------- | --------------------------------------------------------------- | +| `short_` | Parse a signed integer and expose the result as `short`. | +| `int_` | Parse a signed integer and expose the result as `int`. | +| `long_` | Parse a signed integer and expose the result as `long`. | +| `long_long` | Parse a signed integer and expose the result as `long long`. | +| `int8` | Parse a signed integer and expose the result as `std::int8_t`. | +| `int16` | Parse a signed integer and expose the result as `std::int16_t`. | +| `int32` | Parse a signed integer and expose the result as `std::int32_t`. | +| `int64` | Parse a signed integer and expose the result as `std::int64_t`. | + +#### Unsigned Integer Parsers + +| Syntax | Meaning | +| ------------ | ----------------------------------------------------------------------- | +| `ushort_` | Parse an unsigned integer and expose the result as `unsigned short`. | +| `uint_` | Parse an unsigned integer and expose the result as `unsigned int`. | +| `ulong_` | Parse an unsigned integer and expose the result as `unsigned long`. | +| `ulong_long` | Parse an unsigned integer and expose the result as `unsigned long long`. | +| `uint8` | Parse an unsigned integer and expose the result as `std::uint8_t`. | +| `uint16` | Parse an unsigned integer and expose the result as `std::uint16_t`. | +| `uint32` | Parse an unsigned integer and expose the result as `std::uint32_t`. | +| `uint64` | Parse an unsigned integer and expose the result as `std::uint64_t`. | +| `bin` | Parse a base-2 unsigned integer and expose the result as `unsigned int`. | +| `oct` | Parse a base-8 unsigned integer and expose the result as `unsigned int`. | +| `hex` | Parse a base-16 unsigned integer and expose the result as `unsigned int`. | + +#### Real Number Parsers + +| Syntax | Meaning | +| ------------- | ---------------------------------------------------------- | +| `float_` | Parse a signed real number and expose the result as `float`. | +| `double_` | Parse a signed real number and expose the result as `double`. | +| `long_double` | Parse a signed real number and expose the result as `long double`. | + +### Context Fetchers + +The following function objects retrieve commonly used values from the parsing context (usually via a semantic action). Each fetcher returns the context entry itself, normally by reference, and is available only when the corresponding entry exists in the current context. + +| Syntax | Meaning | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `_attr(ctx)` | Return the attribute associated with the current semantic action. This is the attribute instance produced by the parser `p` on the expression `p.on_match(f)`. It is available only when that parser exposes a non-`unused_type` attribute. Equivalent to `x4::get(ctx)`. | +| `_rule_var(ctx)` | Return the attribute variable of the innermost active `x4::rule` invocation. In a recursive rule, this always refers to the current recursive invocation rather than an outer invocation. Equivalent to `x4::get(ctx)`. | +| `_local_var(ctx)` | Return the innermost local variable created by `with_local[p]` using the default `x4::contexts::local_var` context id. Equivalent to `x4::get(ctx)`.
*Note:* when `with_local[p]` uses a custom id, retrieve the value with `x4::get(ctx)` instead. | +| `_as_var(ctx)` | Return the attribute variable managed by the innermost active `as(p)` facility. Semantic actions inside `p` can use this fetcher to access the value being constructed for the enclosing `as` parser. Equivalent to `x4::get(ctx)`. | diff --git a/include/iris/x4.hpp b/include/iris/x4.hpp index 27d6bc77f..55dd2fd0d 100644 --- a/include/iris/x4.hpp +++ b/include/iris/x4.hpp @@ -11,8 +11,10 @@ ==============================================================================*/ #include + #include -#include +#include +#include #include #include #include @@ -20,4 +22,7 @@ #include #include +#include +#include + #endif diff --git a/include/iris/x4/attribute.hpp b/include/iris/x4/attribute.hpp new file mode 100644 index 000000000..0ffbb29f9 --- /dev/null +++ b/include/iris/x4/attribute.hpp @@ -0,0 +1,16 @@ +#ifndef IRIS_ZZ_X4_ATTRIBUTE_HPP +#define IRIS_ZZ_X4_ATTRIBUTE_HPP + +/*============================================================================= + Copyright (c) 2026 The Iris Project Contributors + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +// #include // excluded +#include + +#endif diff --git a/include/iris/x4/directive/as.hpp b/include/iris/x4/attribute/as.hpp similarity index 81% rename from include/iris/x4/directive/as.hpp rename to include/iris/x4/attribute/as.hpp index 6f4c1d47c..d64a9f4ed 100644 --- a/include/iris/x4/directive/as.hpp +++ b/include/iris/x4/attribute/as.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_DIRECTIVE_AS_HPP -#define IRIS_ZZ_X4_DIRECTIVE_AS_HPP +#ifndef IRIS_ZZ_X4_ATTRIBUTE_AS_HPP +#define IRIS_ZZ_X4_ATTRIBUTE_AS_HPP /*============================================================================= Copyright (c) 2025 Nana Sakisaka @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -23,12 +25,12 @@ namespace iris::x4 { namespace detail { template -struct as_directive_ctx_impl // false +struct as_type_parser_ctx_impl // false { using type = Context; }; template -struct as_directive_ctx_impl +struct as_type_parser_ctx_impl { using type = std::remove_cvref_t( std::declval(), @@ -38,11 +40,11 @@ struct as_directive_ctx_impl } // detail -// `as_directive` forces the attribute of subject parser +// `as_type_parser` forces the attribute of subject parser // to be `T`. When `T` is `unused_type`, this is equivalent to // `omit_directive`. template -struct as_directive : unary_parser> +struct as_type_parser : unary_parser> { static_assert(!std::is_const_v); // Forbid const `unused_type` static_assert(!std::same_as); // Unknown use case, not supported for now @@ -55,9 +57,9 @@ struct as_directive : unary_parser> static constexpr bool has_action = false; // Explicitly re-enable attribute detection in `x4::rule` static constexpr bool requires_exact_attribute_type = true; - // `as_directive` should NOT inherit underlying parser's `handles_container` - // because `as_directive` is an atomic parser. The default implementation of - // `parser_traits>::handles_container` must transparently + // `as_type_parser` should NOT inherit underlying parser's `handles_container` + // because `as_type_parser` is an atomic parser. The default implementation of + // `parser_traits>::handles_container` must transparently // handle this case. private: @@ -72,7 +74,7 @@ struct as_directive : unary_parser> requires std::same_as, T> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const - noexcept(is_nothrow_parsable_v::type, exposed_attr_for_child_t>) + noexcept(is_nothrow_parsable_v::type, exposed_attr_for_child_t>) { if constexpr (Subject::has_action) { return this->subject.parse(first, last, x4::replace_first_context(ctx, outer_attr), unused); @@ -87,7 +89,7 @@ struct as_directive : unary_parser> (!std::same_as, T>) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr&) const - noexcept(is_nothrow_parsable_v::type, unused_type>) + noexcept(is_nothrow_parsable_v::type, unused_type>) { if constexpr (Subject::has_action) { return this->subject.parse(first, last, x4::replace_first_context(ctx, unused), unused); @@ -104,7 +106,7 @@ struct as_directive : unary_parser> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const noexcept( - is_nothrow_parsable_v::type, exposed_attr_for_child_t> && + is_nothrow_parsable_v::type, exposed_attr_for_child_t> && noexcept(x4::move_to(std::declval(), outer_attr)) ) { @@ -134,9 +136,9 @@ template struct as_fn { template - [[nodiscard]] static constexpr as_directive> + [[nodiscard]] static constexpr as_type_parser> operator()(Subject&& subject) - noexcept(is_parser_nothrow_constructible_v>, Subject>) + noexcept(is_parser_nothrow_constructible_v>, Subject>) { return {std::forward(subject)}; } @@ -144,14 +146,14 @@ struct as_fn } // detail -namespace parsers::directive { +namespace parsers { template [[maybe_unused]] inline constexpr detail::as_fn as{}; -} // parsers::directive +} // parsers -using parsers::directive::as; +using parsers::as; } // iris::x4 diff --git a/include/iris/x4/auxiliary/attr.hpp b/include/iris/x4/attribute/value.hpp similarity index 64% rename from include/iris/x4/auxiliary/attr.hpp rename to include/iris/x4/attribute/value.hpp index 5e1b31fea..30f296595 100644 --- a/include/iris/x4/auxiliary/attr.hpp +++ b/include/iris/x4/attribute/value.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_AUXILIARY_ATTR_HPP -#define IRIS_ZZ_X4_AUXILIARY_ATTR_HPP +#ifndef IRIS_ZZ_X4_ATTRIBUTE_VALUE_HPP +#define IRIS_ZZ_X4_ATTRIBUTE_VALUE_HPP /*============================================================================= Copyright (c) 2001-2011 Hartmut Kaiser @@ -26,16 +26,17 @@ namespace iris::x4 { +// `fixed_value(...)` template -struct attr_parser : parser> +struct fixed_value_parser : parser> { static_assert(X4Attribute); - static_assert(!X4UnusedAttribute, "attr_parser with `unused_type` is meaningless"); + static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); // `HeldValueT` is almost always equal to `T`. // - // The most notable situation where they differ is when `attr_parser` is initialized - // by `char const (&)[N]`. In such case, `attr_parser` must hold the value by + // The most notable situation where they differ is when `fixed_value_parser` is initialized + // by `char const (&)[N]`. In such case, `fixed_value_parser` must hold the value by // `std::string_view`, instead of `std::string`, to be constexpr. static_assert(X4Movable); @@ -45,9 +46,9 @@ struct attr_parser : parser> template requires - (!std::is_same_v, attr_parser>) && + (!std::is_same_v, fixed_value_parser>) && std::is_constructible_v - constexpr explicit attr_parser(U&& value) + constexpr explicit fixed_value_parser(U&& value) noexcept(std::is_nothrow_constructible_v) : held_value_(std::forward(value)) {} @@ -66,12 +67,12 @@ struct attr_parser : parser> HeldValueT held_value_; }; -// `init_attr` +// `reset_value` template -struct attr_parser : parser> +struct fixed_value_parser : parser> { static_assert(X4Attribute); - static_assert(!X4UnusedAttribute, "attr_parser with `unused_type` is meaningless"); + static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); using attribute_type = T; @@ -105,7 +106,7 @@ struct attr_parser : parser> namespace detail { template -using string_array_attr_parser_t = attr_parser< +using string_array_attr_parser_t = fixed_value_parser< std::basic_string>>, std::basic_string_view>> >; @@ -113,40 +114,44 @@ using string_array_attr_parser_t = attr_parser< } // detail template -attr_parser(R const&) -> attr_parser< +fixed_value_parser(R const&) -> fixed_value_parser< std::basic_string>>, std::basic_string_view>> >; template -struct get_info> +struct get_info> { using result_type = std::string; [[nodiscard]] constexpr std::string - operator()(attr_parser const&) const + operator()(fixed_value_parser const&) const { - return "attr"; + if constexpr (std::is_void_v) { + return "reset_value"; + } else { + return "fixed_value(...)"; + } } }; namespace detail { -struct attr_gen +struct fixed_value_gen { template - [[nodiscard]] static constexpr attr_parser> + [[nodiscard]] static constexpr fixed_value_parser> operator()(T&& value) - noexcept(std::is_nothrow_constructible_v>, T>) + noexcept(std::is_nothrow_constructible_v>, T>) { - return attr_parser>{std::forward(value)}; + return fixed_value_parser>{std::forward(value)}; } - template - [[nodiscard]] static constexpr string_array_attr_parser_t - operator()(R&& value) - noexcept(std::is_nothrow_constructible_v, R>) + template + [[nodiscard]] static constexpr string_array_attr_parser_t + operator()(CharArrayT&& char_array) + noexcept(std::is_nothrow_constructible_v, CharArrayT>) { - return string_array_attr_parser_t{std::forward(value)}; + return string_array_attr_parser_t{std::forward(char_array)}; } }; @@ -156,21 +161,21 @@ namespace parsers { // An always-succeeding parser that has the `attribute_type` equivalent // to the given parameter. Copies the held instance on each invocation. -[[maybe_unused]] inline constexpr detail::attr_gen attr{}; +[[maybe_unused]] inline constexpr detail::fixed_value_gen fixed_value{}; -// A special `attr` parser that resets the variable and always succeeds. +// A special `fixed_value` parser that resets the variable and always succeeds. // // This can be used for constructing `constexpr` instance of a parser // even when `T` has dynamically allocated storage. -// For example, normal `attr(std::vector{})` cannot be assigned -// to a `constexpr` instance, but `init_attr>` can. +// For example, normal `fixed_value(std::vector{})` cannot be assigned +// to a `constexpr` instance, but `reset_value>` can. template -[[maybe_unused]] inline constexpr attr_parser init_attr{}; +[[maybe_unused]] inline constexpr fixed_value_parser reset_value{}; } // parsers -using parsers::attr; -using parsers::init_attr; +using parsers::fixed_value; +using parsers::reset_value; } // iris::x4 diff --git a/include/iris/x4/core/action.hpp b/include/iris/x4/core/action.hpp index d0c02ee18..b3b70234b 100644 --- a/include/iris/x4/core/action.hpp +++ b/include/iris/x4/core/action.hpp @@ -249,27 +249,6 @@ struct action : proxy_parser> } }; -template -[[nodiscard, deprecated( - "Use `operator[]` instead. The symbol `/` normally means \"ordered choice\" " - "in PEG, and is irrelevant to semantic actions. Furthermore, using C++'s " - "`operator/` for this purpose may introduce surprising behavior when it's " - "mixed with ordinary PEG operators, for instance, the unary `operator+`, " - "due to precedence." -)]] -constexpr action, std::remove_cvref_t> -operator/(Subject&& p, Action&& f) - noexcept( - is_parser_nothrow_castable_v && - std::is_nothrow_constructible_v< - action, std::remove_cvref_t>, - as_parser_t, Action - > - ) -{ - return {as_parser(std::forward(p)), std::forward(f)}; -} - } // iris::x4 #endif diff --git a/include/iris/x4/core/move_to.hpp b/include/iris/x4/core/move_to.hpp index bb1c7589a..025c2da7f 100644 --- a/include/iris/x4/core/move_to.hpp +++ b/include/iris/x4/core/move_to.hpp @@ -240,7 +240,7 @@ move_to(It first, Se last, Dest& dest) } // Move non-container `src` into container `dest`. -// e.g. Source=std::string_view, Dest=std::string (used in `attr_parser`) +// e.g. Source=std::string_view, Dest=std::string (used in `fixed_value_parser`) template Dest> requires (!traits::X4Container) && diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index 24ad68ba5..08c1f6a7b 100644 --- a/include/iris/x4/core/parser.hpp +++ b/include/iris/x4/core/parser.hpp @@ -76,8 +76,9 @@ struct parser : private detail::parser_base decltype(std::declval().derived()), Action > - [[nodiscard]] constexpr action> - operator[](this Self&& self, Action&& f) + [[nodiscard]] + constexpr action> + on_match(this Self&& self, Action&& f) noexcept(std::is_nothrow_constructible_v< action>, decltype(std::forward(self).derived()), @@ -86,6 +87,24 @@ struct parser : private detail::parser_base { return {std::forward(self).derived(), std::forward(f)}; } + + template + requires std::is_constructible_v< + action>, + decltype(std::declval().derived()), + Action + > + [[nodiscard, deprecated("Use `p.on_match(...)` instead. The legacy `operator[]` syntax will be removed because it frequently conflicts with lambda syntax.")]] + constexpr action> + operator[](this Self&& self, Action&& f) + noexcept(std::is_nothrow_constructible_v< + action>, + decltype(std::forward(self).derived()), + Action + >) + { + return std::forward(self).on_match(std::forward(f)); + } }; template diff --git a/include/iris/x4/directive.hpp b/include/iris/x4/directive.hpp index 898383ef1..33c3bcc76 100644 --- a/include/iris/x4/directive.hpp +++ b/include/iris/x4/directive.hpp @@ -11,7 +11,6 @@ ==============================================================================*/ #include -#include #include #include #include @@ -19,10 +18,8 @@ #include #include #include -#include #include #include #include -#include #endif diff --git a/include/iris/x4/directive/seek.hpp b/include/iris/x4/directive/seek.hpp deleted file mode 100644 index 7d98ce4ff..000000000 --- a/include/iris/x4/directive/seek.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef IRIS_ZZ_X4_DIRECTIVE_SEEK_HPP -#define IRIS_ZZ_X4_DIRECTIVE_SEEK_HPP - -/*============================================================================= - Copyright (c) 2011 Jamboree - Copyright (c) 2014 Lee Clagett - Copyright (c) 2017 wanghan02 - Copyright (c) 2024-2025 Nana Sakisaka - Copyright (c) 2026 The Iris Project Contributors - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -==============================================================================*/ - -#include -#include - -#include -#include -#include - -namespace iris::x4 { - -template -struct seek_directive : proxy_parser> -{ - template Se, class Context, X4Attribute Attr> - [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - { - for (It current(first); ; ++current) { - if (this->subject.parse(current, last, ctx, attr)) { - first = current; - return true; - } - - if constexpr (has_context_v) { - if (x4::has_expectation_failure(ctx)) { - return false; - } - } - - // fail only after subject fails & no input - if (current == last) return false; - } - } -}; - -namespace detail { - -struct seek_gen -{ - template - [[nodiscard]] constexpr seek_directive> - operator[](Subject&& subject) const // TODO: MSVC does not support static operator[] - noexcept(is_parser_nothrow_constructible_v>, Subject>) - { - return {as_parser(std::forward(subject))}; - } -}; - -} // detail - -namespace parsers::directive { - -[[maybe_unused]] inline constexpr detail::seek_gen seek{}; - -} // parsers::directive - -using parsers::directive::seek; - -} // iris::x4 - -#endif diff --git a/include/iris/x4/directive/with.hpp b/include/iris/x4/directive/with.hpp index b0ca2b5ac..236b24436 100644 --- a/include/iris/x4/directive/with.hpp +++ b/include/iris/x4/directive/with.hpp @@ -224,6 +224,62 @@ template using parsers::directive::with; + +// -------------------------------------------------------- +// -------------------------------------------------------- +// -------------------------------------------------------- + + +template +struct without_directive : proxy_parser> +{ + template Se, class Context, X4Attribute Attr> + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& ctx, Attr& attr) const + noexcept( + x4::is_nothrow_parsable_v< + Subject, It, Se, + std::remove_cvref_t(ctx))>, + Attr + > + ) + { + return this->subject.parse(first, last, x4::remove_all_contexts(ctx), attr); + } + + [[nodiscard]] constexpr std::string get_x4_info() const + { + return std::format("without<...>[{}]", get_info{}(this->subject)); + } +}; + +namespace detail { + +template +struct without_gen +{ + template + [[nodiscard]] constexpr without_directive, IDs...> + operator[](Subject&& subject) const // TODO: MSVC 2022 does not properly handle static operator[] + noexcept(std::is_nothrow_constructible_v, IDs...>, Subject>) + { + return without_directive, IDs...>{ + std::forward(subject) + }; + } +}; + +} // detail + +namespace parsers::directive { + +template +[[maybe_unused]] inline constexpr detail::without_gen without{}; + +} // parsers::directive + +using parsers::directive::without; + } // iris::x4 #endif diff --git a/include/iris/x4/directive/without.hpp b/include/iris/x4/directive/without.hpp deleted file mode 100644 index 123f414b1..000000000 --- a/include/iris/x4/directive/without.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef IRIS_ZZ_X4_DIRECTIVE_WITHOUT_HPP -#define IRIS_ZZ_X4_DIRECTIVE_WITHOUT_HPP - -/*============================================================================= - Copyright (c) 2026 The Iris Project Contributors - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#include -#include - -#include -#include -#include -#include - -namespace iris::x4 { - -template -struct without_directive : proxy_parser> -{ - template Se, class Context, X4Attribute Attr> - [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - x4::is_nothrow_parsable_v< - Subject, It, Se, - std::remove_cvref_t(ctx))>, - Attr - > - ) - { - return this->subject.parse(first, last, x4::remove_all_contexts(ctx), attr); - } - - [[nodiscard]] constexpr std::string get_x4_info() const - { - return std::format("without<...>[{}]", get_info{}(this->subject)); - } -}; - -namespace detail { - -template -struct without_gen -{ - template - [[nodiscard]] constexpr without_directive, IDs...> - operator[](Subject&& subject) const // TODO: MSVC 2022 does not properly handle static operator[] - noexcept(std::is_nothrow_constructible_v, IDs...>, Subject>) - { - return without_directive, IDs...>{ - std::forward(subject) - }; - } -}; - -} // detail - -namespace parsers::directive { - -template -[[maybe_unused]] inline constexpr detail::without_gen without{}; - -} // parsers::directive - -using parsers::directive::without; - -} // iris::x4 - -#endif diff --git a/include/iris/x4/operator.hpp b/include/iris/x4/operator.hpp index dfcc0ef58..96224a9a6 100644 --- a/include/iris/x4/operator.hpp +++ b/include/iris/x4/operator.hpp @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/include/iris/x4/operator/list.hpp b/include/iris/x4/operator/delimited_list.hpp similarity index 93% rename from include/iris/x4/operator/list.hpp rename to include/iris/x4/operator/delimited_list.hpp index 18258b92a..d9df58eaa 100644 --- a/include/iris/x4/operator/list.hpp +++ b/include/iris/x4/operator/delimited_list.hpp @@ -24,7 +24,7 @@ namespace iris::x4 { template -struct list : binary_parser> +struct delimited_list : binary_parser> { using attribute_type = traits::default_container::attribute_type>::type; @@ -34,7 +34,7 @@ struct list : binary_parser> traits::can_hold::attribute_type, typename traits::container_value::type> >; - using binary_parser::binary_parser; + using binary_parser::binary_parser; template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool @@ -108,13 +108,13 @@ struct list : binary_parser> }; template -[[nodiscard]] constexpr list, as_parser_plain_t> +[[nodiscard]] constexpr delimited_list, as_parser_plain_t> operator%(Left&& left, Right&& right) noexcept( is_parser_nothrow_castable_v && is_parser_nothrow_castable_v && std::is_nothrow_constructible_v< - list, as_parser_plain_t>, + delimited_list, as_parser_plain_t>, as_parser_t, as_parser_t > diff --git a/include/iris/x4/auxiliary.hpp b/include/iris/x4/primitive.hpp similarity index 71% rename from include/iris/x4/auxiliary.hpp rename to include/iris/x4/primitive.hpp index 6f9558d2d..87dd63852 100644 --- a/include/iris/x4/auxiliary.hpp +++ b/include/iris/x4/primitive.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_AUXILIARY_HPP -#define IRIS_ZZ_X4_AUXILIARY_HPP +#ifndef IRIS_ZZ_X4_PRIMITIVE_HPP +#define IRIS_ZZ_X4_PRIMITIVE_HPP /*============================================================================= Copyright (c) 2001-2014 Joel de Guzman @@ -12,9 +12,8 @@ ==============================================================================*/ #include -#include -#include -#include -#include +#include +#include +#include #endif diff --git a/include/iris/x4/auxiliary/eoi.hpp b/include/iris/x4/primitive/eoi.hpp similarity index 95% rename from include/iris/x4/auxiliary/eoi.hpp rename to include/iris/x4/primitive/eoi.hpp index 426919743..d276da8ec 100644 --- a/include/iris/x4/auxiliary/eoi.hpp +++ b/include/iris/x4/primitive/eoi.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_AUXILIARY_EOI_HPP -#define IRIS_ZZ_X4_AUXILIARY_EOI_HPP +#ifndef IRIS_ZZ_X4_PRIMITIVE_EOI_HPP +#define IRIS_ZZ_X4_PRIMITIVE_EOI_HPP /*============================================================================= Copyright (c) 2001-2014 Joel de Guzman diff --git a/include/iris/x4/auxiliary/eol.hpp b/include/iris/x4/primitive/eol.hpp similarity index 95% rename from include/iris/x4/auxiliary/eol.hpp rename to include/iris/x4/primitive/eol.hpp index b52529aa7..20bc70999 100644 --- a/include/iris/x4/auxiliary/eol.hpp +++ b/include/iris/x4/primitive/eol.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_AUXILIARY_EOL_HPP -#define IRIS_ZZ_X4_AUXILIARY_EOL_HPP +#ifndef IRIS_ZZ_X4_PRIMITIVE_EOL_HPP +#define IRIS_ZZ_X4_PRIMITIVE_EOL_HPP /*============================================================================= Copyright (c) 2001-2014 Joel de Guzman diff --git a/include/iris/x4/auxiliary/eps.hpp b/include/iris/x4/primitive/eps.hpp similarity index 98% rename from include/iris/x4/auxiliary/eps.hpp rename to include/iris/x4/primitive/eps.hpp index 0c1f1165e..2e9f99b5a 100644 --- a/include/iris/x4/auxiliary/eps.hpp +++ b/include/iris/x4/primitive/eps.hpp @@ -1,5 +1,5 @@ -#ifndef IRIS_ZZ_X4_AUXILIARY_EPS_HPP -#define IRIS_ZZ_X4_AUXILIARY_EPS_HPP +#ifndef IRIS_ZZ_X4_PRIMITIVE_EPS_HPP +#define IRIS_ZZ_X4_PRIMITIVE_EPS_HPP /*============================================================================= Copyright (c) 2001-2014 Joel de Guzman diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index 344d8ca61..de7c06650 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -268,7 +268,7 @@ struct rule_impl // // Note: `x4::as(...)` explicitly unsets `has_action` even if the underlying subject // has semantic action, so it will be dispatched to the latter branch (unless the - // `as_directive` itself has semantic action). + // `as_type_parser` itself has semantic action). if constexpr (RHS::has_action) { if constexpr (ForceAttr) { parse_ok = rule_impl::parse_rhs( diff --git a/test/x4/CMakeLists.txt b/test/x4/CMakeLists.txt index bf0d0e4e5..1fbf60563 100644 --- a/test/x4/CMakeLists.txt +++ b/test/x4/CMakeLists.txt @@ -40,10 +40,10 @@ endfunction() x4_define_tests( actions + alloy_wrong_substitute alternative and_predicate as - attr attribute_type_check bool char @@ -51,6 +51,7 @@ x4_define_tests( container_support context debug + delimited_list difference eoi eol @@ -63,7 +64,6 @@ x4_define_tests( iterator kleene lexeme - list lit matches move_to @@ -84,7 +84,6 @@ x4_define_tests( rule2 rule3 rule4 - seek sequence skip smart_ptr @@ -96,11 +95,11 @@ x4_define_tests( uint uint_radix unused + value with with_local without x3_rule_problem - alloy_wrong_substitute_test_case ) x4_define_test(rule_separate_tu rule_separate_tu.cpp rule_separate_tu_grammar.cpp) diff --git a/test/x4/actions.cpp b/test/x4/actions.cpp index dd4aa10e6..96fb0fdc1 100644 --- a/test/x4/actions.cpp +++ b/test/x4/actions.cpp @@ -19,12 +19,12 @@ TEST_CASE("action") { using x4::int_; - IRIS_X4_ASSERT_CONSTEXPR_CTORS(x4::int_[std::true_type{}]); + IRIS_X4_ASSERT_CONSTEXPR_CTORS(x4::int_.on_match(std::true_type{})); { int x = 0; auto const fun_action = [&](auto&& ctx) { x += x4::_attr(ctx); }; - CHECK(parse("{42}", '{' >> int_[fun_action] >> '}')); + CHECK(parse("{42}", '{' >> int_.on_match(fun_action) >> '}')); } { auto const fail = [](auto&&) { return false; }; @@ -35,7 +35,7 @@ TEST_CASE("action") next = x4::_attr(ctx); }; - REQUIRE(parse(input, x4::int_[fail] | x4::digit[setnext], x4::space).is_partial_match()); + REQUIRE(parse(input, x4::int_.on_match(fail) | x4::digit.on_match(setnext), x4::space).is_partial_match()); CHECK(next == '1'); } @@ -46,7 +46,7 @@ TEST_CASE("action") x4_test::stationary st { 0 }; static_assert(x4::X4Attribute); - REQUIRE(parse("{42}", p[([]{})], st)); + REQUIRE(parse("{42}", p.on_match([]{}), st)); CHECK(st.val == 42); } } diff --git a/test/x4/alloy_wrong_substitute_test_case.cpp b/test/x4/alloy_wrong_substitute.cpp similarity index 52% rename from test/x4/alloy_wrong_substitute_test_case.cpp rename to test/x4/alloy_wrong_substitute.cpp index d92e9dad3..4df41ed5e 100644 --- a/test/x4/alloy_wrong_substitute_test_case.cpp +++ b/test/x4/alloy_wrong_substitute.cpp @@ -1,6 +1,6 @@ #include "iris_x4_test.hpp" -#include +#include #include #include @@ -9,8 +9,6 @@ #include -namespace alloy_wrong_substitute_test_case { - struct A { int foo; @@ -23,47 +21,37 @@ struct B std::string fuga; }; -} // alloy_wrong_substitute_test_case - namespace iris::alloy { // If enabled, `B` can be wrongly treated as substitutable to `A` template<> -struct adaptor +struct adaptor { using getters_list = make_getters_list< - &alloy_wrong_substitute_test_case::A::foo, - &alloy_wrong_substitute_test_case::A::bar + &A::foo, + &A::bar >; }; template<> -struct adaptor +struct adaptor { using getters_list = make_getters_list< - &alloy_wrong_substitute_test_case::B::hoge, - &alloy_wrong_substitute_test_case::B::fuga + &B::hoge, + &B::fuga >; }; } // iris::alloy -namespace ast { - -using A = alloy_wrong_substitute_test_case::A; -using B = alloy_wrong_substitute_test_case::B; using AorB = iris::rvariant< - alloy_wrong_substitute_test_case::A, - alloy_wrong_substitute_test_case::B + A, + B >; -} // ast - -namespace alloy_wrong_substitute_test_case { - -using ARule = x4::rule; -using BRule = x4::rule; -using AorBRule = x4::rule; +using ARule = x4::rule; +using BRule = x4::rule; +using AorBRule = x4::rule; constexpr ARule a; constexpr BRule b; @@ -85,11 +73,9 @@ IRIS_X4_DEFINE(a_or_b); IRIS_X4_INSTANTIATE(AorBRule, const char*, x4::unused_type); -TEST_CASE("alloy_wrong_substitute_test_case") +TEST_CASE("alloy_wrong_substitute") { const char* ptr = nullptr; - ast::AorB result; + AorB result; (void)a_or_b.parse(ptr, nullptr, x4::unused, result); } - -} // alloy_wrong_substitute_test_case diff --git a/test/x4/alternative.cpp b/test/x4/alternative.cpp index 6c1ea50a5..3b758cfd4 100644 --- a/test/x4/alternative.cpp +++ b/test/x4/alternative.cpp @@ -11,18 +11,22 @@ #include "iris_x4_test.hpp" #include -#include -#include + +#include +#include + #include #include -#include #include #include + +#include + #include #include #include #include -#include +#include #include #include @@ -61,7 +65,7 @@ TEST_CASE("alternative") { using x4::standard::char_; using x4::standard::lit; - using x4::attr; + using x4::fixed_value; using x4::int_; using x4::unused; using x4::omit; @@ -163,8 +167,8 @@ TEST_CASE("alternative") constexpr auto f = [&](auto& ctx){ _rule_var(ctx) = _attr(ctx); }; - (void)(r3 = (eps >> r1)[f]); - (void)(r3 = (r1 | r2)[f]); + (void)(r3 = (eps >> r1).on_match(f)); + (void)(r3 = (r1 | r2).on_match(f)); (void)(r3 = eps >> r1 | r2); (void)r3; } @@ -268,8 +272,8 @@ TEST_CASE("alternative") // alternative over single element tuple as part of another tuple { - constexpr auto key1 = lit("long") >> attr(long()); - constexpr auto key2 = lit("char") >> attr(char()); + constexpr auto key1 = lit("long") >> fixed_value(long{}); + constexpr auto key2 = lit("char") >> fixed_value(char{}); constexpr auto keys = key1 | key2; constexpr auto pair = keys >> lit("=") >> +char_; @@ -305,7 +309,7 @@ TEST_CASE("alternative") // regressing test for #603 struct X {}; std::vector> v; - REQUIRE(parse("xx42x9y", *(int_ | +char_('x') | 'y' >> attr(X{})), v)); + REQUIRE(parse("xx42x9y", *(int_ | +char_('x') | 'y' >> fixed_value(X{})), v)); CHECK(v.size() == 5); } @@ -324,7 +328,7 @@ TEST_CASE("alternative") iris::rvariant v; iris::rvariant x{X{}}; v = x; // iris::rvariant supports that convertion - auto const p = 'x' >> attr(x) | 'z' >> attr(Z{}); + auto const p = 'x' >> fixed_value(x) | 'z' >> fixed_value(Z{}); REQUIRE(parse("z", p, v)); CHECK(iris::get_if(&v) != nullptr); REQUIRE(parse("x", p, v)); @@ -337,7 +341,7 @@ TEST_CASE("alternative") using Foo = std::vector>; using Bar = std::vector>; Bar x; - CHECK(parse("abaabb", +('a' >> attr(Foo{}) | 'b' >> attr(int{})), x)); + CHECK(parse("abaabb", +('a' >> fixed_value(Foo{}) | 'b' >> fixed_value(int{})), x)); } } diff --git a/test/x4/as.cpp b/test/x4/as.cpp index 9b0335c32..232dc962c 100644 --- a/test/x4/as.cpp +++ b/test/x4/as.cpp @@ -10,13 +10,15 @@ #include "iris_x4_test.hpp" -#include -#include -#include +#include +#include +#include + #include #include #include #include + #include #include #include @@ -43,7 +45,7 @@ using Se = It; using Context = unused_type; constexpr auto do_nothing = [](auto&&) {}; -constexpr auto disable_attr = eps[([](auto&&) {})]; +constexpr auto disable_attr = eps.on_match([](auto&&) {}); constexpr auto quoted_string = '\'' >> *~x4::char_('\'') >> '\''; char const* empty_input_first = nullptr; @@ -53,6 +55,7 @@ TEST_CASE("as(p)") { using x4::_as_var; using x4::_attr; + using x4::fixed_value; // result = int or long long // T = int @@ -61,11 +64,11 @@ TEST_CASE("as(p)") // with semantic action { { - constexpr auto p = x4::as(x4::attr(3))[([](auto&& ctx) { + constexpr auto p = x4::as(fixed_value(3)).on_match([](auto&& ctx) { static_assert(std::same_as, unused_type>); static_assert(std::same_as, int>); _attr(ctx) += 5; - })]; + }); { int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); @@ -80,13 +83,13 @@ TEST_CASE("as(p)") // do nothing in semantic action { - constexpr auto p = x4::attr(3); + constexpr auto p = fixed_value(3); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); CHECK(result == 3); } { - constexpr auto p = x4::attr(3)[do_nothing]; + constexpr auto p = fixed_value(3).on_match(do_nothing); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); CHECK(result == 3); @@ -94,7 +97,7 @@ TEST_CASE("as(p)") { constexpr auto p = x4::as( - x4::attr(3) + fixed_value(3) ); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); @@ -102,7 +105,7 @@ TEST_CASE("as(p)") } { constexpr auto p = x4::as( - x4::attr(3)[do_nothing] + fixed_value(3).on_match(do_nothing) ); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); @@ -113,16 +116,16 @@ TEST_CASE("as(p)") // the intermediate value always propagates up. { constexpr auto p = x4::as( - x4::attr(3) - )[do_nothing]; + fixed_value(3) + ).on_match(do_nothing); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); CHECK(result == 3); } { constexpr auto p = x4::as( - x4::attr(3)[do_nothing] - )[do_nothing]; + fixed_value(3).on_match(do_nothing) + ).on_match(do_nothing); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); CHECK(result == 42); @@ -132,7 +135,7 @@ TEST_CASE("as(p)") // ------------------------------------------- // without semantic action { - constexpr auto p = x4::as(x4::attr(3)); + constexpr auto p = x4::as(fixed_value(3)); int result = 42; REQUIRE(p.parse(empty_input_first, empty_input_last, unused, result)); CHECK(result == 3); @@ -151,6 +154,7 @@ TEST_CASE("as(as(p))") { using x4::_as_var; using x4::_attr; + using x4::fixed_value; // result = int or long long // T = int @@ -160,12 +164,12 @@ TEST_CASE("as(as(p))") // with semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(3))[([](auto&& ctx) { + x4::as(fixed_value(3)).on_match([](auto&& ctx) { static_assert(std::same_as, int>); static_assert(std::same_as, int>); CHECK(std::addressof(_as_var(ctx)) != std::addressof(_attr(ctx))); _as_var(ctx) = _attr(ctx) + 5; - })] + }) ); { int result = 42; @@ -182,7 +186,7 @@ TEST_CASE("as(as(p))") // do nothing in semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(3))[do_nothing] + x4::as(fixed_value(3)).on_match(do_nothing) ); { @@ -201,7 +205,7 @@ TEST_CASE("as(as(p))") // without semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(3)) + x4::as(fixed_value(3)) ); { @@ -221,6 +225,7 @@ TEST_CASE("as(as(p))") { using x4::_as_var; using x4::_attr; + using x4::fixed_value; // result = int or long long // T = int @@ -230,11 +235,11 @@ TEST_CASE("as(as(p))") // with semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(short(3)))[([](auto&& ctx) { + x4::as(fixed_value(short(3))).on_match([](auto&& ctx) { static_assert(std::same_as, int>); static_assert(std::same_as, short>); _as_var(ctx) = _attr(ctx) + 5; - })] + }) ); { int result = 42; @@ -251,7 +256,7 @@ TEST_CASE("as(as(p))") // do nothing in semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(short(3)))[do_nothing] + x4::as(fixed_value(short(3))).on_match(do_nothing) ); { @@ -270,7 +275,7 @@ TEST_CASE("as(as(p))") // without semantic action { constexpr auto p = x4::as( - x4::as(x4::attr(short(3))) + x4::as(fixed_value(short(3))) ); { @@ -290,11 +295,11 @@ TEST_CASE("as(as(p))") { constexpr auto p = x4::as( - x4::as(+x4::unicode::char_)[([](auto&& ctx) { + x4::as(+x4::unicode::char_).on_match([](auto&& ctx) { static_assert(std::same_as, std::string>); static_assert(std::same_as, std::u32string>); _as_var(ctx) = iris::unicode::transcode(_attr(ctx)); - })] + }) ); std::u32string_view input = U"テスト"; @@ -440,6 +445,7 @@ TEST_CASE("_as_var") using x4::_attr; using x4::_rule_var; using x4::_as_var; + using x4::fixed_value; // `_as_var(ctx)` (with auto attribute propagation) { @@ -447,13 +453,13 @@ TEST_CASE("_as_var") constexpr auto string_rule = x4::rule{""} = x4::as( - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { _rule_var(ctx) = "default"; - })] >> + }) >> - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { _as_var(ctx) = "foo"; - })] + }) ); std::string_view const input; @@ -469,13 +475,13 @@ TEST_CASE("_as_var") constexpr auto string_rule = x4::rule{""} = x4::as( - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { _rule_var(ctx) = "default"; - })] >> + }) >> - eps[([]([[maybe_unused]] auto&& ctx) { + eps.on_match([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); - })] + }) ) >> disable_attr; // <---------- std::string_view const input; @@ -491,9 +497,9 @@ TEST_CASE("_as_var") constexpr auto unused_rule = x4::as( x4::as( - eps[([]([[maybe_unused]] auto&& ctx) { + eps.on_match([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); - })] + }) ) ); @@ -509,16 +515,16 @@ TEST_CASE("_as_var") std::string result; /*constexpr*/ auto unused_rule = x4::as( - x4::attr("default") >> + fixed_value("default") >> - eps[([]([[maybe_unused]] auto&& ctx) { + eps.on_match([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, std::string>); - })] >> + }) >> x4::as( - eps[([]([[maybe_unused]] auto&& ctx) { + eps.on_match([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); - })] + }) ) ); @@ -542,15 +548,15 @@ TEST_CASE("_as_var") { constexpr auto string_literal = x4::rule{"StringLiteral"} = - eps[([](auto& ctx) { _rule_var(ctx).is_quoted = false; })] >> + eps.on_match([](auto& ctx) { _rule_var(ctx).is_quoted = false; }) >> x4::as( - x4::lit('"')[([](auto&& ctx) { + x4::lit('"').on_match([](auto&& ctx) { StringLiteral& rule_var = _rule_var(ctx); rule_var.is_quoted = true; - })] >> - *(~x4::char_('"'))[([](auto&& ctx) { _as_var(ctx).push_back(_attr(ctx)); })] >> + }) >> + *(~x4::char_('"')).on_match([](auto&& ctx) { _as_var(ctx).push_back(_attr(ctx)); }) >> '"' - )[([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); })]; + ).on_match([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); }); It first = input.begin(); Se const last = input.end(); @@ -562,15 +568,15 @@ TEST_CASE("_as_var") } { constexpr auto string_literal = x4::rule{"StringLiteral"} = - eps[([](auto& ctx) { _rule_var(ctx).is_quoted = false; })] >> + eps.on_match([](auto& ctx) { _rule_var(ctx).is_quoted = false; }) >> x4::as( - x4::lit('"')[([](auto&& ctx) { + x4::lit('"').on_match([](auto&& ctx) { StringLiteral& rule_var = _rule_var(ctx); rule_var.is_quoted = true; - })] >> + }) >> *~x4::char_('"') >> // <----------------- attribute ignored '"' - )[([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); })]; + ).on_match([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); }); It first = input.begin(); Se const last = input.end(); @@ -582,12 +588,12 @@ TEST_CASE("_as_var") } { constexpr auto string_literal = x4::rule{"StringLiteral"} = - eps[([](auto& ctx) { _rule_var(ctx).is_quoted = false; })] >> + eps.on_match([](auto& ctx) { _rule_var(ctx).is_quoted = false; }) >> x4::as( x4::lit('"') >> // <----------------- no semantic action *~x4::char_('"') >> // <----------------- attribute NOT ignored '"' - )[([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); })]; + ).on_match([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); }); It first = input.begin(); Se const last = input.end(); diff --git a/test/x4/attribute_type_check.cpp b/test/x4/attribute_type_check.cpp index d70d218e6..1c05f3ace 100644 --- a/test/x4/attribute_type_check.cpp +++ b/test/x4/attribute_type_check.cpp @@ -9,8 +9,8 @@ #include "iris_x4_test.hpp" -#include -#include +#include +#include #include #include @@ -25,12 +25,12 @@ namespace { // just an `attr` with added type checker template -struct checked_attr_parser : x4::attr_parser +struct checked_fixed_value_parser : x4::fixed_value_parser { - using base_type = x4::attr_parser; + using base_type = x4::fixed_value_parser; - checked_attr_parser(Value const& value) : base_type(value) {} - checked_attr_parser(Value&& value) : base_type(std::move(value)) {} + checked_fixed_value_parser(Value const& value) : base_type(value) {} + checked_fixed_value_parser(Value&& value) : base_type(std::move(value)) {} template Se, class Context, class Attr> [[nodiscard]] constexpr bool @@ -42,7 +42,7 @@ struct checked_attr_parser : x4::attr_parser }; template -checked_attr_parser, Expected> +checked_fixed_value_parser, Expected> checked_attr(Value&& value) { return { std::forward(value) }; } // instantiate our type checker diff --git a/test/x4/char_class.cpp b/test/x4/char_class.cpp index 4f1269078..775268879 100644 --- a/test/x4/char_class.cpp +++ b/test/x4/char_class.cpp @@ -199,9 +199,9 @@ TEST_CASE("char_class") char ch = '\0'; auto f = [&](auto&& ctx){ ch = _attr(ctx); }; - REQUIRE(parse("x", alnum[f])); + REQUIRE(parse("x", alnum.on_match(f))); CHECK(ch == 'x'); - REQUIRE(parse(" A", alnum[f], space)); + REQUIRE(parse(" A", alnum.on_match(f), space)); CHECK(ch == 'A'); } } diff --git a/test/x4/container_support.cpp b/test/x4/container_support.cpp index e7efada1a..9a1df2063 100644 --- a/test/x4/container_support.cpp +++ b/test/x4/container_support.cpp @@ -11,12 +11,12 @@ #include "iris_x4_test.hpp" #include +#include #include #include -#include #include #include -#include +#include #include #include diff --git a/test/x4/list.cpp b/test/x4/delimited_list.cpp similarity index 96% rename from test/x4/list.cpp rename to test/x4/delimited_list.cpp index a19c20fb1..75b3c3c14 100644 --- a/test/x4/list.cpp +++ b/test/x4/delimited_list.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -114,7 +114,7 @@ TEST_CASE("list") std::string s; auto f = [&](auto&& ctx){ s = std::string(_attr(ctx).begin(), _attr(ctx).end()); }; - REQUIRE(parse("a,b,c,d,e,f,g,h", (char_ % ',')[f])); + REQUIRE(parse("a,b,c,d,e,f,g,h", (char_ % ',').on_match(f))); CHECK(s == "abcdefgh"); } diff --git a/test/x4/difference.cpp b/test/x4/difference.cpp index d561f60d5..c4dd601f1 100644 --- a/test/x4/difference.cpp +++ b/test/x4/difference.cpp @@ -57,7 +57,7 @@ TEST_CASE("difference") std::string s; REQUIRE(parse( "/*abcdefghijk*/", - "/*" >> *(char_ - "*/")[([&](auto&& ctx){ s += _attr(ctx); })] >> "*/" + "/*" >> *(char_ - "*/").on_match([&](auto&& ctx){ s += _attr(ctx); }) >> "*/" )); CHECK(s == "abcdefghijk"); } @@ -65,7 +65,7 @@ TEST_CASE("difference") std::string s; REQUIRE(parse( " /*abcdefghijk*/", - "/*" >> *(char_ - "*/")[([&](auto&& ctx){ s += _attr(ctx); })] >> "*/", + "/*" >> *(char_ - "*/").on_match([&](auto&& ctx){ s += _attr(ctx); }) >> "*/", space )); CHECK(s == "abcdefghijk"); diff --git a/test/x4/eoi.cpp b/test/x4/eoi.cpp index 9ac6880d8..8d8490b02 100644 --- a/test/x4/eoi.cpp +++ b/test/x4/eoi.cpp @@ -9,7 +9,7 @@ #include "iris_x4_test.hpp" -#include +#include TEST_CASE("eoi") { diff --git a/test/x4/eol.cpp b/test/x4/eol.cpp index 792c6cd0a..240fe08e9 100644 --- a/test/x4/eol.cpp +++ b/test/x4/eol.cpp @@ -9,7 +9,7 @@ #include "iris_x4_test.hpp" -#include +#include TEST_CASE("eol") { diff --git a/test/x4/eps.cpp b/test/x4/eps.cpp index 6d05a0c3f..12863c6f4 100644 --- a/test/x4/eps.cpp +++ b/test/x4/eps.cpp @@ -9,7 +9,7 @@ #include "iris_x4_test.hpp" -#include +#include #include TEST_CASE("eps") diff --git a/test/x4/error_handler.cpp b/test/x4/error_handler.cpp index 1403c14df..7fd764045 100644 --- a/test/x4/error_handler.cpp +++ b/test/x4/error_handler.cpp @@ -10,11 +10,13 @@ #include "iris_x4_test.hpp" #include -#include + +#include #include +#include + #include #include -#include #include #include diff --git a/test/x4/expect.cpp b/test/x4/expect.cpp index 5da40afd1..6f13d3805 100644 --- a/test/x4/expect.cpp +++ b/test/x4/expect.cpp @@ -15,18 +15,24 @@ #include "iris_x4_test.hpp" +#include + +#include + +#include +#include +#include +#include + #include +#include #include #include #include -#include -#include -#include -#include -#include -#include + +#include + #include -#include #include #include #include @@ -34,13 +40,12 @@ #include #include #include -#include #include -#include + #include #include #include -#include +#include #include #include #include @@ -139,7 +144,7 @@ TEST_CASE("expectation_failure_context_uninstantiated_in_expect_less_parse") using x4::eoi; using x4::eol; using x4::eps; - using x4::attr; + using x4::fixed_value; using x4::lit; using x4::string; using x4::char_; @@ -152,7 +157,6 @@ TEST_CASE("expectation_failure_context_uninstantiated_in_expect_less_parse") using x4::no_skip; using x4::omit; using x4::repeat; - using x4::seek; using x4::skip; using x4::with; @@ -169,11 +173,12 @@ TEST_CASE("expectation_failure_context_uninstantiated_in_expect_less_parse") bool dummy_bool = false; - (void)eps[([]{})].parse(first, last, unused, unused); // action - (void)int_[([]{})].parse(first, last, unused, dummy_int); // action - (void)(int_ >> int_)[([]{})].parse(first, last, unused, dummy_ints); // action + (void)eps.on_match([]{}).parse(first, last, unused, unused); // action + (void)int_.on_match([]{}).parse(first, last, unused, dummy_int); // action + (void)(int_ >> int_).on_match([]{}).parse(first, last, unused, dummy_ints); // action - (void)attr(42).parse(first, last, unused, unused); + (void)fixed_value(42).parse(first, last, unused, unused); + (void)fixed_value("foo").parse(first, last, unused, unused); (void)eoi.parse(first, last, unused, unused); (void)eol.parse(first, last, unused, unused); (void)eps.parse(first, last, unused, unused); @@ -215,7 +220,6 @@ TEST_CASE("expectation_failure_context_uninstantiated_in_expect_less_parse") (void)omit[eps].parse(first, last, unused, unused); (void)repeat(1)[eps].parse(first, last, unused, unused); - (void)seek[eps].parse(first, last, unused, unused); (void)skip(space)[eps].parse(first, last, unused, unused); (void)with(input)[eps].parse(first, last, unused, unused); @@ -290,7 +294,6 @@ TEST_CASE("expect") using x4::no_skip; using x4::omit; using x4::skip; - using x4::seek; using x4::repeat; using x4::matches; using x4::eps; @@ -478,7 +481,7 @@ TEST_CASE("expect") }); } - // auxilary parsers + // primitive parsers { X4_TEST_SUCCESS_PASS("a12", lit('a') > eps > +digit); X4_TEST_SUCCESS_PASS("a12", lit('a') > +digit > eoi); @@ -495,7 +498,7 @@ TEST_CASE("expect") }); int n = 0; - X4_TEST_ATTR_SUCCESS_PASS("abc", lit("abc") > x4::attr(12) > eoi, n); + X4_TEST_ATTR_SUCCESS_PASS("abc", lit("abc") > x4::fixed_value(12) > eoi, n); CHECK(n == 12); } @@ -653,15 +656,6 @@ TEST_CASE("expect") X4_TEST_SUCCESS_PASS("bcab", repeat(2, 3)[lit('a') > 'b'] | +alpha); } - // seek - { - X4_TEST_SUCCESS_PASS("a1b1c1", seek[lit('c') > '1']); - X4_TEST_FAILURE("a1b1c2c1", seek[lit('c') > '1'], { - CHECK(which == "'1'"sv); - CHECK(where == "2c1"sv); - }); - } - // alternative { X4_TEST_SUCCESS_PASS("ac", lit('a') >> 'b' | "ac"); @@ -706,7 +700,7 @@ TEST_CASE("expect") }); } - // list + // delimited_list { X4_TEST_SUCCESS_PASS("ab::ab::ac", (lit('a') >> 'b') % (lit(':') >> ':') >> "::ac"); X4_TEST_SUCCESS_PASS("ab::ab:ac", (lit('a') > 'b') % (lit(':') >> ':') >> ":ac"); diff --git a/test/x4/int.cpp b/test/x4/int.cpp index 7a1cc7394..369f45efb 100644 --- a/test/x4/int.cpp +++ b/test/x4/int.cpp @@ -213,12 +213,12 @@ TEST_CASE("int") auto f = [&](auto&& ctx){ n = _attr(ctx); }; - REQUIRE(parse("123", int_[f])); + REQUIRE(parse("123", int_.on_match(f))); CHECK(n == 123); - REQUIRE(parse("789", int_[f], m)); + REQUIRE(parse("789", int_.on_match(f), m)); REQUIRE(n == 789); CHECK(m == 789); - REQUIRE(parse(" 456", int_[f], space)); + REQUIRE(parse(" 456", int_.on_match(f), space)); CHECK(n == 456); } diff --git a/test/x4/iterator.cpp b/test/x4/iterator.cpp index 14f9dc8d6..f346acdbf 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -12,18 +12,20 @@ #include "iris_x4_test.hpp" +#include + +#include +#include +#include +#include + #include +#include #include #include #include -#include -#include -#include -#include -#include -#include + #include -#include #include #include #include @@ -31,22 +33,25 @@ #include #include #include -#include #include + #include #include #include #include + #include #include #include -#include +#include #include #include #include #include #include +#include + #include #include #include @@ -213,14 +218,14 @@ TEST_CASE("rollback on failed parse (action)") { constexpr auto input = "foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eps[([](auto&&) { return false; })].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eps.on_match([](auto&&) { return false; }).parse(first, input.end(), unused, unused)); CHECK(first == input.begin()); } { constexpr auto input = "42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(int_[([](auto&&) { return false; })].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(int_.on_match([](auto&&) { return false; }).parse(first, input.end(), unused, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser itself succeeds; always results in side effect } @@ -228,15 +233,15 @@ TEST_CASE("rollback on failed parse (action)") constexpr auto input = "42,43"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE((int_ >> ',' >> int_)[([](auto&&) { return false; })].parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE((int_ >> ',' >> int_).on_match([](auto&&) { return false; }).parse(first, input.end(), unused, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{42, 43}); // sequence parser itself succeeds; always results in side effect } } -TEST_CASE("rollback on failed parse (auxiliary)") +TEST_CASE("rollback on failed parse (primitive)") { - using x4::attr; + using x4::fixed_value; using x4::eps; using x4::eoi; using x4::eol; @@ -245,7 +250,7 @@ TEST_CASE("rollback on failed parse (auxiliary)") constexpr auto input = "foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((attr(42) >> eps(false)).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((fixed_value(42) >> eps(false)).parse(first, input.end(), unused, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } @@ -290,7 +295,6 @@ TEST_CASE("rollback on failed parse (directive)") using x4::no_skip; using x4::omit; using x4::repeat; - using x4::seek; using x4::skip; using x4::with; @@ -494,29 +498,6 @@ TEST_CASE("rollback on failed parse (directive)") CHECK(dummy_bools == std::vector{}); } - { - constexpr auto input = "foo"sv; - auto first = input.begin(); - REQUIRE_FALSE(seek[eps(false)].parse(first, input.end(), unused, unused)); - CHECK(first == input.begin()); - } - { - constexpr auto input = "foo"sv; - auto first = input.begin(); - int dummy_int = -1; - REQUIRE_FALSE(seek[int_].parse(first, input.end(), unused, dummy_int)); - CHECK(first == input.begin()); - CHECK(dummy_int == -1); - } - { - constexpr auto input = "42"sv; - auto first = input.begin(); - int dummy_int = -1; - REQUIRE_FALSE(seek[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); - CHECK(first == input.begin()); - CHECK(dummy_int == 2); // `seek` has side effect - } - { constexpr auto input = "foo"sv; auto first = input.begin(); @@ -757,7 +738,7 @@ TEST_CASE("rollback on failed parse (operator)") std::vector dummy_bools; REQUIRE_FALSE((true_ % eps(false) >> eps(false)).parse(first, input.end(), unused, dummy_bools)); CHECK(first == input.begin()); - CHECK(dummy_bools == std::vector{true});// `list` parser (within sequence) exposes the side effects + CHECK(dummy_bools == std::vector{true});// `delimited_list` parser (within sequence) exposes the side effects } { diff --git a/test/x4/kleene.cpp b/test/x4/kleene.cpp index 693868dad..a86e37f0b 100644 --- a/test/x4/kleene.cpp +++ b/test/x4/kleene.cpp @@ -87,7 +87,7 @@ TEST_CASE("kleene") std::string v; auto f = [&](auto&& ctx){ v = _attr(ctx); }; - REQUIRE(parse("bbbb", (*char_)[f])); + REQUIRE(parse("bbbb", (*char_).on_match(f))); REQUIRE(v.size() == 4); CHECK(v[0] == 'b'); CHECK(v[1] == 'b'); @@ -102,7 +102,7 @@ TEST_CASE("kleene") std::vector v; auto f = [&](auto&& ctx){ v = _attr(ctx); }; - REQUIRE(parse("123 456 789", (*int_)[f], space)); + REQUIRE(parse("123 456 789", (*int_).on_match(f), space)); CHECK(v.size() == 3); CHECK(v[0] == 123); CHECK(v[1] == 456); diff --git a/test/x4/omit.cpp b/test/x4/omit.cpp index bc24f4a18..822ebbd40 100644 --- a/test/x4/omit.cpp +++ b/test/x4/omit.cpp @@ -111,7 +111,7 @@ TEST_CASE("omit") char c = 0; auto f = [&](auto&& ctx){ c = _attr(ctx); }; - REQUIRE(parse("x123\"a string\"", (char_ >> omit[int_] >> "\"a string\"")[f])); + REQUIRE(parse("x123\"a string\"", (char_ >> omit[int_] >> "\"a string\"").on_match(f))); CHECK(c == 'x'); } @@ -120,7 +120,7 @@ TEST_CASE("omit") int n = 0; auto f = [&](auto&& ctx){ n = _attr(ctx); }; - REQUIRE(parse("x 123 \"a string\"", (omit[char_] >> int_ >> "\"a string\"")[f], space)); + REQUIRE(parse("x 123 \"a string\"", (omit[char_] >> int_ >> "\"a string\"").on_match(f), space)); CHECK(n == 123); } diff --git a/test/x4/optional.cpp b/test/x4/optional.cpp index d45152329..93c89346f 100644 --- a/test/x4/optional.cpp +++ b/test/x4/optional.cpp @@ -148,7 +148,7 @@ TEST_CASE("optional") { // test action std::optional n = 0; - REQUIRE(parse("1234", (-int_)[test_attribute_type()], n)); + REQUIRE(parse("1234", (-int_).on_match(test_attribute_type()), n)); CHECK(*n == 1234); } @@ -161,13 +161,13 @@ TEST_CASE("optional") { std::optional n; auto f = [&](auto&& ctx) { n = _attr(ctx); }; - CHECK(parse("abcd", (-int_)[f]).is_partial_match()); + CHECK(parse("abcd", (-int_).on_match(f)).is_partial_match()); CHECK(!n.has_value()); } { std::optional n = 0; auto f = [&](auto&& ctx){ n = _attr(ctx); }; - REQUIRE(parse("1234", (-int_)[f])); + REQUIRE(parse("1234", (-int_).on_match(f))); CHECK(*n == 1234); } diff --git a/test/x4/partial_success.cpp b/test/x4/partial_success.cpp index 9219b6284..c04fca916 100644 --- a/test/x4/partial_success.cpp +++ b/test/x4/partial_success.cpp @@ -8,12 +8,12 @@ #include "iris_x4_test.hpp" -#include -#include +#include #include #include #include #include +#include #include #include #include @@ -23,7 +23,7 @@ // list-like #include #include -#include +#include #include #include @@ -81,7 +81,7 @@ struct strong_int TEST_CASE("partial success (alternative)") { - using x4::attr; + using x4::fixed_value; using x4::eps; using x4::omit; using x4::int_; @@ -94,12 +94,12 @@ TEST_CASE("partial success (alternative)") // Sanity checks { int i = -1; - REQUIRE(parse("", attr(42), i)); + REQUIRE(parse("", fixed_value(42), i)); CHECK(i == 42); } { std::string str; - REQUIRE(parse("", attr("foo"), str)); + REQUIRE(parse("", fixed_value("foo"), str)); CHECK(str == "foo"); } { @@ -117,12 +117,12 @@ TEST_CASE("partial success (alternative)") { std::vector ints; - REQUIRE(parse("1 2", eps(false) | attr(98) >> attr(99), ints).is_partial_match()); + REQUIRE(parse("1 2", eps(false) | fixed_value(98) >> fixed_value(99), ints).is_partial_match()); CHECK(ints == std::vector{98, 99}); } { std::vector ints; - REQUIRE(parse("1 2", int_ >> int_ >> eps(false) | attr(98) >> attr(99), space, ints).is_partial_match()); + REQUIRE(parse("1 2", int_ >> int_ >> eps(false) | fixed_value(98) >> fixed_value(99), space, ints).is_partial_match()); // If we don't properly "hold" the value on the failed branch of // `x4::alternative`, we would see {1, 2, 98, 99} here. CHECK(ints == std::vector{98, 99}); @@ -130,13 +130,13 @@ TEST_CASE("partial success (alternative)") // Failed parse should not modify the exposed attribute { std::vector ints; - REQUIRE(!parse("1 2", int_ >> int_ >> eps(false) | attr(98) >> attr(99) >> eps(false), space, ints)); + REQUIRE(!parse("1 2", int_ >> int_ >> eps(false) | fixed_value(98) >> fixed_value(99) >> eps(false), space, ints)); // Wrong implementation yields {1, 2, 98, 99} or {98, 99} CHECK(ints == std::vector{}); } { std::vector ints; - REQUIRE(parse("1 2", attr(std::vector{3, 4}) >> eps(false) | attr(98) >> attr(99), space, ints).is_partial_match()); + REQUIRE(parse("1 2", fixed_value(std::vector{3, 4}) >> eps(false) | fixed_value(98) >> fixed_value(99), space, ints).is_partial_match()); // Wrong implementation yields {3, 4, 98, 99} CHECK(ints == std::vector{98, 99}); } @@ -173,14 +173,14 @@ TEST_CASE("partial success (alternative)") } { std::string str; - REQUIRE(parse("foodie", attr("bookworm") >> eps(false) | string("foodie"), str)); + REQUIRE(parse("foodie", fixed_value("bookworm") >> eps(false) | string("foodie"), str)); // Wrong implementation yields "bookwormfoodie" CHECK(str == "foodie"); } // Failed parse should not modify the exposed attribute { std::string str; - REQUIRE(!parse("foodie", attr("bookworm") >> eps(false) | string("foodie") >> eps(false), str)); + REQUIRE(!parse("foodie", fixed_value("bookworm") >> eps(false) | string("foodie") >> eps(false), str)); // Wrong implementation yields "bookwormfoodie" or "foodie" CHECK(str == ""); } @@ -201,7 +201,7 @@ TEST_CASE("partial success (alternative)") { strong_int si; - REQUIRE(parse("1", int_ | attr(strong_int{9}), si)); + REQUIRE(parse("1", int_ | fixed_value(strong_int{9}), si)); CHECK(si == strong_int{1}); CHECK(si.assigned_count == 1); } @@ -224,13 +224,13 @@ TEST_CASE("partial success (alternative)") { pair_int pi; - REQUIRE(parse("1 2", int_ >> int_ | attr(pair_int{98, 99}), space, pi)); + REQUIRE(parse("1 2", int_ >> int_ | fixed_value(pair_int{98, 99}), space, pi)); CHECK(pi == pair_int{1, 2}); } { pair_int pi; REQUIRE(parse("1 2", - int_ >> int_ >> eps(false) | attr(pair_int{98, 99}) >> omit[int_ >> int_], + int_ >> int_ >> eps(false) | fixed_value(pair_int{98, 99}) >> omit[int_ >> int_], space, pi )); CHECK(pi == pair_int{98, 99}); @@ -273,7 +273,7 @@ TEST_CASE("partial success (list-like)") STATIC_CHECK(x4::parser_traits::template handles_container); STATIC_CHECK(x4::parser_traits>::template handles_container); STATIC_CHECK(x4::parser_traits>::template handles_container); - STATIC_CHECK(x4::parser_traits>>::template handles_container); + STATIC_CHECK(x4::parser_traits>>::template handles_container); } // kleene @@ -300,7 +300,7 @@ TEST_CASE("partial success (list-like)") CHECK(abcs == "abcabc"sv); // wrong implementation yields "abcabcab" } - // list + // delimited_list { std::string abcs; REQUIRE(parse("abc,abx", abc % ',' >> ",abx", abcs)); @@ -367,7 +367,7 @@ TEST_CASE("partial success (list-like)") STATIC_CHECK(x4::parser_traits::template handles_container); STATIC_CHECK(x4::parser_traits>::template handles_container); STATIC_CHECK(x4::parser_traits>::template handles_container); - STATIC_CHECK(x4::parser_traits>>::template handles_container); + STATIC_CHECK(x4::parser_traits>>::template handles_container); } // kleene @@ -394,7 +394,7 @@ TEST_CASE("partial success (list-like)") CHECK(aOOcs == "aOOcaOOc"sv); // wrong implementation yields "aOOcaOOcab" } - // list + // delimited_list { std::string aOOcs; REQUIRE(parse("aOOc,aOOx", aOOc % ',' >> ",aOOx", aOOcs)); diff --git a/test/x4/plus.cpp b/test/x4/plus.cpp index 55ece085f..36019941e 100644 --- a/test/x4/plus.cpp +++ b/test/x4/plus.cpp @@ -87,7 +87,7 @@ TEST_CASE("plus") std::string v; auto f = [&](auto&& ctx){ v = _attr(ctx); }; - REQUIRE(parse("bbb", (+char_)[f])); + REQUIRE(parse("bbb", (+char_).on_match(f))); REQUIRE(v.size() == 3); CHECK(v[0] == 'b'); CHECK(v[1] == 'b'); @@ -99,7 +99,7 @@ TEST_CASE("plus") std::vector v; auto f = [&](auto&& ctx){ v = _attr(ctx); }; - REQUIRE(parse("1 2 3", (+int_)[f], space)); + REQUIRE(parse("1 2 3", (+int_).on_match(f), space)); REQUIRE(v.size() == 3); CHECK(v[0] == 1); CHECK(v[1] == 2); diff --git a/test/x4/recursive.cpp b/test/x4/recursive.cpp index 49b05d123..fae38e9ac 100644 --- a/test/x4/recursive.cpp +++ b/test/x4/recursive.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include diff --git a/test/x4/rule2.cpp b/test/x4/rule2.cpp index d38aff9e6..dea2d53af 100644 --- a/test/x4/rule2.cpp +++ b/test/x4/rule2.cpp @@ -37,21 +37,21 @@ TEST_CASE("rule2") char ch{}; // This semantic action requires the context auto f = [&](auto&& ctx){ ch = _attr(ctx); }; - REQUIRE(parse("x", a[f])); + REQUIRE(parse("x", a.on_match(f))); CHECK(ch == 'x'); } { char ch{}; // This semantic action requires the (unused) context auto f2 = [&](auto&&){ ch = 'y'; }; - REQUIRE(parse("x", a[f2])); + REQUIRE(parse("x", a.on_match(f2))); CHECK(ch == 'y'); } { char ch{}; // The semantic action may optionally not have any arguments at all auto f3 = [&]{ ch = 'z'; }; - REQUIRE(parse("x", a[f3])); + REQUIRE(parse("x", a.on_match(f3))); CHECK(ch == 'z'); } @@ -70,7 +70,7 @@ TEST_CASE("rule2") { char ch{}; auto f = [&](auto&& ctx){ ch = _attr(ctx); }; - REQUIRE(parse("x", a[f])); + REQUIRE(parse("x", a.on_match(f))); CHECK(ch == 'x'); } { @@ -81,7 +81,7 @@ TEST_CASE("rule2") { char ch{}; auto f = [&](auto&& ctx) { ch = _attr(ctx); }; - REQUIRE(parse("x", a[f])); + REQUIRE(parse("x", a.on_match(f))); CHECK(ch == 'x'); } { @@ -101,7 +101,7 @@ TEST_CASE("rule2") std::string s; auto f = [&](auto&& ctx) { s = _attr(ctx); }; - REQUIRE(parse("a,b,c,d,e,f", r[f])); + REQUIRE(parse("a,b,c,d,e,f", r.on_match(f))); CHECK(s == "abcdef"); } { @@ -109,7 +109,7 @@ TEST_CASE("rule2") std::string s; auto f = [&](auto&& ctx) { s = _attr(ctx); }; - REQUIRE(parse("a,b,c,d,e,f", r[f])); + REQUIRE(parse("a,b,c,d,e,f", r.on_match(f))); CHECK(s == "abcdef"); } { @@ -117,7 +117,7 @@ TEST_CASE("rule2") std::string s; auto f = [&](auto&& ctx) { s = _attr(ctx); }; - REQUIRE(parse("abcdef", r[f])); + REQUIRE(parse("abcdef", r.on_match(f))); CHECK(s == "abcdef"); } } diff --git a/test/x4/rule3.cpp b/test/x4/rule3.cpp index 128b2d2ea..11f658cf6 100644 --- a/test/x4/rule3.cpp +++ b/test/x4/rule3.cpp @@ -10,11 +10,11 @@ #include "iris_x4_test.hpp" #include -#include #include #include #include -#include +#include +#include #include #include #include @@ -117,9 +117,9 @@ TEST_CASE("rule3") std::string s; using rule_type = rule; - auto rdef = rule_type{} = alpha[([](auto&& ctx) { + auto rdef = rule_type{} = alpha.on_match([](auto&& ctx) { x4::_rule_var(ctx) += x4::_attr(ctx); - })]; + }); REQUIRE(parse("abcdef", +rdef, s)); CHECK(s == "abcdef"); @@ -131,21 +131,21 @@ TEST_CASE("rule3") using rule_type = rule; auto rdef = rule_type() = - alpha[([](auto&& ctx) { + alpha.on_match([](auto&& ctx) { _rule_var(ctx) += _attr(ctx); - })]; + }); REQUIRE(parse("abcdef", +rdef, s)); CHECK(s == "abcdef"); } { - auto r = rule{} = eps[([] ([[maybe_unused]] auto&& ctx) { + auto r = rule{} = eps.on_match([] ([[maybe_unused]] auto&& ctx) { static_assert( std::is_same_v, unused_type>, "Attr must not be synthesized" ); - })]; + }); CHECK(parse("", r)); } diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 4824331fa..018c60317 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -105,8 +105,8 @@ TEST_CASE("rule4") rule rb; (void)rb; auto f = [](auto&&) {}; - auto ra_def = ra %= int_[f]; - auto ra_def2 = (rb = (ra %= int_[f])); + auto ra_def = ra %= int_.on_match(f); + auto ra_def2 = (rb = (ra %= int_.on_match(f))); { int attr = 0; diff --git a/test/x4/rule_separate_tu.cpp b/test/x4/rule_separate_tu.cpp index 8f849d768..df713c4a5 100644 --- a/test/x4/rule_separate_tu.cpp +++ b/test/x4/rule_separate_tu.cpp @@ -20,19 +20,19 @@ namespace sem_act { constexpr auto nop = [](auto const&) {}; constexpr x4::rule used_attr1 = "used_attr1"; -constexpr auto used_attr1_def = used_attr::grammar[nop]; +constexpr auto used_attr1_def = used_attr::grammar.on_match(nop); IRIS_X4_DEFINE(used_attr1); constexpr x4::rule used_attr2 = "used_attr2"; -constexpr auto used_attr2_def = unused_attr::grammar[nop]; +constexpr auto used_attr2_def = unused_attr::grammar.on_match(nop); IRIS_X4_DEFINE(used_attr2); constexpr x4::rule unused_attr1 = "unused_attr1"; -constexpr auto unused_attr1_def = used_attr::grammar[nop]; +constexpr auto unused_attr1_def = used_attr::grammar.on_match(nop); IRIS_X4_DEFINE(unused_attr1); constexpr x4::rule unused_attr2 = "unused_attr2"; -constexpr auto unused_attr2_def = unused_attr::grammar[nop]; +constexpr auto unused_attr2_def = unused_attr::grammar.on_match(nop); IRIS_X4_DEFINE(unused_attr2); } // sem_act diff --git a/test/x4/seek.cpp b/test/x4/seek.cpp deleted file mode 100644 index 561b0e0f9..000000000 --- a/test/x4/seek.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*============================================================================= - Copyright (c) 2011 Jamboree - Copyright (c) 2014 Lee Clagett - Copyright (c) 2025 Nana Sakisaka - Copyright (c) 2026 The Iris Project Contributors - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -#include "iris_x4_test.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -TEST_CASE("seek") -{ - IRIS_X4_ASSERT_CONSTEXPR_CTORS(x4::seek['x']); - - // test eoi - CHECK(parse("", x4::seek[x4::eoi])); - CHECK(parse(" ", x4::seek[x4::eoi], x4::space)); - CHECK(parse("a", x4::seek[x4::eoi])); - CHECK(parse(" a", x4::seek[x4::eoi], x4::space)); - - // test literal finding - { - int i = 0; - REQUIRE(parse("!@#$%^&*KEY:123", x4::seek["KEY:"] >> x4::int_, i)); - CHECK(i == 123); - } - // test sequence finding - { - int i = 0; - REQUIRE(parse("!@#$%^&* KEY : 123", x4::seek[x4::lit("KEY") >> ':'] >> x4::int_, x4::space, i)); - CHECK(i == 123); - } - - // test attr finding - { - std::vector v; - REQUIRE(parse("a06b78c3d", +x4::seek[x4::int_], v).is_partial_match()); - REQUIRE(v.size() == 3); - CHECK(v[0] == 6); - CHECK(v[1] == 78); - CHECK(v[2] == 3); - } - - // test action - { - - bool b = false; - auto const action = [&b] { b = true; }; - REQUIRE(parse("abcdefg", x4::seek["def"][action]).is_partial_match()); - CHECK(b == true); - } - - // test container - { - std::vector v; - REQUIRE(parse("abcInt:100Int:95Int:44", x4::seek[+("Int:" >> x4::int_)], v)); - REQUIRE(v.size() == 3); - CHECK(v[0] == 100); - CHECK(v[1] == 95); - CHECK(v[2] == 44); - } - - // test failure rollback - CHECK(!parse("abcdefg", x4::seek[x4::int_])); - - // past the end regression GH#658 - CHECK(!parse(" ", x4::seek['x'], x4::space)); -} diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index b92d6cb0a..55ac84c6c 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -10,17 +10,20 @@ #include "iris_x4_test.hpp" #include -#include -#include + +#include +#include +#include #include #include #include -#include -#include -#include #include #include #include + +#include +#include + #include #include #include @@ -44,7 +47,7 @@ TEST_CASE("sequence") using x4::standard::string; using x4::standard::lit; using x4::standard::alnum; - using x4::attr; + using x4::fixed_value; using x4::omit; using x4::unused; using x4::int_; @@ -460,7 +463,7 @@ TEST_CASE("sequence") // Test that sequence with only one parser producing attribute makes it unwrapped STATIC_CHECK(std::same_as< - x4::parser_traits> attr(long()))>::attribute_type, + x4::parser_traits> fixed_value(long{}))>::attribute_type, long >); @@ -474,7 +477,7 @@ TEST_CASE("sequence") n = alloy::get<1>(_attr(ctx)); }; - REQUIRE(parse("x123\"a string\"", (char_ >> int_ >> "\"a string\"")[f])); + REQUIRE(parse("x123\"a string\"", (char_ >> int_ >> "\"a string\"").on_match(f))); CHECK(c == 'x'); CHECK(n == 123); } @@ -488,7 +491,7 @@ TEST_CASE("sequence") n = alloy::get<1>(_attr(ctx)); }; - REQUIRE(parse("x 123 \"a string\"", (char_ >> int_ >> "\"a string\"")[f], space)); + REQUIRE(parse("x 123 \"a string\"", (char_ >> int_ >> "\"a string\"").on_match(f), space)); CHECK(c == 'x'); CHECK(n == 123); } diff --git a/test/x4/smart_ptr.cpp b/test/x4/smart_ptr.cpp index 069b9ade7..b6224ba4e 100644 --- a/test/x4/smart_ptr.cpp +++ b/test/x4/smart_ptr.cpp @@ -8,7 +8,7 @@ #include "iris_x4_test.hpp" #include -#include +#include #include #include diff --git a/test/x4/symbols1.cpp b/test/x4/symbols1.cpp index d2bd863c4..4fe0f11fb 100644 --- a/test/x4/symbols1.cpp +++ b/test/x4/symbols1.cpp @@ -159,21 +159,21 @@ TEST_CASE("symbols1") int i = 0; auto f = [&](auto&& ctx){ i = _attr(ctx); }; // lambda with capture (important for subsequent checks) - using Parser = std::remove_cvref_t; + using Parser = std::remove_cvref_t; STATIC_CHECK(x4::X4ExplicitSubject); - CHECK(parse("Joel", sym[f])); + CHECK(parse("Joel", sym.on_match(f))); CHECK(i == 1); - CHECK(parse("Ruby", sym[f])); + CHECK(parse("Ruby", sym.on_match(f))); CHECK(i == 2); - CHECK(parse("Tenji", sym[f])); + CHECK(parse("Tenji", sym.on_match(f))); CHECK(i == 3); - CHECK(parse("Tutit", sym[f])); + CHECK(parse("Tutit", sym.on_match(f))); CHECK(i == 4); - CHECK(parse("Kim", sym[f])); + CHECK(parse("Kim", sym.on_match(f))); CHECK(i == 5); - CHECK(parse("Joey", sym[f])); + CHECK(parse("Joey", sym.on_match(f))); CHECK(i == 6); - CHECK(!parse("XXX", sym[f])); + CHECK(!parse("XXX", sym.on_match(f))); } } diff --git a/test/x4/uint.cpp b/test/x4/uint.cpp index d255fad24..729ce1cac 100644 --- a/test/x4/uint.cpp +++ b/test/x4/uint.cpp @@ -204,9 +204,9 @@ TEST_CASE("uint") int n = 0; auto f = [&](auto&& ctx){ n = _attr(ctx); }; - REQUIRE(parse("123", uint_[f])); + REQUIRE(parse("123", uint_.on_match(f))); CHECK(n == 123); - REQUIRE(parse(" 456", uint_[f], space)); + REQUIRE(parse(" 456", uint_.on_match(f), space)); CHECK(n == 456); } diff --git a/test/x4/attr.cpp b/test/x4/value.cpp similarity index 60% rename from test/x4/attr.cpp rename to test/x4/value.cpp index e85d21a7c..d6ac989a5 100644 --- a/test/x4/attr.cpp +++ b/test/x4/value.cpp @@ -9,7 +9,7 @@ #include "iris_x4_test.hpp" -#include +#include #include #include #include @@ -27,23 +27,23 @@ TEST_CASE("attr") using namespace std::string_literals; using namespace std::string_view_literals; - using x4::attr; + using x4::fixed_value; using x4::int_; { - [[maybe_unused]] constexpr auto attr_p = attr(1); - STATIC_CHECK(std::same_as, x4::attr_parser>); + [[maybe_unused]] constexpr auto attr_p = fixed_value(1); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>); } { - [[maybe_unused]] constexpr auto attr_p = attr(3.14); - STATIC_CHECK(std::same_as, x4::attr_parser>); + [[maybe_unused]] constexpr auto attr_p = fixed_value(3.14); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>); } { - constexpr auto attr_p = attr("foo"); - STATIC_REQUIRE(std::same_as, x4::attr_parser, std::basic_string_view>>); + constexpr auto attr_p = fixed_value("foo"); + STATIC_REQUIRE(std::same_as, x4::fixed_value_parser, std::basic_string_view>>); - // Make sure `attr(std::string_view)` is parsable into std::string + // Make sure `fixed_value(std::string_view)` is parsable into std::string { constexpr auto result = [&](std::string_view expected_str) consteval { std::string str; @@ -66,71 +66,71 @@ TEST_CASE("attr") } } { - [[maybe_unused]] /*constexpr*/ auto attr_p = attr("foo"s); - STATIC_CHECK(std::same_as, x4::attr_parser>>); + [[maybe_unused]] /*constexpr*/ auto attr_p = fixed_value("foo"s); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>>); } { - [[maybe_unused]] constexpr auto attr_p = attr("foo"sv); - STATIC_CHECK(std::same_as, x4::attr_parser>>); + [[maybe_unused]] constexpr auto attr_p = fixed_value("foo"sv); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>>); } { - [[maybe_unused]] constexpr auto attr_p = attr(U"foo"); - STATIC_CHECK(std::same_as, x4::attr_parser, std::basic_string_view>>); + [[maybe_unused]] constexpr auto attr_p = fixed_value(U"foo"); + STATIC_CHECK(std::same_as, x4::fixed_value_parser, std::basic_string_view>>); } { - [[maybe_unused]] /*constexpr*/ auto attr_p = attr(U"foo"s); - STATIC_CHECK(std::same_as, x4::attr_parser>>); + [[maybe_unused]] /*constexpr*/ auto attr_p = fixed_value(U"foo"s); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>>); } { - [[maybe_unused]] constexpr auto attr_p = attr(U"foo"sv); - STATIC_CHECK(std::same_as, x4::attr_parser>>); + [[maybe_unused]] constexpr auto attr_p = fixed_value(U"foo"sv); + STATIC_CHECK(std::same_as, x4::fixed_value_parser>>); } - IRIS_X4_ASSERT_CONSTEXPR_CTORS(attr(1)); - IRIS_X4_ASSERT_CONSTEXPR_CTORS(attr("asd")); + IRIS_X4_ASSERT_CONSTEXPR_CTORS(fixed_value(1)); + IRIS_X4_ASSERT_CONSTEXPR_CTORS(fixed_value("asd")); { constexpr char s[] = "asd"; - IRIS_X4_ASSERT_CONSTEXPR_CTORS(attr(s)); + IRIS_X4_ASSERT_CONSTEXPR_CTORS(fixed_value(s)); } { int d = 0; - REQUIRE(parse("", attr(1), d)); + REQUIRE(parse("", fixed_value(1), d)); CHECK(d == 1); } { int d = 0; int d1 = 1; - REQUIRE(parse("", attr(d1), d)); + REQUIRE(parse("", fixed_value(d1), d)); CHECK(d == 1); } { std::pair p; - REQUIRE(parse("1", int_ >> attr(2), p)); + REQUIRE(parse("1", int_ >> fixed_value(2), p)); CHECK(p.first == 1); CHECK(p.second == 2); } { char c = '\0'; - REQUIRE(parse("", attr('a'), c)); + REQUIRE(parse("", fixed_value('a'), c)); CHECK(c == 'a'); } { std::string str; - REQUIRE(parse("", attr("test"), str)); + REQUIRE(parse("", fixed_value("test"), str)); CHECK(str == "test"); } { std::string str; - REQUIRE(parse("", attr(std::string("test")), str)); + REQUIRE(parse("", fixed_value(std::string("test")), str)); CHECK(str == "test"); } { std::vector array = {0, 1, 2}; std::vector vec; - REQUIRE(parse("", attr(array), vec)); + REQUIRE(parse("", fixed_value(array), vec)); REQUIRE(vec.size() == 3); CHECK(vec[0] == 0); CHECK(vec[1] == 1); @@ -139,7 +139,7 @@ TEST_CASE("attr") { std::string s; - REQUIRE(parse("s", "s" >> attr(std::string("123")), s)); + REQUIRE(parse("s", "s" >> fixed_value(std::string("123")), s)); CHECK(s == "123"); } @@ -154,7 +154,7 @@ TEST_CASE("attr") } { std::vector> vecs; - REQUIRE(parse("", attr(std::vector{1, 2, 3}) >> attr(std::vector{4, 5, 6}), vecs)); + REQUIRE(parse("", fixed_value(std::vector{1, 2, 3}) >> fixed_value(std::vector{4, 5, 6}), vecs)); CHECK(vecs == std::vector{std::vector{1, 2, 3}, std::vector{4, 5, 6}}); } @@ -167,41 +167,41 @@ TEST_CASE("attr") } { std::vector strs; - REQUIRE(parse("", attr(std::string("123")) >> attr(std::string("456")), strs)); + REQUIRE(parse("", fixed_value(std::string("123")) >> fixed_value(std::string("456")), strs)); CHECK(strs == std::vector{"123", "456"}); } { std::string s; - REQUIRE(parse("", attr(std::string("123")) >> attr(std::string("456")), s)); + REQUIRE(parse("", fixed_value(std::string("123")) >> fixed_value(std::string("456")), s)); CHECK(s == "123456"); } { std::vector ints; - REQUIRE(parse("", attr(std::vector{1, 2, 3}) >> attr(std::vector{4, 5, 6}), ints)); + REQUIRE(parse("", fixed_value(std::vector{1, 2, 3}) >> fixed_value(std::vector{4, 5, 6}), ints)); CHECK(ints == std::vector{1, 2, 3, 4, 5, 6}); } { std::vector ints; REQUIRE(parse("", - (attr(std::vector{1, 2, 3}) >> attr(std::vector{4, 5, 6})) >> - (attr(std::vector{7, 8, 9}) >> attr(std::vector{0, 1, 2})), + (fixed_value(std::vector{1, 2, 3}) >> fixed_value(std::vector{4, 5, 6})) >> + (fixed_value(std::vector{7, 8, 9}) >> fixed_value(std::vector{0, 1, 2})), ints )); CHECK(ints == std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2}); } } -TEST_CASE("init_attr") +TEST_CASE("reset_value") { - using x4::init_attr; + using x4::reset_value; { int val = 42; - STATIC_CHECK(std::same_as)>::attribute_type, int>); - REQUIRE(parse("", init_attr, val)); + STATIC_CHECK(std::same_as)>::attribute_type, int>); + REQUIRE(parse("", reset_value, val)); CHECK(val == 0); } { @@ -210,8 +210,8 @@ TEST_CASE("init_attr") val.emplace_back(42); auto const prev_capacity = val.capacity(); - STATIC_CHECK(std::same_as>)>::attribute_type, std::vector>); - REQUIRE(parse("", init_attr>, val)); + STATIC_CHECK(std::same_as>)>::attribute_type, std::vector>); + REQUIRE(parse("", reset_value>, val)); CHECK(val.empty()); CHECK(val.capacity() == prev_capacity); // should preserve capacity as per `.clear()` } diff --git a/test/x4/with.cpp b/test/x4/with.cpp index f876a4e19..bfc9f3e5f 100644 --- a/test/x4/with.cpp +++ b/test/x4/with.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -50,11 +50,11 @@ using x4::with; using x4::_attr; template -constexpr auto value_equals = int_[([](auto&& ctx) { +constexpr auto value_equals = int_.on_match([](auto&& ctx) { auto&& with_val = x4::get(ctx); static_assert(std::same_as); return with_val == _attr(ctx); -})]; +}); } // anonymous @@ -148,22 +148,22 @@ TEST_CASE("with") { // injecting non-const lvalue into the context int val = 0; - auto const r = int_[([](auto&& ctx){ + auto const r = int_.on_match([](auto&& ctx){ x4::get(ctx) += x4::_attr(ctx); - })]; + }); REQUIRE(parse("123,456", with(val)[r % ','])); CHECK(val == 579); } { // injecting rvalue into the context - auto const r1 = int_[([](auto&& ctx){ + auto const r1 = int_.on_match([](auto&& ctx){ x4::get(ctx) += x4::_attr(ctx); - })]; + }); auto const r2 = rule() = - x4::lit('(') >> (r1 % ',') >> x4::lit(')')[([](auto&& ctx){ + x4::lit('(') >> (r1 % ',') >> x4::lit(')').on_match([](auto&& ctx){ x4::_rule_var(ctx) = x4::get(ctx); - })]; + }); int attr = 0; REQUIRE(parse("(1,2,3)", with(100)[r2], attr)); CHECK(attr == 106); @@ -186,7 +186,7 @@ TEST_CASE("with") auto f = [](auto&& ctx){ x4::_rule_var(ctx) = x4::_attr(ctx) + functor()(x4::get(ctx)); }; - auto const r = rule() = int_[f]; + auto const r = rule() = int_.on_match(f); { int attr = 0; diff --git a/test/x4/with_local.cpp b/test/x4/with_local.cpp index d3f3c9882..e547976d4 100644 --- a/test/x4/with_local.cpp +++ b/test/x4/with_local.cpp @@ -8,10 +8,10 @@ #include "iris_x4_test.hpp" -#include -#include +#include #include -#include +#include +#include #include #include @@ -34,13 +34,13 @@ TEST_CASE("with_local") { constexpr auto p = with_local[ as( - int_[([](auto&& ctx) { + int_.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); _local_var(ctx) = _attr(ctx) * 100; - })] >> - eps[([](auto&& ctx) { + }) >> + eps.on_match([](auto&& ctx) { _as_var(ctx) = _local_var(ctx); - })] + }) ) ]; constexpr std::string_view input = "42"; @@ -55,13 +55,13 @@ TEST_CASE("with_local") int i = -1; auto const p = with_local[ - int_[([](auto&& ctx) { + int_.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); _local_var(ctx) = _attr(ctx) * 100; - })] >> - eps[([&](auto&& ctx) { + }) >> + eps.on_match([&](auto&& ctx) { i = _local_var(ctx); - })] + }) ]; constexpr std::string_view input = "42"; auto first = input.begin(); @@ -74,25 +74,25 @@ TEST_CASE("with_local") int i = -1; auto const p = with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); - })] >> + }) >> with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); - })] >> - int_[([](auto&& ctx) { + }) >> + int_.on_match([](auto&& ctx) { _local_var(ctx) = _attr(ctx); - })] >> - eps[([&](auto&& ctx) { + }) >> + eps.on_match([&](auto&& ctx) { i = _local_var(ctx) * 100; - })] + }) ] >> - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); - })] + }) ]; constexpr std::string_view input = "42"; auto first = input.begin(); @@ -105,27 +105,27 @@ TEST_CASE("with_local") double d = -1.0; auto const p = with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { static_assert(std::same_as); CHECK(_local_var(ctx) == 0); - })] >> + }) >> with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { static_assert(std::same_as); CHECK(_local_var(ctx) == 0.0); - })] >> - int_[([](auto&& ctx) { + }) >> + int_.on_match([](auto&& ctx) { _local_var(ctx) = _attr(ctx); - })] >> - eps[([&](auto&& ctx) { + }) >> + eps.on_match([&](auto&& ctx) { d = std::ceil(_local_var(ctx) / 10); - })] + }) ] >> - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(_local_var(ctx) == 0); - })] + }) ]; constexpr std::string_view input = "42"; auto first = input.begin(); @@ -141,26 +141,26 @@ TEST_CASE("with_local") struct B_ID {}; auto const p = with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(x4::get(ctx) == 0); - })] >> + }) >> with_local[ - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(x4::get(ctx) == 0.0); - })] >> - int_[([](auto&& ctx) { + }) >> + int_.on_match([](auto&& ctx) { x4::get(ctx) = _attr(ctx) * 10; x4::get(ctx) = _attr(ctx) * 100; - })] >> - eps[([&](auto&& ctx) { + }) >> + eps.on_match([&](auto&& ctx) { res = std::make_tuple(x4::get(ctx), x4::get(ctx)); - })] + }) ] >> - eps[([](auto&& ctx) { + eps.on_match([](auto&& ctx) { CHECK(x4::get(ctx) == 420); - })] + }) ]; constexpr std::string_view input = "42"; auto first = input.begin(); diff --git a/test/x4/without.cpp b/test/x4/without.cpp index 3e09178e3..fd1be30bb 100644 --- a/test/x4/without.cpp +++ b/test/x4/without.cpp @@ -7,10 +7,9 @@ #include "iris_x4_test.hpp" -#include -#include -#include #include +#include +#include #include #include