Header lexy/dsl/option.hpp

The nullopt and opt() rules.

Rule lexy::dsl::nullopt

lexy/dsl/option.hpp
namespace lexy
{
    template <typename T>
    concept optional-like
      = std::is_default_constructible_v<T>
        && requires(T t) {
          *t;
          !t;
        };

    struct nullopt
    {
        template <typename T>
          requires optional-like<T>
        constexpr operator T() const;
    };
}

namespace lexy::dsl
{
    constexpr rule auto nullopt;
}

nullopt is a rule that produces a value of type lexy::nullopt without consuming anything.

Parsing

Always succeeds without consuming anything.

Errors

None.

Values

A single object of the tag type lexy::nullopt. This type is implicitly convertible to any type that is optional-like by constructing a default constructible instance of it.

Example 1. Recover from missing version numbers
struct version
{
    std::optional<int> major, minor, patch;
};

struct production
{
    static constexpr auto rule = [] {
        // If we don't have an integer, recover by producing nullopt.
        auto number = dsl::try_(dsl::integer<int>, dsl::nullopt);
        auto dot    = dsl::try_(dsl::period);
        return number + dot + number + dot + number;
    }();

    static constexpr auto value = lexy::construct<version>;
};
Note
See tutorial.cpp for a more complete version number parser.
Example 2. Create an empty optional if we're not having the key
struct flag
{
    std::optional<std::string> key;
    std::string                value;
};

struct production
{
    struct flag_key
    {
        static constexpr auto rule  = dsl::identifier(dsl::ascii::alpha);
        static constexpr auto value = lexy::as_string<std::string>;
    };

    struct flag_value
    {
        static constexpr auto rule  = dsl::identifier(dsl::ascii::alnum);
        static constexpr auto value = lexy::as_string<std::string>;
    };

    static constexpr auto whitespace = dsl::ascii::blank; // no newline

    static constexpr auto rule = [] {
        // key = value
        auto key_value = dsl::p<flag_key> + dsl::lit_c<'='> + dsl::p<flag_value>;
        // no key, just value
        auto value = dsl::nullopt + dsl::p<flag_value>;

        // We have a key if we're having an equal sign before the newline.
        auto key_condition = dsl::lookahead(dsl::lit_c<'='>, dsl::newline);
        return (key_condition >> key_value | dsl::else_ >> value) + dsl::eol;
    }();

    static constexpr auto value = lexy::construct<flag>;
};
Caution
If lexy::nullopt is combined with a type that has a constructor accepting lexy::nullopt (e.g. because it is templated), it will use that constructor instead of the default constructor. For example, std::optional<std::optional<int>>(lexy::nullopt{}) will contain a default-constructed std::optional<int>, and is not empty itself. This is not the same behavior as std::optional<std::optional<int>>(std::nullopt{}), which will create a std::optional<std::optional<int>> containing nothing.

Rule lexy::dsl::opt

lexy/dsl/option.hpp
namespace lexy::dsl
{
    constexpr rule auto opt(branch-rule auto branch);
}

opt is a rule that tries to parse a branch rule, producing a lexy::nullopt otherwise.

Parsing

Tries to parse branch. If that backtracks, succeeds without consuming anything.

Errors

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

Values

If branch was parsed, all values produced by it. Otherwise, a single object of the tag type lexy::nullopt (see above).

Example 3. Only parse a fraction if preceded by a dot
struct decimal
{
    int                        integer;
    std::optional<std::string> fraction;
};

struct production
{
    struct fraction
    {
        static constexpr auto rule  = dsl::capture(dsl::digits<>);
        static constexpr auto value = lexy::as_string<std::string>;
    };

    static constexpr auto rule = [] {
        auto integer = dsl::integer<int>;

        return integer + dsl::opt(dsl::period >> dsl::p<fraction>);
    }();

    static constexpr auto value = lexy::construct<decimal>;
};
Note
opt(branch) is functionally equivalent to the choice branch | lexy::dsl::else_ >> lexy::dsl::nullopt, and it parses the same as lexy::dsl::if_.
Tip
Use lexy::bind with lexy::nth_value and or to pass a different value than lexy::nullopt to the callback.

See also