Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 191 additions & 1 deletion README.md

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion include/iris/x4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
==============================================================================*/

#include <iris/config.hpp>

#include <iris/x4/core/action.hpp>
#include <iris/x4/auxiliary.hpp>
#include <iris/x4/primitive.hpp>
#include <iris/x4/attribute.hpp>
#include <iris/x4/char.hpp>
#include <iris/x4/directive.hpp>
#include <iris/x4/rule.hpp>
#include <iris/x4/numeric.hpp>
#include <iris/x4/operator.hpp>
#include <iris/x4/string.hpp>

#include <iris/x4/debug/print_attribute.hpp>
#include <iris/x4/debug/default_error_handler.hpp>

#endif
16 changes: 16 additions & 0 deletions include/iris/x4/attribute.hpp
Original file line number Diff line number Diff line change
@@ -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 <iris/config.hpp>
#include <iris/x4/attribute/as.hpp>
// #include <iris/x4/attribute/smart_ptr.hpp> // excluded
#include <iris/x4/attribute/value.hpp>

#endif
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,6 +12,8 @@
#include <iris/x4/core/parser.hpp>
#include <iris/x4/core/move_to.hpp>
#include <iris/x4/core/unused.hpp>
#include <iris/x4/core/context.hpp>
#include <iris/x4/core/action_context.hpp>

