Preview of main at d0a501a, 2026-11-07

lexical is a header-only library of validators for the string formats of JSON Schema, in C++20, one of the libraries of webcpp. Each format is a function that takes a std::string_view and answers bool, and, but regex, a Boost.URL grammar rule, which composes with Boost.URL’s combinators into grammars of a program’s own. Each is checked as its RFC or ECMA-262 defines it, against the official JSON-Schema-Test-Suite, and regex against V8. Nothing allocates but an IRI too long for its inline buffer, through a std::pmr::memory_resource.

This page teaches lexical format by format: what each checks, its rules and its examples. Every program it shows was compiled and run, natively and for wasm32-wasip2, wasm32-wasip3 and emscripten, and printed the output shown under it. The reference that closes it lists every name the headers declare.

Introduction

JSON Schema names string formats, date-time, email, hostname, uri, regex and the others, each defined by an RFC or by ECMA-262. lexical checks each of them as its reference defines it, and nothing more: a validator answers whether a string is in the format, and a rule says where in a longer input the format’s text ends.

Every validator takes any byte string. One that is not well-formed UTF-8 is invalid in every format, and in the formats that are ASCII, so is any byte above 0x7F.

Format Function Reference

date-time

is_date_time

RFC 3339 5.6, 5.7

date

is_date

RFC 3339 5.6 full-date

time

is_time

RFC 3339 5.6 full-time

duration

is_duration

RFC 3339 appendix A

email

is_email

RFC 5321 4.1.2

idn-email

is_idn_email

RFC 6531 3.3

hostname

is_hostname

RFC 1123 2.1, RFC 5890 2.3.2.1

idn-hostname

is_idn_hostname

RFC 5890 2.3.2.3, UTS 46

ipv4

is_ipv4

RFC 2673 3.2

ipv6

is_ipv6

RFC 4291 2.2

uri

is_uri

RFC 3986 3

uri-reference

is_uri_reference

RFC 3986 4.1

iri

is_iri

RFC 3987 2.2

iri-reference

is_iri_reference

RFC 3987 2.2

uri-template

is_uri_template

RFC 6570 2

json-pointer

is_json_pointer

RFC 6901 3

relative-json-pointer

is_relative_json_pointer

draft-handrews-relative-json-pointer-01

uuid

is_uuid

RFC 9562 4

regex

is_regex

ECMA-262 Pattern, u mode

A first example

A program that checks a string of a few formats:

#include <webcpp/lexical.hpp>

#include <iostream>
#include <string_view>

namespace lexical = webcpp::lexical;

namespace {

void show(std::string_view format, std::string_view text, bool valid) {
    std::cout << format << ' ' << text << ": " << (valid ? "valid" : "invalid") << '\n';
}

}  // namespace

int main() {
    show("date-time", "1985-04-12T23:20:50.52Z", lexical::is_date_time("1985-04-12T23:20:50.52Z"));
    show("date", "2021-02-29", lexical::is_date("2021-02-29"));
    show("email", "joe.bloggs@[IPv6:::1]", lexical::is_email("joe.bloggs@[IPv6:::1]"));
    show("hostname", "xn--nxasmq6b.example", lexical::is_hostname("xn--nxasmq6b.example"));
    show("uri", "//example.com/p", lexical::is_uri("//example.com/p"));
    show("uri-reference", "//example.com/p", lexical::is_uri_reference("//example.com/p"));
    show("json-pointer", "/a~1b", lexical::is_json_pointer("/a~1b"));
}

It prints:

date-time 1985-04-12T23:20:50.52Z: valid
date 2021-02-29: invalid
email joe.bloggs@[IPv6:::1]: valid
hostname xn--nxasmq6b.example: valid
uri //example.com/p: invalid
uri-reference //example.com/p: valid
json-pointer /a~1b: valid

2021-02-29 is no date, 2021 being no leap year; //example.com/p is a URI reference and no URI, which needs a scheme.

Requirements

<webcpp/lexical.hpp> includes every format but regex, whose header <webcpp/lexical/regex.hpp> brings the Unicode tables of ECMA-262’s classes; the names are in the namespaces webcpp::lexical and webcpp::lexical::grammar. lexical is header-only C++20 and needs Boost 1.92 or newer: Boost.URL, whose compiled part webcpp builds from Boost’s sources for every target, and Boost.System; and webcpp’s configuration, config, and webcpp::idna, for internationalized names. A b2 project uses it through /webcpp/lexical//lexical, which brings all of them:

alias lexical
  : : : : <include>include <library>/boost//headers
    <library>/webcpp/config//config
    <library>/webcpp/idna//idna
    <library>/webcpp//boost_url ;

It runs natively, on wasm32-wasip2 and wasm32-wasip3, and on emscripten, with exceptions or without.

Dates and times

is_date_time, is_date and is_time check RFC 3339’s date-time, full-date and full-time (section 5.6), and is_duration its appendix A’s duration, exactly: ASCII digits only, a month 01 to 12, a day that exists in its month of the Gregorian calendar (February 29 in a year divisible by 4, and not by 100 unless by 400), hours 00 to 23, minutes 00 to 59, any number of fraction digits, and an offset of Z or +hh:mm/-hh:mm. T and Z match in either case, as RFC 3339’s note says; so do the letters of a duration, which are ABNF’s quoted strings (RFC 5234 2.3).

Second 60 is a leap second. RFC 3339 allows it when the time, moved to UTC by its offset, is 23:59, and is_time(s) follows it, as drafts 7 to 2020-12 of JSON Schema test. JSON Schema v1 says a time must not hold one: is_time(s, leap_seconds::refused) is its time. is_date_time allows them in every draft, v1 included, as the suite’s date-time tests say. The date of a leap second is not checked against IERS’s list: no table, the same answer every year.

A duration’s numbers are never converted, so any number of digits is valid and nothing overflows. P1Y2D is invalid: appendix A keeps years, months and days in that order and contiguous, and a week stands alone.

Identifiers

is_uuid checks RFC 9562’s string form of a UUID: 32 hexadecimal digits in either case, in groups of 8, 4, 4, 4 and 12 joined by hyphens. It checks the shape, not the version or the variant, as JSON Schema’s suite asks; braces and the urn:uuid: prefix are not part of the form.

is_json_pointer checks RFC 6901’s JSON Pointer: the empty string, or reference tokens each led by /, in which ~ is followed by 0 or 1. A token holds any other code point, NUL included, so the string is decoded as UTF-8 and must be well-formed.

is_relative_json_pointer checks a relative JSON Pointer: a non-negative integer without a leading zero, then # or a JSON Pointer. Draft 2020-12 and v1 of JSON Schema cite draft-bhutton-relative-json-pointer-00, which lets an index manipulation follow the integer, a sign and another non-negative integer without a leading zero (0+1, 0-0); drafts 7 and 2019-09 cite draft-handrews-relative-json-pointer-01, which has none. The draft’s grammar puts the manipulation only before a JSON Pointer, yet its own example 0-1# puts it before #; lexical accepts both, so 1-1# is valid too. The function takes that choice as its second argument: index_manipulation::allowed, the default, for 2020-12 and v1, and index_manipulation::refused for 7 and 2019-09. The rule takes it as relative_json_pointer_rule_t{index_manipulation::refused}. A sign that no integer follows (0+, 0-#), or one whose integer has a leading zero (0+01), is no manipulation, under either choice.

Addresses

is_ipv4 and is_ipv6 are Boost.URL’s own rules, boost::urls::ipv4_address_rule and boost::urls::ipv6_address_rule, which are also the rules a grammar of a program’s own composes. An IPv4 address is RFC 2673’s dotted quad, each part 0 to 255 without a leading zero (RFC 3986’s dec-octet), so 192.168.0.01 is invalid. An IPv6 address is one of RFC 4291 2.2’s three text forms, without a zone id or a prefix length.

Host names

is_hostname checks RFC 1123 2.1’s host names: labels of 1 to 63 letters, digits and hyphens, no hyphen first or last, joined by single dots, the name at most 253 octets, no empty label and so no trailing dot. A label that starts with xn--, in any case, is an A-label, an internationalized label in its ASCII form: lexical lowercases it, as UTS 46 maps an ASCII label, and asks webcpp::idna whether IDNA2008 accepts it, decoded, canonical and with the Bidi rule of the label alone, the verdict is_idn_hostname gives that label. Another label with -- in its third and fourth places, an R-LDH label such as ab--cd, is a valid host name, as RFC 1123 says, and not an internationalized one (RFC 5890 2.3.1).

lexical judges each A-label alone, a decision of its own, so RFC 5893’s Bidi rule across the labels of a name, section 2, is checked only by is_idn_hostname: 0a.xn--4db is a host name, while its U-label form, 0a. and a Hebrew letter, is no internationalized host name. The two formats differ here by that decision, as they do on R-LDH labels.

Internationalized host names

is_idn_hostname checks a host name that may hold any script, by UTS 46’s processing and IDNA2008’s validity, through idna’s to_ascii with its default options (idna’s strict profile). The name is mapped as UTS 46 says, so fullwidth digits, an ignored U+200B, a name not in NFC and the ideographic full stop U+3002 are valid where IDNA2008 accepts the result; then every label must be valid in IDNA2008, CONTEXTO and the Bidi rule across the labels included, and the name fit DNS’s lengths in its ASCII form: labels of at most 63 octets, the name at most 253, and no trailing dot. An R-LDH label such as ab--cd is a host name and not an internationalized one, and the Bidi rule spans an internationalized name’s labels where is_hostname judges each A-label alone. The two formats side by side:

