Header lexy/dsl/production.hpp

The inline_, p, and recurse rules.

Rule lexy::dsl::inline_

lexy/dsl/production.hpp
namespace lexy::dsl
{
    template <production P>
    constexpr rule auto inline_;

    template <production P>
      requires token-rule<production_rule<P>>
    constexpr token-rule auto inline_;

    template <production P>
      requires branch-rule<production_rule<P>>
    constexpr branch-rule auto inline_;
}

inline_ is a rule that parses the rule of production P as part of the current production.

Requires

P is a complete type, i.e. p cannot be used for recursion.

Matching

Matches P::rule.

(Branch) Parsing

Parses P::rule.

Errors

All errors raised by P::rule. The rule then fails if P::rule has failed.

Values

All values produced by P::rule.

Example 1. Parse a child production inline
struct name
{
    static constexpr auto rule
        // One or more alpha numeric characters, underscores or hyphens.
        = dsl::identifier(dsl::ascii::alnum / dsl::lit_c<'_'> / dsl::lit_c<'-'>);
};

struct production
{
    // Allow arbitrary spaces between individual tokens.
    // Note that this includes the individual characters of the name.
    static constexpr auto whitespace = dsl::ascii::space;

    static constexpr auto rule = [] {
        auto greeting = LEXY_LIT("Hello");
        return greeting + dsl::inline_<name> + dsl::exclamation_mark + dsl::eof;
    }();
};
Tip
Use inline_ when you need to parse a production as part of the whitespace rule.

Rule lexy::dsl::p

lexy/dsl/production.hpp
namespace lexy::dsl
{
    template <production P>
    constexpr rule auto p;

    template <production P>
      requires branch-rule<production_rule<P>>
    constexpr branch-rule auto p;
}

p is a rule that parses the production P.

Requires

P is a complete type, i.e. p cannot be used for recursion.

(Branch) Parsing

Parses P::rule in a new context for P.

Errors

All errors raised by parsing P::rule, but forwarded to the new context for P. The rule fails if P::rule has failed.

Values

All values produced by P::rule are forwarded to the new context, e.g. to P::value. The final value of the context is produced as the single value for p.

Parse tree
  • If P inherits from lexy::transparent_production, the nodes generated by P::rule are added to the parse tree as if they were added by p itself.

  • Otherwise, it creates a single production node for P. Its children are all the nodes generated by P::rule.

Example 2. Parse a child production
struct name
{
    static constexpr auto rule
        // One or more alpha numeric characters, underscores or hyphens.
        = dsl::identifier(dsl::ascii::alnum / dsl::lit_c<'_'> / dsl::lit_c<'-'>);
};

struct production
{
    // Allow arbitrary spaces between individual tokens.
    static constexpr auto whitespace = dsl::ascii::space;

    static constexpr auto rule = [] {
        auto greeting = LEXY_LIT("Hello");
        return greeting + dsl::p<name> + dsl::exclamation_mark + dsl::eof;
    }();
};
Example 3. Productions with branches
constexpr auto id          = dsl::identifier(dsl::ascii::alpha);
constexpr auto kw_function = LEXY_KEYWORD("function", id);
constexpr auto kw_type     = LEXY_KEYWORD("type", id);

struct function_decl
{
    static constexpr auto rule = [] {
        auto arguments = dsl::parenthesized(LEXY_LIT("..."));
        auto body      = dsl::curly_bracketed(LEXY_LIT("..."));

        return kw_function >> id + arguments + body;
    }();
};

struct type_decl
{
    static constexpr auto rule //
        = kw_type >> id + dsl::lit_c<'='> + id + dsl::semicolon;
};

struct production
{
    static constexpr auto whitespace = dsl::ascii::space;
    static constexpr auto rule       = dsl::p<function_decl> | dsl::p<type_decl>;
};
Note
The p rule cannot handle direct or indirect recursion, as P must be a complete type.
Caution
While parsing P::rule, the current whitespace rule can be different. If whitespace parsing has been disabled using lexy::dsl::no_whitespace, it is temporarily re-enabled while parsing P::rule. If whitespace parsing has been disabled because the current production inherits from lexy::token_production, it is still disabled, unless P explicitly defines a new whitespace rule.

Rule lexy::dsl::recurse

lexy/dsl/production.hpp
namespace lexy
{
    struct max_recursion_depth_exceeded {};
}

namespace lexy::dsl
{
    struct recurse // models rule or branch-rule
    {
        template <typename Tag>
        static constexpr recurse max_depth_error;
    };

    template <production P>
    constexpr recurse auto recurse;        // rule
    template <production P>
    constexpr recurse auto recurse_branch; // branch-rule
}

recurse is a rule that parses the production P but supports recursion.

It behaves similar to the p rule, but P does not need to a be a complete type, which allows recursion. For the same reason, recurse<P> is never a branch rule: it can’t know whether P is a branch. Use recurse_branch<P> if you know that P is a branch; it will cause a delayed static_assert if it isn’t.

Parsing

Checks whether the current depth of recursive production call exceeds the maximum parse depth, which is determined by lexy::max_recursion_depth. Fails, if that is the case. Otherwise, parses p<P>, i.e. the production P.

Branch parsing

Branch parses p<P>, i.e. the production P. The recursive depth check is done after the branch condition has matched. It will not backtrack if the condition matches but the depth is exceeded.

Errors
  • A generic error with the specified Tag or lexy::max_recursion_depth_exceeded if the recursive depth is exceeded, at the position where it would have started to match the production. It then fails without recovering.

  • All errors raised by parsing p<P>.

Example 4. Parse a parenthesized expression
struct production
{
    static constexpr auto rule
        // Either we parse ourselves surrounded by expressions, or an atom.
        = dsl::parenthesized(dsl::recurse<production>) | LEXY_LIT("atom");
};
Example 5. Parse a parenthesized expression with a recursion limit
struct production
{
    // We define a (tiny) maximum recursion depth.
    // This prevents unbounded recursion which can cause a stack overflow.
    static constexpr auto max_recursion_depth = 3;

    static constexpr auto rule
        // Either we parse ourselves surrounded by expressions, or an atom.
        = dsl::parenthesized(dsl::recurse<production>) | LEXY_LIT("atom");
};
Warning
Left recursion will create a max recursion error. Use lexy::dsl::loop or lexy::dsl::list instead.
Note
The recursion depth only counts productions parsed by recurse; intermediate productions parsed using p are ignored. In particular, the nesting level of p rules, which is statically determined by the grammar and not by the input, is allowed to exceed the maximum recursion depth.

See also