#include <concepts>
#include <iterator>
Expand All @@ -23,12 +25,12 @@ namespace iris::x4 {
namespace detail {

template<bool SubjectHasAction, class Context, X4Attribute OuterAttr>
struct as_directive_ctx_impl // false
struct as_type_parser_ctx_impl // false
{
using type = Context;
};
template<class Context, X4Attribute OuterAttr>
struct as_directive_ctx_impl<true, Context, OuterAttr>
struct as_type_parser_ctx_impl<true, Context, OuterAttr>
{
using type = std::remove_cvref_t<decltype(x4::replace_first_context<contexts::as_var>(
std::declval<Context const&>(),
Expand All @@ -38,11 +40,11 @@ struct as_directive_ctx_impl<true, Context, OuterAttr>

} // 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<X4Attribute T, class Subject>
struct as_directive : unary_parser<Subject, as_directive<T, Subject>>
struct as_type_parser : unary_parser<Subject, as_type_parser<T, Subject>>
{
static_assert(!std::is_const_v<T>); // Forbid const `unused_type`
static_assert(!std::same_as<T, unused_container_type>); // Unknown use case, not supported for now
Expand All @@ -55,9 +57,9 @@ struct as_directive : unary_parser<Subject, as_directive<T, Subject>>
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<as_directive<...>>::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<as_type_parser<...>>::handles_container` must transparently
// handle this case.

private:
Expand All @@ -72,7 +74,7 @@ struct as_directive : unary_parser<Subject, as_directive<T, Subject>>
requires std::same_as<std::remove_const_t<OuterAttr>, T>
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const
noexcept(is_nothrow_parsable_v<Subject, It, Se, typename detail::as_directive_ctx_impl<Subject::has_action, Context, OuterAttr>::type, exposed_attr_for_child_t<OuterAttr>>)
noexcept(is_nothrow_parsable_v<Subject, It, Se, typename detail::as_type_parser_ctx_impl<Subject::has_action, Context, OuterAttr>::type, exposed_attr_for_child_t<OuterAttr>>)
{
if constexpr (Subject::has_action) {
return this->subject.parse(first, last, x4::replace_first_context<contexts::as_var>(ctx, outer_attr), unused);
Expand All @@ -87,7 +89,7 @@ struct as_directive : unary_parser<Subject, as_directive<T, Subject>>
(!std::same_as<std::remove_const_t<OuterAttr>, T>)
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& ctx, OuterAttr&) const
noexcept(is_nothrow_parsable_v<Subject, It, Se, typename detail::as_directive_ctx_impl<Subject::has_action, Context, unused_type>::type, unused_type>)
noexcept(is_nothrow_parsable_v<Subject, It, Se, typename detail::as_type_parser_ctx_impl<Subject::has_action, Context, unused_type>::type, unused_type>)
{
if constexpr (Subject::has_action) {
return this->subject.parse(first, last, x4::replace_first_context<contexts::as_var>(ctx, unused), unused);
Expand All @@ -104,7 +106,7 @@ struct as_directive : unary_parser<Subject, as_directive<T, Subject>>
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const
noexcept(
is_nothrow_parsable_v<Subject, It, Se, typename detail::as_directive_ctx_impl<Subject::has_action, Context, T>::type, exposed_attr_for_child_t<T>> &&
is_nothrow_parsable_v<Subject, It, Se, typename detail::as_type_parser_ctx_impl<Subject::has_action, Context, T>::type, exposed_attr_for_child_t<T>> &&
noexcept(x4::move_to(std::declval<T>(), outer_attr))
)
{
Expand Down Expand Up @@ -134,24 +136,24 @@ template<X4Attribute T>
struct as_fn
{
template<X4Subject Subject>
[[nodiscard]] static constexpr as_directive<T, as_parser_plain_t<Subject>>
[[nodiscard]] static constexpr as_type_parser<T, as_parser_plain_t<Subject>>
operator()(Subject&& subject)
noexcept(is_parser_nothrow_constructible_v<as_directive<T, as_parser_plain_t<Subject>>, Subject>)
noexcept(is_parser_nothrow_constructible_v<as_type_parser<T, as_parser_plain_t<Subject>>, Subject>)
{
return {std::forward<Subject>(subject)};
}
};

} // detail

namespace parsers::directive {
namespace parsers {

template<X4Attribute T>
[[maybe_unused]] inline constexpr detail::as_fn<T> as{};

} // parsers::directive
} // parsers

using parsers::directive::as;
using parsers::as;

} // iris::x4

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -26,16 +26,17 @@

namespace iris::x4 {

// `fixed_value(...)`
template<class T, class HeldValueT = T>
struct attr_parser : parser<attr_parser<T, HeldValueT>>
struct fixed_value_parser : parser<fixed_value_parser<T, HeldValueT>>
{
static_assert(X4Attribute<T>);
static_assert(!X4UnusedAttribute<T>, "attr_parser with `unused_type` is meaningless");
static_assert(!X4UnusedAttribute<T>, "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<HeldValueT const&, T>);
Expand All @@ -45,9 +46,9 @@ struct attr_parser : parser<attr_parser<T, HeldValueT>>

template<class U>
requires
(!std::is_same_v<std::remove_cvref_t<U>, attr_parser>) &&
(!std::is_same_v<std::remove_cvref_t<U>, fixed_value_parser>) &&
std::is_constructible_v<HeldValueT, U>
constexpr explicit attr_parser(U&& value)
constexpr explicit fixed_value_parser(U&& value)
noexcept(std::is_nothrow_constructible_v<HeldValueT, U>)
: held_value_(std::forward<U>(value))
{}
Expand All @@ -66,12 +67,12 @@ struct attr_parser : parser<attr_parser<T, HeldValueT>>
HeldValueT held_value_;
};

// `init_attr<T>`
// `reset_value<T>`
template<class T>
struct attr_parser<T, void> : parser<attr_parser<T, void>>
struct fixed_value_parser<T, void> : parser<fixed_value_parser<T, void>>
{
static_assert(X4Attribute<T>);
static_assert(!X4UnusedAttribute<T>, "attr_parser with `unused_type` is meaningless");
static_assert(!X4UnusedAttribute<T>, "fixed_value_parser with `unused_type` is meaningless");

using attribute_type = T;

Expand Down Expand Up @@ -105,48 +106,52 @@ struct attr_parser<T, void> : parser<attr_parser<T, void>>
namespace detail {

template<CharArray R>
using string_array_attr_parser_t = attr_parser<
using string_array_attr_parser_t = fixed_value_parser<
std::basic_string<std::remove_extent_t<std::remove_cvref_t<R>>>,
std::basic_string_view<std::remove_extent_t<std::remove_cvref_t<R>>>
>;

} // detail

template<CharArray R>
attr_parser(R const&) -> attr_parser<
fixed_value_parser(R const&) -> fixed_value_parser<
std::basic_string<std::remove_extent_t<std::remove_cvref_t<R>>>,
std::basic_string_view<std::remove_extent_t<std::remove_cvref_t<R>>>
>;

template<class T, class HeldValueT>
struct get_info<attr_parser<T, HeldValueT>>
struct get_info<fixed_value_parser<T, HeldValueT>>
{
using result_type = std::string;
[[nodiscard]] constexpr std::string
operator()(attr_parser<T, HeldValueT> const&) const
operator()(fixed_value_parser<T, HeldValueT> const&) const
{
return "attr";
if constexpr (std::is_void_v<HeldValueT>) {
return "reset_value<T>";
} else {
return "fixed_value<T>(...)";
}
}
};

namespace detail {

struct attr_gen
struct fixed_value_gen
{
template<class T>
[[nodiscard]] static constexpr attr_parser<std::remove_cvref_t<T>>
[[nodiscard]] static constexpr fixed_value_parser<std::remove_cvref_t<T>>
operator()(T&& value)
noexcept(std::is_nothrow_constructible_v<attr_parser<std::remove_cvref_t<T>>, T>)
noexcept(std::is_nothrow_constructible_v<fixed_value_parser<std::remove_cvref_t<T>>, T>)
{
return attr_parser<std::remove_cvref_t<T>>{std::forward<T>(value)};
return fixed_value_parser<std::remove_cvref_t<T>>{std::forward<T>(value)};
}

template<CharArray R>
[[nodiscard]] static constexpr string_array_attr_parser_t<R>
operator()(R&& value)
noexcept(std::is_nothrow_constructible_v<string_array_attr_parser_t<R>, R>)
template<CharArray CharArrayT>
[[nodiscard]] static constexpr string_array_attr_parser_t<CharArrayT>
operator()(CharArrayT&& char_array)
noexcept(std::is_nothrow_constructible_v<string_array_attr_parser_t<CharArrayT>, CharArrayT>)
{
return string_array_attr_parser_t<R>{std::forward<R>(value)};
return string_array_attr_parser_t<CharArrayT>{std::forward<CharArrayT>(char_array)};
}
};

Expand All @@ -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<int>{})` cannot be assigned
// to a `constexpr` instance, but `init_attr<std::vector<int>>` can.
// For example, normal `fixed_value(std::vector<int>{})` cannot be assigned
// to a `constexpr` instance, but `reset_value<std::vector<int>>` can.
template<class T>
[[maybe_unused]] inline constexpr attr_parser<T, void> init_attr{};
[[maybe_unused]] inline constexpr fixed_value_parser<T, void> reset_value{};

} // parsers

using parsers::attr;
using parsers::init_attr;
using parsers::fixed_value;
using parsers::reset_value;

} // iris::x4

Expand Down
21 changes: 0 additions & 21 deletions include/iris/x4/core/action.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,27 +249,6 @@ struct action : proxy_parser<Subject, action<Subject, ActionF>>
}
};

template<X4Subject Subject, class Action>
[[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<as_parser_plain_t<Subject>, std::remove_cvref_t<Action>>
operator/(Subject&& p, Action&& f)
noexcept(
is_parser_nothrow_castable_v<Subject> &&
std::is_nothrow_constructible_v<
action<as_parser_plain_t<Subject>, std::remove_cvref_t<Action>>,
as_parser_t<Subject>, Action
>
)
{
return {as_parser(std::forward<Subject>(p)), std::forward<Action>(f)};
}

} // iris::x4

#endif
2 changes: 1 addition & 1 deletion include/iris/x4/core/move_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<traits::NonUnusedAttr Source, traits::CategorizedAttr<traits::container_attr> Dest>
requires
(!traits::X4Container<Source>) &&
Expand Down
23 changes: 21 additions & 2 deletions include/iris/x4/core/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ struct parser : private detail::parser_base
decltype(std::declval<Self>().derived()),
Action
>
[[nodiscard]] constexpr action<Derived, std::remove_cvref_t<Action>>
operator[](this Self&& self, Action&& f)
[[nodiscard]]
constexpr action<Derived, std::remove_cvref_t<Action>>
on_match(this Self&& self, Action&& f)
noexcept(std::is_nothrow_constructible_v<
action<Derived, std::remove_cvref_t<Action>>,
decltype(std::forward<Self>(self).derived()),
Expand All @@ -86,6 +87,24 @@ struct parser : private detail::parser_base
{
return {std::forward<Self>(self).derived(), std::forward<Action>(f)};
}

template<class Self, class Action>
requires std::is_constructible_v<
action<Derived, std::remove_cvref_t<Action>>,
decltype(std::declval<Self>().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<Derived, std::remove_cvref_t<Action>>
operator[](this Self&& self, Action&& f)
noexcept(std::is_nothrow_constructible_v<
action<Derived, std::remove_cvref_t<Action>>,
decltype(std::forward<Self>(self).derived()),
Action
>)
{
return std::forward<Self>(self).on_match(std::forward<Action>(f));
}
};

template<class Subject, class Derived>
Expand Down
3 changes: 0 additions & 3 deletions include/iris/x4/directive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@
==============================================================================*/

#include <iris/config.hpp>
#include <iris/x4/directive/as.hpp>
#include <iris/x4/directive/expect.hpp>
#include <iris/x4/directive/lexeme.hpp>
#include <iris/x4/directive/matches.hpp>
#include <iris/x4/directive/no_case.hpp>
#include <iris/x4/directive/no_skip.hpp>
#include <iris/x4/directive/omit.hpp>
#include <iris/x4/directive/repeat.hpp>
#include <iris/x4/directive/seek.hpp>
#include <iris/x4/directive/skip.hpp>
#include <iris/x4/directive/with.hpp>
#include <iris/x4/directive/with_local.hpp>
#include <iris/x4/directive/without.hpp>

#endif
Loading
Loading