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 isoptional-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 config.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.
Similarly, std::optional<int*>(lexy::nullopt{}) will contain a nullptr . |
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 ifbranch
has failed.- Values
If
branch
was parsed, all values produced by it. Otherwise, a single object of the tag typelexy::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. |
Tip | Use lexy::dsl::flag if you want a value if branch parsed as well. |