String is_hostname is_idn_hostname

ab--cd.example

valid

invalid

0a.xn--4db

valid

invalid

0a. and HEBREW LETTER ALEF

invalid

invalid

xn--nxasmq6b.example

valid

valid

bücher.example

invalid

valid

A refused name’s reason is idna’s: the rule answers the idna::error that to_ascii returned, contexto for a middle dot outside l·l, bidi for 0a. and a Hebrew letter.

E-mail addresses

is_email checks RFC 5321 4.1.2’s Mailbox, the address of an SMTP envelope: a local part of atoms joined by dots or a quoted string, at most 64 octets, then @ and a host name, or an address literal, [192.0.2.1] or [IPv6:2001:db8::1], the tag in any case. RFC 5321’s other literals need a tag registered with IANA, and IPv6 is the only one, so [RFC-5322-domain-literal] is invalid. RFC 5322’s comments and display names are not part of a Mailbox. The whole Mailbox is at most 254 octets: RFC 5321 4.5.3.1.3 limits a path to 256 octets, its angle brackets included.

A literal’s addresses are judged by Boost.URL’s rules, RFC 3986’s IPv4 address and RFC 4291’s IPv6 address, rather than by RFC 5321 4.1.3’s own Snum and IPv6-comp: an IPv4 part with a leading zero, [001.2.3.4], which Snum allows, is invalid, and an IPv6 address whose :: stands for a single group, [IPv6:1:2:3:4:5:6::8], which IPv6-comp refuses, is valid.

