Header lexy/dsl/context_counter.hpp

A counter that can be manipulated during parsing.

Rule DSL lexy::dsl::context_counter

lexy/dsl/context_counter.hpp
namespace lexy::dsl
{
    struct context_counter-dsl // note: not a rule itself
    {
        template <int InitialValue = 0>
        constexpr rule auto create() const;

        constexpr rule auto inc() const;
        constexpr rule auto dec() const;

        constexpr rule auto push(rule auto r) const;
        constexpr rule auto pop(rule auto r) const;

        template <int Value>
        constexpr branch-rule auto is() const;
        constexpr branch-rule auto is_zero() const;

        constexpr rule auto value() const;
    };

    template <typename Id>
    constexpr context_counter-dsl context_counter;
}

context_counter is not a rule, but a DSL for specifying rules that manipulate a counter of the current context.

Example 1. Parse n a, then n b without recursion
struct production
{
    struct mismatch
    {};

    static constexpr auto rule = [] {
        // Declare a counter - it is not created yet!
        auto counter = dsl::context_counter<production>;

        // Parse a sequence of 'a' and add the number to it.
        auto a = counter.push(dsl::while_(dsl::lit_c<'a'>));
        // Parse a sequence of 'b' and subtract the number from it.
        auto b = counter.pop(dsl::while_(dsl::lit_c<'b'>));

        // Create the counter initialized to zero,
        // parse the two, and require its back to zero.
        return counter.create() + a + b + dsl::must(counter.is_zero()).error<mismatch>;
    }();
};

Rule .create()

lexy/dsl/context_counter.hpp
template <int InitialValue = 0>
constexpr rule auto create() const;

.create() returns a rule that creates the counter.

Parsing

Matches everything, without consuming anything. As a side effect, it creates a counter with name Id inside the current context. If a context variable (of any) type with that name already exists, it is shadowed. This counter is initialized to InitialValue; it’s underlying type is int.

Errors

None.

Values

None.

Rules .inc(), .dec()

lexy/dsl/context_counter.hpp
constexpr rule auto inc() const;
constexpr rule auto dec() const;

.inc(), .dec() return rules that increment/decrement counter.

Requires

A counter with the name Id has been created in the current context, i.e. .create() has been parsed earlier.

Parsing

Matches everything, without consuming anything. As a side effect, it modifies the counter with name Id of the current context:

  • .inc() increments it by one.

  • .dec() decrements it by one.

Errors

None.

Values

None.

Rules .push(), .pop()

lexy/dsl/context_counter.hpp
constexpr rule auto push(rule auto r) const;
constexpr rule auto pop(rule auto r) const;

.push(), .pop() return rules that add/subtract the counter.

Requires

A counter with the name Id has been created in the current context, i.e. .create() has been parsed earlier.

Parsing

Parses r and counts the number of code units consumed by r. As a side effect, it modifies the counter with name Id of the current context:

  • .push() adds the number of code units to it.

  • .pop() subtracts the number of code units from it.

Errors

All errors raised by parsing r. The rule then fails if r has failed.

Values

All errors produced by parsing r.

Caution
The count includes any whitespace characters.

Branch rules .is(), .is_zero()

lexy/dsl/context_counter.hpp
template <int Value>
constexpr branch-rule auto is() const;

constexpr branch-rule auto is_zero() const
{
    return is<0>();
}

.is() and .is_zero() return branch rules that check the value of the counter.

Requires

A counter with the name Id has been created in the current context, i.e. .create() has been parsed earlier.

Parsing

Matches everything, without consuming anything.

Branch parsing

Backtracks unless the value of the boolean with name Id of the current context is Value (.is()) or 0 (.is_zero()). Then matches everything without consuming anything.

Errors

None.

Values

None.

Rule .value()

lexy/dsl/context_counter.hpp
constexpr rule auto value() const;

.value() is a rule that returns the value of the counter.

Requires

A counter with the name Id has been created in the current context, i.e. .create() has been parsed earlier.

Parsing

Matches everything, without consuming anything.

Errors

None.

Values

The current value of the counter (an int) with name Id of the current context.

Rule DSL lexy::dsl::equal_counts

lexy/dsl/context_counter.hpp
namespace lexy::dsl
{
    constexpr branch-rule auto equal_counts(context_counter-dsl... counters);
}

equal_counts is a branch rule that checks whether all counters have the same value.

Requires
  • sizeof…​(counters) > 1

  • All counters have been created in the current context, i.e. .create() has been parsed earlier.

Parsing

Matches everything, without consuming anything. Only succeeds if all counters have the same value in the current context.

Branch parsing

Backtracks unless all counters have the same value in the current context; no error is raised. Matches everything, without consuming anything.

Errors

A generic error with the tag lexy::unequal_counts at the unchanged reader position. It then recovers without having consumed any input.

Values

None.

Example 2. Parse n a, then n b, then n c
struct production
{
    static constexpr auto rule = [] {
        // Parse 'a's and count them.
        auto ac = dsl::context_counter<struct ac_id>;
        auto a  = ac.create() + ac.push(dsl::while_(dsl::lit_c<'a'>));

        // Parse 'b's and count them.
        auto bc = dsl::context_counter<struct bc_id>;
        auto b  = bc.create() + bc.push(dsl::while_(dsl::lit_c<'b'>));

        // Parse 'c's and count them.
        auto cc = dsl::context_counter<struct cc_id>;
        auto c  = cc.create() + cc.push(dsl::while_(dsl::lit_c<'c'>));

        // Check that they're equal.
        auto check = dsl::equal_counts(ac, bc, cc);

        return a + b + c + check;
    }();
};

See also