Its address literal is a composition of Boost.URL’s combinators, tuple_rule, variant_rule, delim_rule and squelch, over Boost.URL’s ipv4_address_rule and ipv6_address_rule. The domain is not a variant_rule alternative: the rule looks for [ and then asks the literal’s rule or hostname_rule, so that the reason a host name was refused, idna’s for an A-label, reaches the caller; Boost.URL’s variant_rule answers mismatch when every alternative fails. A grammar of a program’s own composes lexical’s rules the same way (Composing rules).

is_idn_email checks RFC 6531’s internationalized Mailbox: the local part may hold any well-formed non-ASCII UTF-8, C1 controls and noncharacters included, and is not normalized; the domain is an internationalized host name or an address literal. A quoted pair stays ASCII. Its lengths are those of the Mailbox as written, in UTF-8: the local part at most 64 octets and the whole at most 254, the form an SMTPUTF8 envelope carries (RFC 6531 extends RFC 5321’s Mailbox without changing its limits, which RFC 5321 counts in octets), whatever the domain’s ASCII form measures; the domain also fits DNS’s lengths in its ASCII form, as is_idn_hostname asks.

URIs and IRIs

is_uri is Boost.URL’s parse_uri, RFC 3986’s URI, a scheme required and a fragment allowed, and is_uri_reference is Boost.URL’s parse_uri_reference, a URI or a relative reference, each with a guard of lexical’s, and so are their rules, uri_rule and uri_reference_rule. RFC 3986 4.2 gives a relative reference that has an authority a path-abempty, empty or led by /. Boost.URL 1.92 accepts //example.com:abc/p, read as host example.com, an empty port and the path abc/p, and //a@b@example.com/, read as user a, host b and the path @example.com/. With a scheme it refuses both, and stops the URI at the authority, so that inside a larger grammar Boost.URL’s rule matches http://a@b of http://a@b@example.com/. lexical refuses a relative reference whose path after an authority is neither empty nor led by /, and uri_reference_rule refuses both forms alike with error::invalid. The guard stays until a Boost release refuses those references itself.

RFC 3986 holds [ and ] only around an IP literal (3.2.2): a path, a query and a fragment hold them percent-encoded, as %5B and %5D. Boost.URL 1.92 refuses them in a path and a fragment, and accepts them in a query: http://a/?[x] is a URI for Boost.URL. lexical refuses a bracket in a query, in is_uri, is_uri_reference, is_iri, is_iri_reference and their rules alike, which give error::invalid for it inside a larger grammar; an IP literal, http://[::1]/, and %5B in a query stay valid. This guard stays until a Boost release refuses those brackets itself.

RFC 3986 3.5 gives a fragment pchar, / and ? alone: the first # starts the fragment, and a second is held only percent-encoded, as %23. Boost.URL 1.92 adds # to its fragment characters on purpose, so http://a/#x#y is a URI for Boost.URL, whose fragment is x#y. lexical refuses a # in a fragment, in the same four functions and their rules, which give error::invalid for it inside a larger grammar; #x%23y stays valid. This guard stays until a Boost release refuses it itself.

is_iri and is_iri_reference check RFC 3987’s IRI and IRI reference, which allow non-ASCII code points, by mapping the IRI to a URI as RFC 3987 3.1 says and handing it to Boost.URL. Each non-ASCII code point must be well-formed UTF-8 and in ucschar, or in iprivate within the query; the bidirectional formatting characters U+200E, U+200F and U+202A to U+202E are refused anywhere, as RFC 3987 4.1 requires, and so is any non-ASCII code point inside an IP literal: RFC 3987 takes its IP literal from RFC 3986, ASCII alone. Each octet of a non-ASCII code point then becomes %XX, which RFC 3986 allows everywhere else that RFC 3987 allows ucschar, so a non-ASCII code point in a scheme or a port is refused by Boost.URL. No IDNA is involved: an IRI’s host is a registered name of any ucschar.

The mapped URI needs contiguous storage. An ASCII IRI is parsed as it is, never copied; a non-ASCII one is mapped into a buffer of 1,024 octets on the stack, which holds any IRI of up to 341 octets; a longer one takes one buffer of its mapped length from the memory resource given to is_iri(s, resource), or from std::pmr::get_default_resource(). A resource of a program’s own, here a buffer that refuses to grow:

std::array<std::byte, 4096> storage{};
std::pmr::monotonic_buffer_resource resource(storage.data(), storage.size(),
                                             std::pmr::null_memory_resource());
std::string long_iri = "http://example.com/";
for (int i = 0; i < 300; ++i) {
    long_iri += "\303\251";  // 300 e with an acute accent: 1,819 octets mapped, past 1,024
}
std::cout << std::boolalpha;
std::cout << "short: "
          << lexical::is_iri("http://\306\222\303\270\303\270.example/\317\200", resource)
          << '\n';
std::cout << "long: " << lexical::is_iri(long_iri, resource) << '\n';
std::cout << "bidi: " << lexical::is_iri("http://a/\342\200\216", resource) << '\n';

It prints:

short: true
long: true
bidi: false

RFC 3986 3.2.2’s IP literal holds an IPv6 address or an IPvFuture, and no zone identifier. RFC 6874 added one, http://[fe80::1%25eth0]/, which Boost.URL 1.92 takes; RFC 9844 obsoleted RFC 6874 in 2025 and reverted its change to RFC 3986. lexical refuses a zone identifier, in the same four functions and their rules, which give error::invalid for it inside a larger grammar; %25 outside an IP literal stays valid. This guard stays until a Boost release refuses zone identifiers itself. Each guard, and the one verdict of Boost.URL that lexical keeps though RFC 3986 differs, is listed in Known deviations.

Boost.URL is a compiled library: a program that uses these formats links /webcpp//boost_url, which /webcpp/lexical//lexical brings. Measured on 2026-10-09 with Boost 1.92 and wasi-sdk 34, built with b2’s release flags (-O3, wasm exceptions) and stripped at link time, a wasm32-wasip2 program that calls is_uri is 85,625 bytes larger than an empty one.

Known deviations

lexical is faithful to RFC 3986, the reference JSON Schema names for these formats, where Boost.URL 1.92 is more liberal. lexical refuses four things Boost.URL accepts: a relative reference whose path after an authority is neither empty nor led by /; [ or ] in a query; # in a fragment; and a zone identifier in an IP literal. Its rules also refuse a reference that Boost.URL’s would stop at an authority before a path segment, as at the second @ of http://a@b@example.com/. The tests pin each of these verdicts.

lexical keeps one verdict of Boost.URL that RFC 3986 does not give, since it never loosens Boost.URL: an IPvFuture led by an uppercase V, http://[V1.x]/, is refused, though RFC 5234 makes ABNF’s quoted strings case-insensitive and RFC 3986’s "v" matches it; http://[v1.x]/ is valid.

These are all the differences found on 2026-10-09, when the verdicts of Boost.URL 1.92 and of lexical were compared with a transcription of RFC 3986’s ABNF, on inputs that put every ASCII character but NUL and line feed into each part of a reference, and on references generated at random.

URI templates

is_uri_template checks RFC 6570’s URI Template of any level, without expanding it: literals, and expressions in braces with an optional operator and comma-separated variables, each with a prefix length of 1 to 9999 or an explode *. Erratum 6937, verified in 2022, makes the apostrophe a literal, as JSON Schema’s suite expects. The operators RFC 6570 reserves for future use, the equals sign, the comma, the exclamation mark, the at sign and the vertical bar, are accepted, as its ABNF accepts them. A literal may be a ucschar or iprivate code point, so a template of an IRI is valid too.

Regular expressions

is_regex answers whether a string is a regular expression, JSON Schema’s regex: ECMA-262’s Pattern in u mode, with ECMAScript 2025’s modifiers, (?i:...), and its duplicate group names, (?<y>...)|(?<y>...). It is declared in <webcpp/lexical/regex.hpp>, the one header of lexical that brings the Unicode tables of ECMA-262’s classes: <webcpp/lexical.hpp> leaves it out, and a program that never validates a pattern does not compile them.

for (const std::string_view pattern : {
         R"(^\p{Letter}+$)",
         R"((?<year>\d{4})-(?<month>\d{2}))",
         R"((?<=\$\d*)[0-9])",
         "^(abc]",
         R"(\Aabc\z)",
         "(?i)abc",
         "(?i:abc)",
     }) {
    std::cout << pattern << " -> " << (webcpp::lexical::is_regex(pattern) ? "valid" : "invalid")
              << '\n';
}

It prints:

^\p{Letter}+$ -> valid
(?<year>\d{4})-(?<month>\d{2}) -> valid
(?<=\$\d*)[0-9] -> valid
^(abc] -> invalid
\Aabc\z -> invalid
(?i)abc -> invalid
(?i:abc) -> valid

How a pattern is judged

A pattern is valid when ECMA-262 accepts it as a Pattern in u mode, early errors included: when new RegExp(pattern, "u") does not throw a SyntaxError. lexical’s validator reads the pattern as that grammar reads it, and refuses what the grammar refuses there: Annex B’s escapes and classes, \a, \- outside a class, \8, an octal escape; Perl’s \A, \Z and \z; a group of another dialect, (?P<n>...), (?#...), a leading (?i); a property ECMA-262 does not name, as \p{letter} or \p{Script=Foo}; a backreference to a group the pattern does not hold; two groups of one name that a match could both hold. A pattern that is not well-formed UTF-8 is refused, and a NUL is a literal like any other code point.

Valid means that the syntax and the early errors accept the pattern, and nothing more. is_regex compiles no pattern and runs none: it says nothing of how long a matcher takes on a valid pattern, nor of the limits an engine sets on the program it builds from one. The property names \p{...} accepts are Unicode 17.0.0’s, read from the generated tables (The Unicode tables): no ICU and no std::locale.

ECMA-262 allows a lookbehind of any width, (?<=\$\d*), and so does is_regex: it never measures a lookbehind, since it compiles nothing.

is_regex has no rule: a pattern has no grammar of its own inside a longer input, so is_regex judges a whole string.

Bounds

Two bounds keep a hostile pattern, which JSON Schema meets in an instance, from taking the stack or the time of a program. Past one of them, is_regex answers false without judging the pattern, though ECMA-262 may allow it:

Constant Value Bounds

max_regex_length

65,536

a pattern’s bytes, refused before they are read: the validator reads parts of a pattern again, so a bound on its length bounds its time

max_regex_nesting

32

groups, lookarounds and modifiers one inside the other: the validator descends into a group by calling itself, so the nesting bounds its stack

What it allocates

is_regex allocates nothing, on any target. It takes no memory resource: its reader works on the pattern’s own bytes and keeps its state on the stack, to the depth max_regex_nesting allows. So <webcpp/lexical/regex.hpp> keeps lexical’s rule that a validator allocates nothing, as the allocation table says (Allocation).

Compared with V8

A test of lexical, regex_v8, compares is_regex with V8 on 24,297 patterns, of which 16,475 are valid by V8’s verdict. The patterns are drawn at random, from seeds 2 to 9 of Python’s random.Random, from a grammar of atoms, groups of every kind, backreferences, assertions, quantifiers and defects; every name \p{...} accepts, under each of its forms, and the same names misspelt; every pattern the JSON-Schema-Test-Suite’s schemas hold, in pattern and patternProperties, and every string of its tests of the format regex; and the cases the grammar does not draw: ECMAScript 2025’s modifiers and duplicate group names, lookbehinds of every width, escapes of every kind, and classes and quantifiers at their edges. V8’s verdict is new RegExp(pattern, "u") throwing a SyntaxError or not, written by the oracle of test/oracle, which runs the pinned Node, 26.7.0, whose V8 is 14.6 and whose Unicode is 17.0. On each of these patterns the verdict of is_regex is V8’s, on every target (Testing).

To check the committed verdicts against the pinned Node, and is_regex against them:

b2 -a libs/lexical/test/oracle//oracle
b2 -a libs/lexical/test//regex_v8

After a change to the patterns, update-expected writes the verdicts again:

b2 libs/lexical/test/oracle//update-expected

The Unicode tables

tools/generate_tables.py, in lexical’s repository, writes <webcpp/lexical/detail/regex_tables.hpp> from Unicode’s data files of version 17.0.0, pinned in tools/unicode.json, each by its URL under https://www.unicode.org/Public/17.0.0/ and its SHA-256: a file its cache lacks is downloaded, every file is hashed, and a digest that differs stops it before it writes anything. Before it writes, it checks what the validator and the traits assume of the data: General_Category has 30 values that are no group; every value a file holds is one PropertyValueAliases.txt names; each of ECMA-262’s binary properties holds a code point; every name is ASCII and names one value; the classes fit the traits’ mask; and no simple case folding orbit holds more than four code points. A script that no code point has, Katakana_Or_Hiragana, gets no name, as ECMA-262’s table of the values of Script leaves it out. tools/generate_patterns.py writes the patterns regex_v8 reads, the same way.

The own lane generated runs both generators again, into the build directory, and compares each output with the committed file, byte for byte; update-generated writes them in place, the one writer of the generated files. The ordinary lanes never run them, since a generator may need the network. The data are Unicode’s, under the Unicode License v3, whose notice is in the appendix "Unicode data".

b2 -a libs/lexical/test//generated
b2 libs/lexical/test//update-generated

Composing rules

Every format but regex is also a Boost.URL rule, in webcpp::lexical::grammar: an object whose type <name>_rule_t is public, so a rule that takes a parameter, time_rule_t{leap_seconds::refused} or iri_rule_t{&resource}, is spelled without a factory. A rule yields the text it matched, as a grammar::text; uri_rule and uri_reference_rule yield Boost.URL’s url_view. ipv4 and ipv6 are Boost.URL’s own rules, boost::urls::ipv4_address_rule and ipv6_address_rule.

A rule composes with Boost.URL’s combinators, tuple_rule, variant_rule, optional_rule, range_rule, into a grammar of a program’s own: an internationalized host and an optional port, bücher.example:8080, are a tuple_rule of idn_hostname_rule and an optional_rule of another tuple_rule, a colon, squelch(delim_rule(':')), then unsigned_rule<std::uint16_t>, which yields the host’s text and the port’s number; a rule that refuses says whose rule it was, through its error’s category:

constexpr auto idn_host_port = urlg::tuple_rule(
    fg::idn_hostname_rule,
    urlg::optional_rule(urlg::tuple_rule(urlg::squelch(urlg::delim_rule(':')),
                                         urlg::unsigned_rule<std::uint16_t>{})));

for (const std::string_view text : {
         "b\303\274cher.example:8080",
         "example.com",
         "a\302\267b.example:80",
         "example.com:99999",
     }) {
    const auto parsed = urlg::parse(text, idn_host_port);
    if (parsed.has_error()) {
        const boost::system::error_code error = parsed.error();
        std::cout << text << " -> refused by " << error.category().name() << '\n';
        continue;
    }
    const auto& [host, port] = *parsed;
    std::cout << text << " -> host " << host;
    if (port) {
        std::cout << ", port " << *port;
    }
    std::cout << '\n';
}

It prints:

bücher.example:8080 -> host bücher.example, port 8080
example.com -> host example.com
a·b.example:80 -> refused by webcpp.idna
example.com:99999 -> refused by boost.url.grammar

Where a rule stops inside a longer input is part of its contract:

Rule Stops

the ASCII rules

where their grammar does

hostname_rule

at the first character outside letters, digits, - and .

idn_hostname_rule

at the first ASCII character outside letters, digits, - and .; every non-ASCII byte belongs to the name, since UTS 46 may map it

idna_label_rule

as idn_hostname_rule, and also at ., U+3002, U+FF0E and U+FF61

email_rule, idn_email_rule

where the domain or the address literal does

iri_rule, iri_reference_rule

at the first byte no IRI holds: an ASCII control, the space, the double quote, the less-than and greater-than signs, the backslash, the circumflex, the grave accent, a brace or the vertical bar

uri_template_rule

at the first byte that is neither a literal nor the start of an expression; an expression or a percent sign that starts and breaks makes it fail

json_pointer_rule

at the end of the input: a token holds every other character

Each validates the text it took and fails as a whole rather than match a shorter prefix, and a rule that fails leaves its input where it was. idna_label_rule is a terminal of lexical’s own that asks webcpp::idna for its verdict, so a grammar joins labels with separators of its own. It judges each label alone: a grammar that joins labels into a name checks what spans the name itself, RFC 5893’s Bidi rule across its labels (section 2) and its 253 octets in the ASCII form, or hands the whole name to idn_hostname_rule, which checks both. Joined by a grammar’s own dots, 0a. and a label of a Hebrew letter are two valid labels and no valid name.

An internationalized rule’s error is idna’s, the idna::error that refused the name (Errors), but only where nothing discards it: Boost.URL’s variant_rule answers mismatch when every alternative fails, whatever each one said. lexical’s e-mail rules look for [ themselves rather than make the domain an alternative, so that idna’s reason reaches the caller; a grammar that needs that reason does the same.

Two facts of Boost.URL shape a composition. A range_rule over a rule larger than 128 bytes keeps it in a pool of Boost.URL’s own, which allocates: pass such a rule through boost::urls::grammar::ref, as lexical does. And parse of a whole string fails with error::leftover when the rule stops early, which is how a validator refuses trailing input.

Errors

A validator answers bool: whether the string is in the format. A rule answers a grammar::result, the text it matched or why it refused, and lexical has no error category of its own:

  • boost::urls::grammar::error::mismatch: no text of the format starts where the rule was asked to match, so a variant_rule tries its next alternative;

  • boost::urls::grammar::error::invalid: the format’s text started there and broke one of its constraints, a month 13 or a leap second at the wrong minute;

  • boost::urls::grammar::error::leftover, from parse on a whole string: the format’s text ended before the string did;

  • an error of webcpp::idna, from the rules of internationalized names: the reason UTS 46 or IDNA2008 refused the name.

lexical raises no exception of its own, and builds without exceptions. WEBCPP_LEXICAL_NO_EXCEPTIONS, defined without exceptions or by the developer, changes nothing in it: it exists so that a program can set it for every webcpp library alike. Only an allocation can throw, std::bad_alloc from the default resource or the global operator new: an IRI function’s resource (Allocation). is_regex allocates nothing and is noexcept: a pattern it refuses is answered false, never an exception (How a pattern is judged).

Allocation

A validator and a rule allocate nothing, but the IRI functions, which allocate only through a memory resource, where the table below says:

Function or rule Allocates

every validator and rule but those below

never: Boost.URL’s views and lexical’s rules hold no storage, idna works in fixed storage, a range_rule keeps its rule inline, and is_regex reads its pattern in place (What it allocates)

is_iri, is_iri_reference, iri_rule, iri_reference_rule

never for an ASCII IRI or one whose mapped URI fits 1,024 octets; beyond, one buffer of the mapped length, from the resource given, else std::pmr::get_default_resource()

A test counts every call of the global operator new its program makes, over every validator and rule, on long and hostile inputs, a megabyte of each format among them, and finds none but those the table names, on every target.

The stack holds the rest: an IRI’s buffer of 1,024 octets, idna’s working storage of a label, and the descent of is_regex into a pattern’s groups, which max_regex_nesting bounds (Bounds).

The JSON Schema suite

The official JSON-Schema-Test-Suite holds, for each draft, the format tests of optional/format/, and for v1 those of format/, which v1 requires. A program of lexical’s tests runs each string test against the validator of its format, on every target lexical builds for: is_time(s, leap_seconds::refused) for v1’s time, is_relative_json_pointer(s, index_manipulation::refused) for the relative JSON Pointers of drafts 7 and 2019-09, and the same call in every other draft. A test whose instance is not a string checks json_schema’s rule that a format applies to strings only, and the unknown format json_schema’s handling of a format it does not know: neither is a question for a validator.

Dialect String tests Passing

draft 4

248

248

draft 6

348

348

draft 7

684

684

draft 2019-09

753

753

draft 2020-12

753

753

v1

754

754

all

3,540

3,540

A test expected to fail is listed in its dialect’s known-failure list with its reason; the only reason allowed is a difference of Unicode versions, since idna’s tables are those of Unicode 17.0.0. Every list is empty. The program fails on a failure no list names, and on a listed test that passes.

The suite’s tests that need a regular expression to match, its pattern and patternProperties keywords, are json_schema’s: lexical does not search. Every pattern they hold is valid for is_regex, as for V8 (Compared with V8).

Guarantees

  1. For every format but regex, is_x(s) and parse(s, x_rule) give the same answer for every string.

  2. No function or rule allocates but those the allocation table names: the IRI functions, only through a memory resource.

  3. Nothing throws but an allocation that fails.

  4. Every function refuses a string that is not well-formed UTF-8.

  5. A rule that fails leaves its iterator where it was.

  6. is_regex answers every byte string, a value, and never throws: a pattern past one of its bounds is answered false before it can take more stack or time than the bounds allow.

  7. The Unicode tables and the patterns compared with V8 are what their generators write from the pinned files, byte for byte.

Testing

lexical is tested natively, on wasm32-wasip2 and wasm32-wasip3 under wasmtime, and on emscripten under node, from the superproject’s root:

b2 libs/lexical/test libs/lexical/example
b2 toolset=clang-wasip2 testing.launcher=wasmtime libs/lexical/test libs/lexical/example
b2 toolset=clang-wasip3 testing.launcher=wasmtime libs/lexical/test libs/lexical/example
b2 toolset=emscripten libs/lexical/test libs/lexical/example

Its 41 tests are written with Boost’s lightweight_test: each public header compiled alone, the configuration macro compiled three times, the terminals every format shares, the cases of each format, the rules composed with Boost.URL’s combinators, what each validator allocates, the run of the JSON Schema suite’s 3,540 string tests (The JSON Schema suite), and the comparison of is_regex with V8 on 24,297 patterns (Compared with V8). Its 4 examples run on every target too, each compared with the output the page shows.

The suite’s files, and lexical’s known-failure lists and patterns, reach a WASI program through webcpp.data, the directory wasmtime is given with --dir, and an emscripten program through node’s file system.

Two own lanes run apart from the targets’, each natively. oracle asks V8, the pinned Node’s, for its verdict on every pattern regex_v8 reads, and compares them with the committed ones, which only update-expected writes; generated runs the generators of the Unicode tables and of the patterns again, and compares their outputs with the committed files (The Unicode tables):

b2 -a libs/lexical/test/oracle//oracle
b2 -a libs/lexical/test//generated
b2 libs/lexical/test/oracle//update-expected

The test matrix of every lane is published at https://webcpporg.github.io/report/.

Reference

Every name lexical declares, in the namespaces webcpp::lexical and webcpp::lexical::grammar, from the Doc Comments of its headers: each function, rule, type and constant with its declaration, what it does and how it fails. The other sections teach the library; this one is where a name is looked up.

Macros

Name Description

WEBCPP_LEXICAL_NO_EXCEPTIONS

Defined when lexical is built without exceptions: when webcpp has none (the compiler has none, or the developer defined WEBCPP_NO_EXCEPTIONS for every webcpp library), or when the developer defines it alone, to disable lexical’s in a build that has them.

webcpp::lexical

Namespaces

Name

grammar

Enums

Name Description

index_manipulation

Whether a relative JSON Pointer may move its array index with a sign and an integer.

leap_seconds

Whether a time may hold second 60, a leap second.

Functions

Name Description

is_date

Whether s is an RFC 3339 full-date, JSON Schema’s date.

is_date_time

Whether s is an RFC 3339 date-time, JSON Schema’s date-time.

is_duration

Whether s is an RFC 3339 appendix A duration, JSON Schema’s duration.

is_email

Whether s is an RFC 5321 Mailbox, JSON Schema’s email.

is_hostname

Whether s is a host name, JSON Schema’s hostname.

is_idn_email

Whether s is an RFC 6531 internationalized Mailbox, JSON Schema’s idn-email.

is_idn_hostname

Whether s is an internationalized host name, JSON Schema’s idn-hostname.

is_ipv4

Whether s is an IPv4 address in dotted-quad form, JSON Schema’s ipv4.

is_ipv6

Whether s is an IPv6 address, JSON Schema’s ipv6.

is_iri

is_iri overloads

is_iri_reference

is_iri_reference overloads

is_json_pointer

Whether s is an RFC 6901 JSON Pointer, JSON Schema’s json-pointer.

is_regex

Whether pattern is a regular expression, JSON Schema’s regex: an ECMA-262 Pattern in u mode, with ECMAScript 2025’s modifiers and duplicate group names.

is_relative_json_pointer

Whether s is a relative JSON Pointer, JSON Schema’s relative-json-pointer.

is_time

Whether s is an RFC 3339 full-time, JSON Schema’s time.

is_uri

Whether s is an RFC 3986 URI, JSON Schema’s uri: a scheme, and a fragment allowed.

is_uri_reference

Whether s is an RFC 3986 URI-reference, JSON Schema’s uri-reference: a URI or a relative reference.

is_uri_template

Whether s is an RFC 6570 URI Template of any level, JSON Schema’s uri-template.

is_uuid

Whether s is a UUID in RFC 9562’s string form, JSON Schema’s uuid.

Variables

Name Description

max_regex_length

The longest pattern is_regex judges, in bytes: 65,536.

max_regex_nesting

The deepest a pattern’s groups may nest for is_regex to judge it: 32 levels of groups, lookarounds and modifiers, one inside the other.

webcpp::lexical::grammar

Types

Name Description

date_rule_t

The rule of RFC 3339 5.6’s full-date: a year, a month and a day that exists.

date_time_rule_t

The rule of RFC 3339 5.6’s date-time: a full-date, T in either case, a full-time.

duration_rule_t

The rule of RFC 3339 appendix A’s duration, exactly: P, then a date part, a time part, or weeks alone.

email_rule_t

The rule of RFC 5321 4.1.2’s Mailbox: a local part, the at sign, and a domain or an address literal.

hostname_rule_t

The rule of a host name: RFC 1123 2.1’s labels, with each A-label judged as idn-hostname judges it.

idn_email_rule_t

The rule of RFC 6531 3.3’s internationalized Mailbox: email_rule with UTF-8 in the local part and an internationalized domain.

idn_hostname_rule_t

The rule of an internationalized host name: UTS 46’s processing and IDNA2008’s validity, through webcpp::idna::to_ascii.

idna_label_rule_t

The rule of one label of an internationalized host name, a terminal of lexical’s own that asks webcpp::idna for its verdict.

iri_reference_rule_t

The rule of RFC 3987 2.2’s IRI-reference: an IRI or a relative reference, checked and mapped as iri_rule_t does, then parsed by uri_reference_rule.

iri_rule_t

The rule of RFC 3987 2.2’s IRI: an IRI checked code point by code point, mapped to a URI, and parsed by uri_rule.

json_pointer_rule_t

The rule of RFC 6901 section 3’s json-pointer: zero or more reference tokens, each led by /.

relative_json_pointer_rule_t

The rule of the relative-json-pointer of draft-bhutton-relative-json-pointer-00 section 3 and draft-handrews-relative-json-pointer-01 section 3: a non-negative integer, an optional index manipulation, then # or a json-pointer.

time_rule_t

The rule of RFC 3339 5.6’s full-time: a time of day and its offset from UTC.

uri_reference_rule_t

The rule of RFC 3986 4.1’s URI-reference: Boost.URL’s uri_reference_rule, then a guard.

uri_rule_t

The rule of RFC 3986 3’s URI: Boost.URL’s uri_rule, then a guard.

uri_template_rule_t

The rule of RFC 6570 section 2’s URI-Template, with erratum 6937: literals and expressions.

uuid_rule_t

The rule of RFC 9562 section 4’s UUID: 32 hexadecimal digits in groups of 8, 4, 4, 4 and 12, joined by hyphens.

Type Aliases

Name Description

result

What a rule returns: the value it matched, or the error that refused its input.

text

The text a rule of lexical matched, a view of its input.

Variables

Name Description

date_rule

The rule of RFC 3339’s full-date.

date_time_rule

The rule of RFC 3339’s date-time.

duration_rule

The rule of RFC 3339 appendix A’s duration.

email_rule

The rule of RFC 5321’s Mailbox.

hostname_rule

The rule of a host name.

idn_email_rule

The rule of RFC 6531’s Mailbox.

idn_hostname_rule

The rule of an internationalized host name.

idna_label_rule

The rule of one internationalized label.

iri_reference_rule

The rule of RFC 3987’s IRI-reference, with the default resource.

iri_rule

The rule of RFC 3987’s IRI, with the default resource; iri_rule_t{&resource} names another.

json_pointer_rule

The rule of RFC 6901’s json-pointer.

relative_json_pointer_rule

The rule of the relative JSON pointer, index manipulation allowed; relative_json_pointer_rule_t{index_manipulation::refused} refuses it.

time_rule

The rule of RFC 3339’s full-time, leap seconds allowed; time_rule_t{leap_seconds::refused} refuses them.

uri_reference_rule

The rule of RFC 3986’s URI-reference, guarded.

uri_rule

The rule of RFC 3986’s URI, guarded.

uri_template_rule

The rule of RFC 6570’s URI-Template.

uuid_rule

The rule of RFC 9562’s UUID.

webcpp::lexical::grammar::result

What a rule returns: the value it matched, or the error that refused its input.

Synopsis

template<class T>
using result = boost::system::result<T>;

Template Parameters

Name Description

T

The rule’s value type.

See Also

"Errors", in the guide.

webcpp::lexical::grammar::text

The text a rule of lexical matched, a view of its input.

Synopsis

using text = boost::core::string_view;

Description

It is Boost.URL’s text type, which converts to std::string_view.

See Also

"Composing rules", in the guide.

webcpp::lexical::grammar::date_rule_t

The rule of RFC 3339 5.6’s full-date: a year, a month and a day that exists.

Synopsis

struct date_rule_t;

Description

4DIGIT "-" 2DIGIT "-" 2DIGIT, ASCII digits only; the month 01 to 12, the day 01 to the month’s length, February 29 only in a Gregorian leap year. It stops after the day.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a full-date at it.

See Also

"Dates and times", in the guide.

webcpp::lexical::grammar::date_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::date_rule_t::parse

Matches a full-date at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no digit starts it; error::invalid when it started and broke a constraint (Boost.URL’s grammar errors).

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::date_time_rule_t

The rule of RFC 3339 5.6’s date-time: a full-date, T in either case, a full-time.

Synopsis

struct date_time_rule_t;

Description

Leap seconds are allowed, as every draft of JSON Schema, v1 included, says of date-time. It stops after the offset.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a date-time at it.

See Also

"Dates and times", in the guide.

webcpp::lexical::grammar::date_time_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::date_time_rule_t::parse

Matches a date-time at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no digit starts it; error::invalid when it started and broke a constraint.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::duration_rule_t

The rule of RFC 3339 appendix A’s duration, exactly: P, then a date part, a time part, or weeks alone.

Synopsis

struct duration_rule_t;

Description

Years, months and days in that order and each optional after the first given, then T and hours, minutes and seconds likewise; or a number of weeks alone. No fraction, no sign, any number of digits. The designators are letters of the ABNF, so either case matches (RFC 5234 2.3). It stops where the ABNF does: after P1Y, the 2D of P1Y2D is not its.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a duration at it.

See Also

"Dates and times", in the guide.

webcpp::lexical::grammar::duration_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::duration_rule_t::parse

Matches a duration at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no P starts it; error::invalid when it started and broke the grammar.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::email_rule_t

The rule of RFC 5321 4.1.2’s Mailbox: a local part, the at sign, and a domain or an address literal.

Synopsis

Declared in <webcpp/lexical/email.hpp>

struct email_rule_t;

Description

The local part is a Dot-string of atoms (RFC 5322’s atext) or a Quoted-string, at most 64 octets. The domain is hostname_rule, A-labels included, whose error, idna’s for an A-label, the rule returns as it is; an address literal is [] and an IPv4 address ], or [IPv6:] and an IPv6 address ], the tag in any case: RFC 5321’s General-address-literal needs a tag registered with IANA, and IPv6 is the only one. The whole Mailbox is at most 254 octets, RFC 5321 4.5.3.1.3’s path of 256 less its angle brackets. It stops where the domain or the literal does.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a Mailbox at it.

See Also

"Addresses", in the guide.

webcpp::lexical::grammar::email_rule_t::value_type

The rule yields the text it matched.

Synopsis

Declared in <webcpp/lexical/email.hpp>

using value_type = text;

webcpp::lexical::grammar::email_rule_t::parse

Matches a Mailbox at it.

Synopsis

Declared in <webcpp/lexical/email.hpp>

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no local part starts at it; error::invalid when the rest breaks the grammar or the Mailbox is longer than 254 octets; idna’s error when idna refuses an A-label of the domain.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::hostname_rule_t

The rule of a host name: RFC 1123 2.1’s labels, with each A-label judged as idn-hostname judges it.

Synopsis

struct hostname_rule_t;

Description

ASCII only. Labels of 1 to 63 letters, digits and hyphens, no hyphen first or last, joined by single dots, no dot first or last; the name at most 253 octets. A label that starts with xn-- in any case is an A-label: lowercased and checked by webcpp::idna::check_label (RFC 5891 5.4: decoded, IDNA2008, the Bidi rule of the label alone, canonical). Another label with two hyphens in its third and fourth places is valid, as RFC 1123 says. It takes the run of letters, digits, hyphens and dots at its start and validates it whole, never a shorter prefix.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a host name at it.

See Also

"Addresses", in the guide.

webcpp::lexical::grammar::hostname_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::hostname_rule_t::parse

Matches a host name at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no letter, digit, hyphen or dot is at it; error::invalid when the run is no host name; idna’s error when idna refuses an A-label.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::idn_email_rule_t

The rule of RFC 6531 3.3’s internationalized Mailbox: email_rule with UTF-8 in the local part and an internationalized domain.

Synopsis

Declared in <webcpp/lexical/email.hpp>

struct idn_email_rule_t;

Description

atext and qtextSMTP also hold any well-formed non-ASCII UTF-8 sequence (RFC 6532 3.1, C1 controls and noncharacters included), quoted-pairSMTP stays ASCII, the local part is at most 64 octets and is not normalized; the domain is idn_hostname_rule, whose error, idna’s, the rule returns as it is, or an address literal. The whole Mailbox is at most 254 octets as written, in UTF-8, the form an SMTPUTF8 envelope carries, whatever its domain’s ASCII form measures.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches an internationalized Mailbox at it.

See Also

"Addresses", in the guide.

webcpp::lexical::grammar::idn_email_rule_t::value_type

The rule yields the text it matched.

Synopsis

Declared in <webcpp/lexical/email.hpp>

using value_type = text;

webcpp::lexical::grammar::idn_email_rule_t::parse

Matches an internationalized Mailbox at it.

Synopsis

Declared in <webcpp/lexical/email.hpp>

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no local part starts at it; error::invalid when the rest breaks the grammar or the Mailbox is longer than 254 octets; idna’s error when idna refuses the domain.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::idn_hostname_rule_t

The rule of an internationalized host name: UTS 46’s processing and IDNA2008’s validity, through webcpp::idna::to_ascii.

Synopsis

struct idn_hostname_rule_t;

Description

idna’s default options: UTS 46’s mapping, nontransitional, every check, the DNS lengths, and IDNA2008’s stricter validity (CONTEXTO, NV8 and XV8 refused). So fullwidth digits, an ignored U+200B, a name not in NFC and the dots U+3002, U+FF0E and U+FF61 are valid where IDNA2008 allows the result. It takes the run up to the first ASCII byte outside letters, digits, - and . (every non-ASCII byte belongs to it, since UTS 46 may map it) and validates it whole.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches an internationalized host name at it.

See Also

"Addresses", in the guide.

webcpp::lexical::grammar::idn_hostname_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::idn_hostname_rule_t::parse

Matches an internationalized host name at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no name’s byte is at it; else idna’s error when idna refuses the run.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::idna_label_rule_t

The rule of one label of an internationalized host name, a terminal of lexical’s own that asks webcpp::idna for its verdict.

Synopsis

struct idna_label_rule_t;

Description

The run stops also at ., U+3002, U+FF0E and U+FF61, so a grammar composes labels with its own separators. The label is checked as idn_hostname_rule checks a name of one label, alone: a grammar that joins labels into a name checks what spans the name itself, RFC 5893’s Bidi rule across its labels (section 2) and its 253 octets in the ASCII form, or hands the whole name to idn_hostname_rule, which checks both.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches one internationalized label at it.

See Also

"Composing rules", in the guide.

webcpp::lexical::grammar::idna_label_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::idna_label_rule_t::parse

Matches one internationalized label at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no label starts at it; else idna’s error.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::iri_reference_rule_t

The rule of RFC 3987 2.2’s IRI-reference: an IRI or a relative reference, checked and mapped as iri_rule_t does, then parsed by uri_reference_rule.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

struct iri_reference_rule_t;

Description

The empty reference is valid, so the rule matches empty text where a byte no IRI holds is at the start, and never answers mismatch.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches an IRI-reference at it.

Data Members

Name Description

resource

The resource of a mapped URI longer than 1,024 octets; null for the default resource.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::grammar::iri_reference_rule_t::value_type

The rule yields the text it matched.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

using value_type = text;

webcpp::lexical::grammar::iri_reference_rule_t::parse

Matches an IRI-reference at it.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const;

Exceptions

Name Thrown on

std::bad_alloc

When the resource cannot allocate, or what it throws instead.

Return Value

The text matched; error::invalid when the run is no IRI reference.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::iri_reference_rule_t::resource

The resource of a mapped URI longer than 1,024 octets; null for the default resource.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

std::pmr::memory_resource* resource = nullptr;

webcpp::lexical::grammar::iri_rule_t

The rule of RFC 3987 2.2’s IRI: an IRI checked code point by code point, mapped to a URI, and parsed by uri_rule.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

struct iri_rule_t;

Description

A non-ASCII code point must be well-formed UTF-8 and in ucschar, or iprivate in the query; the bidirectional formatting characters are refused anywhere (RFC 3987 4.1), and so is any non-ASCII code point inside an IP literal, which RFC 3987 takes from RFC 3986, ASCII alone. Each octet of a non-ASCII code point maps to %XX, which RFC 3986 allows elsewhere where RFC 3987 allows ucschar, so a non-ASCII scheme or port is refused too. The rule takes the run up to the first byte no IRI holds (an ASCII control, the space, the double quote, the less-than and greater-than signs, the backslash, the circumflex, the grave accent, the braces or the vertical bar) and validates it whole; it never matches a shorter prefix.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches an IRI at it.

Data Members

Name Description

resource

The resource of a mapped URI longer than 1,024 octets; null for the default resource.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::grammar::iri_rule_t::value_type

The rule yields the text it matched.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

using value_type = text;

webcpp::lexical::grammar::iri_rule_t::parse

Matches an IRI at it.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const;

Exceptions

Name Thrown on

std::bad_alloc

When the resource cannot allocate, or what it throws instead.

Return Value

The text matched; error::mismatch when a byte no IRI holds is at it; error::invalid when the run is no IRI.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::iri_rule_t::resource

The resource of a mapped URI longer than 1,024 octets; null for the default resource.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

std::pmr::memory_resource* resource = nullptr;

webcpp::lexical::grammar::json_pointer_rule_t

The rule of RFC 6901 section 3’s json-pointer: zero or more reference tokens, each led by /.

Synopsis

struct json_pointer_rule_t;

Description

A token holds any code point but / and ~, NUL included, and ~0 or ~1; the input is decoded as UTF-8 and must be well-formed. The empty pointer is valid, so the rule matches empty text where no / starts the input, and never answers mismatch. Once a / starts it, inside a larger grammar it runs to the end of the input, since a token holds every character the grammar could continue with.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a json-pointer at it.

See Also

"Identifiers", in the guide.

webcpp::lexical::grammar::json_pointer_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::json_pointer_rule_t::parse

Matches a json-pointer at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched, empty when no / starts the input; error::invalid when a token holds a ~ that is not ~0 or ~1, or ill-formed UTF-8.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::relative_json_pointer_rule_t

The rule of the relative-json-pointer of draft-bhutton-relative-json-pointer-00 section 3 and draft-handrews-relative-json-pointer-01 section 3: a non-negative integer, an optional index manipulation, then # or a json-pointer.

Synopsis

struct relative_json_pointer_rule_t;

Description

The integer is 0 or starts with 1 to 9, so 01 is refused. The index manipulation, a sign and such an integer (0+1, 0-0), is draft-bhutton-00’s, which draft 2020-12 and v1 cite; it may come before the # too (1-1#), as the draft’s example 0-1# does though its ABNF omits it. Drafts 7 and 2019-09 cite draft-handrews-01, which has none, so index_manipulation::refused refuses it. It stops after the #, or where its json-pointer does; where a sign is not a valid manipulation, it stops before the sign.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a relative-json-pointer at it.

Data Members

Name Description

manipulation

Whether an index manipulation may follow the integer.

See Also

"Identifiers", in the guide.

webcpp::lexical::grammar::relative_json_pointer_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::relative_json_pointer_rule_t::parse

Matches a relative-json-pointer at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no digit starts it; error::invalid when its json-pointer breaks a rule.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::relative_json_pointer_rule_t::manipulation

Whether an index manipulation may follow the integer.

Synopsis

index_manipulation manipulation = index_manipulation::allowed;

webcpp::lexical::grammar::time_rule_t

The rule of RFC 3339 5.6’s full-time: a time of day and its offset from UTC.

Synopsis

struct time_rule_t;

Description

Hours 00 to 23, minutes 00 to 59, seconds 00 to 60 with any number of fraction digits, then Z or +hh:mm or -hh:mm (hh 00 to 23, mm 00 to 59); Z in either case. Second 60 is valid only under leap_seconds::allowed and when the time moved to UTC is 23:59. It stops after the offset.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a full-time at it.

Data Members

Name Description

leap

Whether second 60 may be valid.

See Also

"Dates and times", in the guide.

webcpp::lexical::grammar::time_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::time_rule_t::parse

Matches a full-time at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no digit starts it; error::invalid when it started and broke a constraint.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::time_rule_t::leap

Whether second 60 may be valid.

Synopsis

leap_seconds leap = leap_seconds::allowed;

webcpp::lexical::grammar::uri_reference_rule_t

The rule of RFC 3986 4.1’s URI-reference: Boost.URL’s uri_reference_rule, then a guard.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

struct uri_reference_rule_t;

Description

Boost.URL 1.92 accepts a relative reference whose path after the authority is not path-abempty (RFC 3986 4.2), as //example.com:abc/p, whose port is not a number, and an authority that holds two at signs; with a scheme, it stops the URI at that authority. It also accepts "[" and "]" in a query, which RFC 3986 holds only around an IP literal, "#" in a fragment, which RFC 3986 3.5 refuses there, and RFC 6874’s zone identifier in an IP literal, which RFC 9844 withdrew. The guard refuses each with error::invalid. Otherwise the rule yields Boost.URL’s url_view of the reference, and stops where Boost.URL’s rule does.

Type Aliases

Name Description

value_type

The rule yields Boost.URL’s view of the reference it matched.

Member Functions

Name Description

parse

Matches a URI-reference at it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::grammar::uri_reference_rule_t::value_type

The rule yields Boost.URL’s view of the reference it matched.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

using value_type = boost::urls::url_view;

webcpp::lexical::grammar::uri_reference_rule_t::parse

Matches a URI-reference at it.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
result<boost::urls::url_view>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The view of the reference; Boost.URL’s error when its rule refuses the input; error::invalid when the guard does, a reference that ends at its authority before a character that starts a path segment included.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::uri_rule_t

The rule of RFC 3986 3’s URI: Boost.URL’s uri_rule, then a guard.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

struct uri_rule_t;

Description

Boost.URL 1.92 accepts "[" and "]" in a query, which RFC 3986 holds only around an IP literal, "#" in a fragment, which RFC 3986 3.5 refuses there, and RFC 6874’s zone identifier in an IP literal, which RFC 9844 withdrew, and stops a URI at an authority that holds two at signs; the guard refuses each with error::invalid. Otherwise the rule yields Boost.URL’s url_view of the URI, and stops where Boost.URL’s rule does.

Type Aliases

Name Description

value_type

The rule yields Boost.URL’s view of the URI it matched.

Member Functions

Name Description

parse

Matches a URI at it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::grammar::uri_rule_t::value_type

The rule yields Boost.URL’s view of the URI it matched.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

using value_type = boost::urls::url_view;

webcpp::lexical::grammar::uri_rule_t::parse

Matches a URI at it.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
result<boost::urls::url_view>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The view of the URI; Boost.URL’s error when its rule refuses the input; error::invalid when the guard does.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::uri_template_rule_t

The rule of RFC 6570 section 2’s URI-Template, with erratum 6937: literals and expressions.

Synopsis

struct uri_template_rule_t;

Description

A literal is a printable ASCII character but the space, the double quote, the percent sign, the less-than and greater-than signs, the backslash, the circumflex, the grave accent, the braces and the vertical bar (the apostrophe is one, by the erratum), or a pct-encoded triplet, or a ucschar or iprivate code point. An expression is an opening brace, an optional operator (the reserved ones of op-reserve included, as the ABNF admits them), and varspecs separated by commas, each a varname with a colon and a max-length of 1 to 9999, or an asterisk, then a closing brace. The empty template is valid. The rule stops where the template does; an expression or a percent sign that starts and breaks makes it fail.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a URI-Template at it.

See Also

"URI templates", in the guide.

webcpp::lexical::grammar::uri_template_rule_t::value_type

The rule yields the text it matched.

Synopsis

using value_type = text;

webcpp::lexical::grammar::uri_template_rule_t::parse

Matches a URI-Template at it.

Synopsis

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched, possibly empty; error::invalid when an expression or a pct-encoded triplet starts and breaks.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::uuid_rule_t

The rule of RFC 9562 section 4’s UUID: 32 hexadecimal digits in groups of 8, 4, 4, 4 and 12, joined by hyphens.

Synopsis

Declared in <webcpp/lexical/uuid.hpp>

struct uuid_rule_t;

Description

Either case; the version and variant nibbles are not checked; no braces and no urn:uuid:. It stops after the twelfth digit of the last group.

Type Aliases

Name Description

value_type

The rule yields the text it matched.

Member Functions

Name Description

parse

Matches a UUID at it.

See Also

"Identifiers", in the guide.

webcpp::lexical::grammar::uuid_rule_t::value_type

The rule yields the text it matched.

Synopsis

Declared in <webcpp/lexical/uuid.hpp>

using value_type = text;

webcpp::lexical::grammar::uuid_rule_t::parse

Matches a UUID at it.

Synopsis

Declared in <webcpp/lexical/uuid.hpp>

[[nodiscard]]
result<text>
parse(
    char const*& it,
    char const* end) const noexcept;

Return Value

The text matched; error::mismatch when no hexadecimal digit starts it; error::invalid when it started and broke the shape.

Note

The return value should not be discarded.

Parameters

Name Description

it

The start of the input: past the match on success, else unchanged.

end

The end of the input.

webcpp::lexical::grammar::date_rule

The rule of RFC 3339’s full-date.

Synopsis

inline constexpr date_rule_t date_rule = {};

webcpp::lexical::grammar::date_time_rule

The rule of RFC 3339’s date-time.

Synopsis

inline constexpr date_time_rule_t date_time_rule = {};

webcpp::lexical::grammar::duration_rule

The rule of RFC 3339 appendix A’s duration.

Synopsis

inline constexpr duration_rule_t duration_rule = {};

webcpp::lexical::grammar::email_rule

The rule of RFC 5321’s Mailbox.

Synopsis

Declared in <webcpp/lexical/email.hpp>

inline constexpr email_rule_t email_rule = {};

webcpp::lexical::grammar::hostname_rule

The rule of a host name.

Synopsis

inline constexpr hostname_rule_t hostname_rule = {};

webcpp::lexical::grammar::idn_email_rule

The rule of RFC 6531’s Mailbox.

Synopsis

Declared in <webcpp/lexical/email.hpp>

inline constexpr idn_email_rule_t idn_email_rule = {};

webcpp::lexical::grammar::idn_hostname_rule

The rule of an internationalized host name.

Synopsis

inline constexpr idn_hostname_rule_t idn_hostname_rule = {};

webcpp::lexical::grammar::idna_label_rule

The rule of one internationalized label.

Synopsis

inline constexpr idna_label_rule_t idna_label_rule = {};

webcpp::lexical::grammar::iri_reference_rule

The rule of RFC 3987’s IRI-reference, with the default resource.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

inline constexpr iri_reference_rule_t iri_reference_rule = {};

webcpp::lexical::grammar::iri_rule

The rule of RFC 3987’s IRI, with the default resource; iri_rule_t{&resource} names another.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

inline constexpr iri_rule_t iri_rule = {};

webcpp::lexical::grammar::json_pointer_rule

The rule of RFC 6901’s json-pointer.

Synopsis

inline constexpr json_pointer_rule_t json_pointer_rule = {};

webcpp::lexical::grammar::relative_json_pointer_rule

The rule of the relative JSON pointer, index manipulation allowed; relative_json_pointer_rule_t{index_manipulation::refused} refuses it.

Synopsis

inline constexpr relative_json_pointer_rule_t relative_json_pointer_rule = {};

webcpp::lexical::grammar::time_rule

The rule of RFC 3339’s full-time, leap seconds allowed; time_rule_t{leap_seconds::refused} refuses them.

Synopsis

inline constexpr time_rule_t time_rule = {};

webcpp::lexical::grammar::uri_reference_rule

The rule of RFC 3986’s URI-reference, guarded.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

inline constexpr uri_reference_rule_t uri_reference_rule = {};

webcpp::lexical::grammar::uri_rule

The rule of RFC 3986’s URI, guarded.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

inline constexpr uri_rule_t uri_rule = {};

webcpp::lexical::grammar::uri_template_rule

The rule of RFC 6570’s URI-Template.

Synopsis

inline constexpr uri_template_rule_t uri_template_rule = {};

webcpp::lexical::grammar::uuid_rule

The rule of RFC 9562’s UUID.

Synopsis

Declared in <webcpp/lexical/uuid.hpp>

inline constexpr uuid_rule_t uuid_rule = {};

webcpp::lexical::index_manipulation

Whether a relative JSON Pointer may move its array index with a sign and an integer.

Synopsis

enum class index_manipulation : int;

Members

Name Description

allowed

An index manipulation is valid, as in draft-bhutton-00, which 2020-12 and v1 cite.

refused

An index manipulation is refused, as in draft-handrews-01, which 7 and 2019-09 cite.

See Also

"Identifiers", in the guide.

webcpp::lexical::index_manipulation::allowed

An index manipulation is valid, as in draft-bhutton-00, which 2020-12 and v1 cite.

Synopsis

allowed

webcpp::lexical::index_manipulation::refused

An index manipulation is refused, as in draft-handrews-01, which 7 and 2019-09 cite.

Synopsis

refused

webcpp::lexical::leap_seconds

Whether a time may hold second 60, a leap second.

Synopsis

enum class leap_seconds : int;

Members

Name Description

allowed

Second 60 is valid when the time moved to UTC is 23:59, as RFC 3339 5.7 allows.

refused

Second 60 is never valid, as JSON Schema v1’s time asks.

See Also

"Dates and times", in the guide.

webcpp::lexical::leap_seconds::allowed

Second 60 is valid when the time moved to UTC is 23:59, as RFC 3339 5.7 allows.

Synopsis

allowed

webcpp::lexical::leap_seconds::refused

Second 60 is never valid, as JSON Schema v1’s time asks.

Synopsis

refused

webcpp::lexical::is_date

Whether s is an RFC 3339 full-date, JSON Schema’s date.

Synopsis

[[nodiscard]]
bool
is_date(std::string_view s) noexcept;

Return Value

parse(s, grammar::date_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Dates and times", in the guide.

webcpp::lexical::is_date_time

Whether s is an RFC 3339 date-time, JSON Schema’s date-time.

Synopsis

[[nodiscard]]
bool
is_date_time(std::string_view s) noexcept;

Return Value

parse(s, grammar::date_time_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Dates and times", in the guide.

webcpp::lexical::is_duration

Whether s is an RFC 3339 appendix A duration, JSON Schema’s duration.

Synopsis

[[nodiscard]]
bool
is_duration(std::string_view s) noexcept;

Return Value

parse(s, grammar::duration_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Dates and times", in the guide.

webcpp::lexical::is_email

Whether s is an RFC 5321 Mailbox, JSON Schema’s email.

Synopsis

Declared in <webcpp/lexical/email.hpp>

[[nodiscard]]
bool
is_email(std::string_view s) noexcept;

Return Value

parse(s, grammar::email_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_hostname

Whether s is a host name, JSON Schema’s hostname.

Synopsis

[[nodiscard]]
bool
is_hostname(std::string_view s) noexcept;

Return Value

parse(s, grammar::hostname_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_idn_email

Whether s is an RFC 6531 internationalized Mailbox, JSON Schema’s idn-email.

Synopsis

Declared in <webcpp/lexical/email.hpp>

[[nodiscard]]
bool
is_idn_email(std::string_view s) noexcept;

Return Value

parse(s, grammar::idn_email_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_idn_hostname

Whether s is an internationalized host name, JSON Schema’s idn-hostname.

Synopsis

[[nodiscard]]
bool
is_idn_hostname(std::string_view s) noexcept;

Return Value

parse(s, grammar::idn_hostname_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_ipv4

Whether s is an IPv4 address in dotted-quad form, JSON Schema’s ipv4.

Synopsis

Declared in <webcpp/lexical/ip.hpp>

[[nodiscard]]
bool
is_ipv4(std::string_view s) noexcept;

Description

RFC 2673 3.2’s dotted quad, each part RFC 3986’s dec-octet, 0 to 255 without a leading zero.

Return Value

parse(s, boost::urls::ipv4_address_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_ipv6

Whether s is an IPv6 address, JSON Schema’s ipv6.

Synopsis

Declared in <webcpp/lexical/ip.hpp>

[[nodiscard]]
bool
is_ipv6(std::string_view s) noexcept;

Description

RFC 4291 2.2’s three text forms; no zone id, no prefix length.

Return Value

parse(s, boost::urls::ipv6_address_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Addresses", in the guide.

webcpp::lexical::is_iri

is_iri overloads

Synopses

Declared in <webcpp/lexical/uri.hpp>

Whether s is an RFC 3987 IRI, JSON Schema’s iri.

[[nodiscard]]
bool
is_iri(std::string_view s);

Whether s is an RFC 3987 IRI, a mapped URI beyond 1,024 octets taken from resource.

[[nodiscard]]
bool
is_iri(
    std::string_view s,
    std::pmr::memory_resource& resource);

Exceptions

Name Thrown on

std::bad_alloc

When the default resource cannot allocate, only for an IRI whose mapped URI exceeds 1,024 octets.

Return Value

  • parse(s, grammar::iri_rule).has_value().

  • parse(s, grammar::iri_rule_t{&resource}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

resource

Where the mapped copy of a long non-ASCII IRI is allocated; an ASCII IRI never reaches it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_iri

Whether s is an RFC 3987 IRI, JSON Schema’s iri.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_iri(std::string_view s);

Exceptions

Name Thrown on

std::bad_alloc

When the default resource cannot allocate, only for an IRI whose mapped URI exceeds 1,024 octets.

Return Value

parse(s, grammar::iri_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_iri

Whether s is an RFC 3987 IRI, a mapped URI beyond 1,024 octets taken from resource.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_iri(
    std::string_view s,
    std::pmr::memory_resource& resource);

Exceptions

Name Thrown on

std::bad_alloc

When resource cannot allocate, or what it throws instead.

Return Value

parse(s, grammar::iri_rule_t{&resource}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

resource

Where the mapped copy of a long non-ASCII IRI is allocated; an ASCII IRI never reaches it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_iri_reference

is_iri_reference overloads

Synopses

Declared in <webcpp/lexical/uri.hpp>

Whether s is an RFC 3987 IRI-reference, JSON Schema’s iri-reference.

[[nodiscard]]
bool
is_iri_reference(std::string_view s);

Whether s is an RFC 3987 IRI-reference, a mapped URI beyond 1,024 octets taken from resource.

[[nodiscard]]
bool
is_iri_reference(
    std::string_view s,
    std::pmr::memory_resource& resource);

Exceptions

Name Thrown on

std::bad_alloc

When the default resource cannot allocate, only for a reference whose mapped URI exceeds 1,024 octets.

Return Value

  • parse(s, grammar::iri_reference_rule).has_value().

  • parse(s, grammar::iri_reference_rule_t{&resource}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

resource

Where the mapped copy of a long non-ASCII reference is allocated; an ASCII reference never reaches it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_iri_reference

Whether s is an RFC 3987 IRI-reference, JSON Schema’s iri-reference.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_iri_reference(std::string_view s);

Exceptions

Name Thrown on

std::bad_alloc

When the default resource cannot allocate, only for a reference whose mapped URI exceeds 1,024 octets.

Return Value

parse(s, grammar::iri_reference_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_iri_reference

Whether s is an RFC 3987 IRI-reference, a mapped URI beyond 1,024 octets taken from resource.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_iri_reference(
    std::string_view s,
    std::pmr::memory_resource& resource);

Exceptions

Name Thrown on

std::bad_alloc

When resource cannot allocate, or what it throws instead.

Return Value

parse(s, grammar::iri_reference_rule_t{&resource}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

resource

Where the mapped copy of a long non-ASCII reference is allocated; an ASCII reference never reaches it.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_json_pointer

Whether s is an RFC 6901 JSON Pointer, JSON Schema’s json-pointer.

Synopsis

[[nodiscard]]
bool
is_json_pointer(std::string_view s) noexcept;

Return Value

parse(s, grammar::json_pointer_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Identifiers", in the guide.

webcpp::lexical::is_regex

Whether pattern is a regular expression, JSON Schema’s regex: an ECMA-262 Pattern in u mode, with ECMAScript 2025’s modifiers and duplicate group names.

Synopsis

Declared in <webcpp/lexical/regex.hpp>

[[nodiscard]]
bool
is_regex(std::string_view pattern) noexcept;

Description

Only the syntax and the early errors are checked: the pattern is never compiled or run. A pattern that is not well-formed UTF-8 is invalid, and a NUL is a literal like any other code point. A pattern longer than max_regex_length, or nested deeper than max_regex_nesting, is answered false without being judged. Nothing is allocated, and nothing throws.

Return Value

Whether the pattern is an ECMA-262 regular expression in u mode.

Note

The return value should not be discarded.

Parameters

Name Description

pattern

Any byte string.

See Also

"Regular expressions", in the guide.

webcpp::lexical::is_relative_json_pointer

Whether s is a relative JSON Pointer, JSON Schema’s relative-json-pointer.

Synopsis

[[nodiscard]]
bool
is_relative_json_pointer(
    std::string_view s,
    index_manipulation manipulation = index_manipulation::allowed) noexcept;

Return Value

parse(s, grammar::relative_json_pointer_rule_t{manipulation}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

manipulation

Whether an index manipulation (0+1) may be valid: index_manipulation::allowed for draft 2020-12 and v1, index_manipulation::refused for drafts 7 and 2019-09.

See Also

"Identifiers", in the guide.

webcpp::lexical::is_time

Whether s is an RFC 3339 full-time, JSON Schema’s time.

Synopsis

[[nodiscard]]
bool
is_time(
    std::string_view s,
    leap_seconds leap = leap_seconds::allowed) noexcept;

Return Value

parse(s, grammar::time_rule_t{leap}).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

leap

Whether second 60 may be valid: leap_seconds::allowed for drafts 7 to 2020-12, leap_seconds::refused for v1.

See Also

"Dates and times", in the guide.

webcpp::lexical::is_uri

Whether s is an RFC 3986 URI, JSON Schema’s uri: a scheme, and a fragment allowed.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_uri(std::string_view s) noexcept;

Return Value

parse(s, grammar::uri_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_uri_reference

Whether s is an RFC 3986 URI-reference, JSON Schema’s uri-reference: a URI or a relative reference.

Synopsis

Declared in <webcpp/lexical/uri.hpp>

[[nodiscard]]
bool
is_uri_reference(std::string_view s) noexcept;

Return Value

parse(s, grammar::uri_reference_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"URIs and IRIs", in the guide.

webcpp::lexical::is_uri_template

Whether s is an RFC 6570 URI Template of any level, JSON Schema’s uri-template.

Synopsis

[[nodiscard]]
bool
is_uri_template(std::string_view s) noexcept;

Return Value

parse(s, grammar::uri_template_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"URI templates", in the guide.

webcpp::lexical::is_uuid

Whether s is a UUID in RFC 9562’s string form, JSON Schema’s uuid.

Synopsis

Declared in <webcpp/lexical/uuid.hpp>

[[nodiscard]]
bool
is_uuid(std::string_view s) noexcept;

Return Value

parse(s, grammar::uuid_rule).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

s

Any byte string.

See Also

"Identifiers", in the guide.

webcpp::lexical::max_regex_length

The longest pattern is_regex judges, in bytes: 65,536.

Synopsis

Declared in <webcpp/lexical/regex.hpp>

inline constexpr std::size_t max_regex_length = 65536;

Description

A longer pattern is answered false without being read, which bounds the work that a hostile pattern can make the validator do.

See Also

"Regular expressions", in the guide.

webcpp::lexical::max_regex_nesting

The deepest a pattern’s groups may nest for is_regex to judge it: 32 levels of groups, lookarounds and modifiers, one inside the other.

Synopsis

Declared in <webcpp/lexical/regex.hpp>

inline constexpr std::size_t max_regex_nesting = 32;

Description

A pattern nested deeper is answered false without being judged. The validator reads a group by calling itself, and this bound limits that recursion, so that a hostile pattern cannot exhaust the stack of a WebAssembly program.

See Also

"Regular expressions", in the guide.

WEBCPP_LEXICAL_NO_EXCEPTIONS

Defined when lexical is built without exceptions: when webcpp has none (the compiler has none, or the developer defined WEBCPP_NO_EXCEPTIONS for every webcpp library), or when the developer defines it alone, to disable lexical’s in a build that has them.

Synopsis

#define WEBCPP_LEXICAL_NO_EXCEPTIONS

Description

lexical raises no exception of its own: a validator answers false and a rule returns its error, so the macro changes nothing in it. It is provided so that a program can set it for every webcpp library alike. Only the memory an IRI function allocates may throw, and an exception lexical raised would go through boost::throw_exception.

See Also

"Errors", in the guide.

Appendix A: Unicode data

The tables of lexical’s regular expressions, <webcpp/lexical/detail/regex_tables.hpp>, and the patterns its test regex_v8 reads, test/regex/cases/patterns.json, are derived from the Unicode Character Database, version 17.0.0, which Unicode, Inc. publishes under the following notice. lexical’s code is under the Boost Software License 1.0 alone.

UNICODE LICENSE V3

COPYRIGHT AND PERMISSION NOTICE

Copyright © 1991-2026 Unicode, Inc.

NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.

THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.

Appendix B: Changelog

What changed in lexical in each version of webcpp, newest first, one line per change. "Unreleased" is what the next version brings. The release notes of every version are on webcpp’s index page.

Unreleased

webcpp 0.1.0

  • Added the library: validators and Boost.URL rules for JSON Schema’s string formats.

  • Added dates, times and durations of RFC 3339, with a leap-second policy for v1.

  • Added UUIDs, and JSON Pointers absolute and relative, with an index manipulation policy.

  • Added IP addresses, URIs and URI references through Boost.URL, faithful to RFC 3986.

  • Added IRIs and URI templates, mapped to URIs for Boost.URL within a memory resource.

  • Added host names and e-mail addresses, internationalized ones judged by webcpp::idna.

  • Added ECMA-262 patterns in u mode, checked by an allocation-free syntax validator, with Unicode 17 tables.