aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--edify/library/address/cidr.py70
-rw-r--r--edify/library/address/domain.py27
-rw-r--r--edify/library/address/hostname.py40
-rw-r--r--edify/library/address/ip.py232
-rw-r--r--edify/library/address/path.py64
-rw-r--r--edify/library/address/ptr.py41
-rw-r--r--edify/library/address/socket.py75
-rw-r--r--edify/library/address/subnet.py33
-rw-r--r--edify/library/address/uri.py21
-rw-r--r--edify/library/address/url.py70
-rw-r--r--edify/library/api/atom.py19
-rw-r--r--edify/library/api/graphql.py19
-rw-r--r--edify/library/api/hal.py19
-rw-r--r--edify/library/api/jsonapi.py19
-rw-r--r--edify/library/api/oauth.py19
-rw-r--r--edify/library/api/openapi.py19
-rw-r--r--edify/library/api/openid.py19
-rw-r--r--edify/library/api/rss.py19
-rw-r--r--edify/library/api/saml.py19
-rw-r--r--edify/library/api/soap.py19
-rw-r--r--edify/library/api/swagger.py19
-rw-r--r--edify/library/api/webhook.py19
-rw-r--r--edify/library/auth/mnemonic.py16
-rw-r--r--edify/library/color/color.py132
-rw-r--r--edify/library/color/filter.py26
-rw-r--r--edify/library/color/gradient.py22
-rw-r--r--edify/library/color/palette.py43
-rw-r--r--edify/library/color/swatch.py21
-rw-r--r--edify/library/contact/address.py20
-rw-r--r--edify/library/contact/email.py219
-rw-r--r--edify/library/contact/fax.py49
-rw-r--r--edify/library/contact/phone.py58
-rw-r--r--edify/library/data/avro.py19
-rw-r--r--edify/library/data/csv.py19
-rw-r--r--edify/library/data/hdf5.py19
-rw-r--r--edify/library/data/html.py19
-rw-r--r--edify/library/data/ini.py19
-rw-r--r--edify/library/data/json.py19
-rw-r--r--edify/library/data/msgpack.py19
-rw-r--r--edify/library/data/protobuf.py19
-rw-r--r--edify/library/data/toml.py19
-rw-r--r--edify/library/data/tsv.py19
-rw-r--r--edify/library/data/xml.py19
-rw-r--r--edify/library/data/yaml.py19
-rw-r--r--edify/library/document/docx.py18
-rw-r--r--edify/library/document/epub.py18
-rw-r--r--edify/library/document/mobi.py18
-rw-r--r--edify/library/document/odt.py18
-rw-r--r--edify/library/document/pdf.py18
-rw-r--r--edify/library/document/pptx.py18
-rw-r--r--edify/library/document/readme.py18
-rw-r--r--edify/library/document/svg.py18
-rw-r--r--edify/library/document/xlsx.py18
-rw-r--r--edify/library/financial/card.py22
-rw-r--r--edify/library/financial/wallet.py103
-rw-r--r--edify/library/geo/altitude.py22
-rw-r--r--edify/library/geo/coordinate.py79
-rw-r--r--edify/library/geo/place.py19
-rw-r--r--edify/library/geo/plus.py24
-rw-r--r--edify/library/grammar/abnf.py37
-rw-r--r--edify/library/grammar/bnf.py37
-rw-r--r--edify/library/grammar/ebnf.py37
-rw-r--r--edify/library/grammar/peg.py37
-rw-r--r--edify/library/grammar/pest.py37
-rw-r--r--edify/library/medical/medical.py32
-rw-r--r--edify/library/numeric/fraction.py22
-rw-r--r--edify/library/product/barcode.py13
-rw-r--r--edify/library/product/gtin.py16
-rw-r--r--edify/library/product/mpn.py20
-rw-r--r--edify/library/publishing/arxiv.py44
-rw-r--r--edify/library/publishing/doi.py26
-rw-r--r--edify/library/publishing/isbn.py33
-rw-r--r--edify/library/publishing/issn.py18
-rw-r--r--edify/library/publishing/pmc.py11
-rw-r--r--edify/library/publishing/pmid.py10
-rw-r--r--edify/library/transport/aircraft.py17
-rw-r--r--edify/library/transport/vehicle.py19
77 files changed, 2365 insertions, 267 deletions
diff --git a/edify/library/address/cidr.py b/edify/library/address/cidr.py
index 5894952..a2b9454 100644
--- a/edify/library/address/cidr.py
+++ b/edify/library/address/cidr.py
@@ -2,16 +2,66 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
-
-cidr = RegexBackedPattern(
- r"^(?:"
- r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)"
- r"(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}"
- r"/(?:3[0-2]|[12]?\d)"
- r"|(?:[0-9a-fA-F]{1,4}:){0,7}[0-9a-fA-F]{1,4}"
- r"/(?:12[0-8]|1[01]\d|[1-9]?\d)"
- r")$"
+from edify import Pattern, any_of
+
+_ipv4_octet = any_of(
+ Pattern().string("25").any_of().range("0", "5").end(),
+ Pattern().char("2").any_of().range("0", "4").end().digit(),
+ Pattern().char("1").digit().digit(),
+ Pattern().any_of().range("1", "9").end().digit(),
+ Pattern().digit(),
+)
+
+_ipv4_prefix = any_of(
+ Pattern().char("3").any_of().range("0", "2").end(),
+ Pattern().optional().any_of_chars("12").digit(),
+)
+
+_hextet = (
+ Pattern()
+ .between(1, 4)
+ .any_of()
+ .range("0", "9")
+ .range("a", "f")
+ .range("A", "F")
+ .end()
+)
+
+_ipv6_prefix = any_of(
+ Pattern().string("12").any_of().range("0", "8").end(),
+ Pattern().char("1").any_of_chars("01").digit(),
+ Pattern().optional().any_of().range("1", "9").end().digit(),
+)
+
+_ipv4_cidr = (
+ Pattern()
+ .subexpression(_ipv4_octet)
+ .exactly(3)
+ .group()
+ .char(".")
+ .subexpression(_ipv4_octet)
+ .end()
+ .char("/")
+ .subexpression(_ipv4_prefix)
+)
+
+_ipv6_cidr = (
+ Pattern()
+ .between(0, 7)
+ .group()
+ .subexpression(_hextet)
+ .char(":")
+ .end()
+ .subexpression(_hextet)
+ .char("/")
+ .subexpression(_ipv6_prefix)
+)
+
+cidr = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_ipv4_cidr, _ipv6_cidr))
+ .end_of_input()
)
"""Callable :class:`Pattern` for CIDR notation: IPv4 address + ``/0``-``/32``
or IPv6 address + ``/0``-``/128``.
diff --git a/edify/library/address/domain.py b/edify/library/address/domain.py
index ef8d310..2112e4c 100644
--- a/edify/library/address/domain.py
+++ b/edify/library/address/domain.py
@@ -2,11 +2,30 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-domain = RegexBackedPattern(
- r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+"
- r"[a-zA-Z]{2,63}$"
+domain = (
+ Pattern()
+ .start_of_input()
+ .one_or_more()
+ .group()
+ .alphanumeric()
+ .optional()
+ .group()
+ .at_most(61)
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("-")
+ .end()
+ .alphanumeric()
+ .end()
+ .char(".")
+ .end()
+ .between(2, 63)
+ .letter()
+ .end_of_input()
)
"""Callable :class:`Pattern` for the DNS domain name shape:
at least one label followed by a TLD of 2-63 letters.
diff --git a/edify/library/address/hostname.py b/edify/library/address/hostname.py
index 06a8924..cbd691c 100644
--- a/edify/library/address/hostname.py
+++ b/edify/library/address/hostname.py
@@ -2,11 +2,41 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import END, Pattern
-hostname = RegexBackedPattern(
- r"^(?=.{1,253}$)(?:[a-zA-Z0-9]"
- r"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)"
- r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
+_label_tail = (
+ Pattern()
+ .optional()
+ .group()
+ .at_most(61)
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("-")
+ .end()
+ .alphanumeric()
+ .end()
+)
+
+hostname = (
+ Pattern()
+ .start_of_input()
+ .assert_ahead()
+ .between(1, 253)
+ .any_char()
+ .subexpression(END, ignore_start_and_end=False)
+ .end()
+ .group()
+ .alphanumeric()
+ .subexpression(_label_tail)
+ .end()
+ .zero_or_more()
+ .group()
+ .char(".")
+ .alphanumeric()
+ .subexpression(_label_tail)
+ .end()
+ .end_of_input()
)
"""Callable :class:`Pattern` for the RFC 1123 hostname shape."""
diff --git a/edify/library/address/ip.py b/edify/library/address/ip.py
index 25deaae..e51ea54 100644
--- a/edify/library/address/ip.py
+++ b/edify/library/address/ip.py
@@ -2,31 +2,213 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
-
-_IPV4_OCTET = r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)"
-_IPV4 = rf"{_IPV4_OCTET}\.{_IPV4_OCTET}\.{_IPV4_OCTET}\.{_IPV4_OCTET}"
-
-_IPV6 = (
- r"(?:"
- r"([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}"
- r"|([0-9a-fA-F]{1,4}:){1,7}:"
- r"|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}"
- r"|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}"
- r"|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}"
- r"|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}"
- r"|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}"
- r"|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})"
- r"|:((:[0-9a-fA-F]{1,4}){1,7}|:)"
- r"|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}"
- r"|::(ffff(:0{1,4}){0,1}:){0,1}"
- r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}"
- r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
- r"|([0-9a-fA-F]{1,4}:){1,4}:"
- r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}"
- r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
- r")"
+from edify import Pattern, any_of
+from edify.library._support.atoms import hex_any, octet
+
+
+def _hex_group() -> Pattern:
+ return Pattern().between(1, 4).subexpression(hex_any)
+
+
+_ipv4 = (
+ Pattern()
+ .subexpression(octet)
+ .exactly(3)
+ .group()
+ .char(".")
+ .subexpression(octet)
+ .end()
+)
+
+
+def _b1() -> Pattern:
+ return (
+ Pattern()
+ .exactly(7)
+ .group()
+ .subexpression(_hex_group())
+ .char(":")
+ .end()
+ .subexpression(_hex_group())
+ )
+
+
+def _b2() -> Pattern:
+ return (
+ Pattern()
+ .between(1, 7)
+ .group()
+ .subexpression(_hex_group())
+ .char(":")
+ .end()
+ .char(":")
+ )
+
+
+def _b3() -> Pattern:
+ return (
+ Pattern()
+ .between(1, 6)
+ .group()
+ .subexpression(_hex_group())
+ .char(":")
+ .end()
+ .char(":")
+ .subexpression(_hex_group())
+ )
+
+
+def _b_mixed(prefix_count: int, suffix_count: int) -> Pattern:
+ return (
+ Pattern()
+ .between(1, prefix_count)
+ .group()
+ .subexpression(_hex_group())
+ .char(":")
+ .end()
+ .between(1, suffix_count)
+ .group()
+ .char(":")
+ .subexpression(_hex_group())
+ .end()
+ )
+
+
+def _b8() -> Pattern:
+ return (
+ Pattern()
+ .subexpression(_hex_group())
+ .char(":")
+ .group()
+ .between(1, 6)
+ .group()
+ .char(":")
+ .subexpression(_hex_group())
+ .end()
+ .end()
+ )
+
+
+def _b9() -> Pattern:
+ return (
+ Pattern()
+ .char(":")
+ .group()
+ .any_of()
+ .subexpression(
+ Pattern()
+ .between(1, 7)
+ .group()
+ .char(":")
+ .subexpression(_hex_group())
+ .end()
+ )
+ .char(":")
+ .end()
+ .end()
+ )
+
+
+def _b_link_local() -> Pattern:
+ return (
+ Pattern()
+ .string("fe80:")
+ .between(0, 4)
+ .group()
+ .char(":")
+ .between(0, 4)
+ .subexpression(hex_any)
+ .end()
+ .char("%")
+ .one_or_more()
+ .any_of()
+ .range("0", "9")
+ .range("a", "z")
+ .range("A", "Z")
+ .end()
+ )
+
+
+def _map_octet() -> Pattern:
+ return any_of(
+ Pattern().string("25").range("0", "5"),
+ (
+ Pattern()
+ .optional()
+ .group()
+ .any_of()
+ .subexpression(Pattern().char("2").range("0", "4"))
+ .subexpression(Pattern().optional().char("1").digit())
+ .end()
+ .end()
+ .digit()
+ ),
+ )
+
+
+def _mapped_ipv4() -> Pattern:
+ return (
+ Pattern()
+ .exactly(3)
+ .group()
+ .subexpression(_map_octet())
+ .char(".")
+ .end()
+ .subexpression(_map_octet())
+ )
+
+
+def _b_ipv4_mapped() -> Pattern:
+ return (
+ Pattern()
+ .string("::")
+ .optional()
+ .group()
+ .string("ffff")
+ .optional()
+ .group()
+ .char(":")
+ .between(1, 4)
+ .char("0")
+ .end()
+ .char(":")
+ .end()
+ .subexpression(_mapped_ipv4())
+ )
+
+
+def _b_hybrid() -> Pattern:
+ return (
+ Pattern()
+ .between(1, 4)
+ .group()
+ .subexpression(_hex_group())
+ .char(":")
+ .end()
+ .char(":")
+ .subexpression(_mapped_ipv4())
+ )
+
+
+_ipv6 = any_of(
+ _b1(),
+ _b2(),
+ _b3(),
+ _b_mixed(5, 2),
+ _b_mixed(4, 3),
+ _b_mixed(3, 4),
+ _b_mixed(2, 5),
+ _b8(),
+ _b9(),
+ _b_link_local(),
+ _b_ipv4_mapped(),
+ _b_hybrid(),
)
-ip = RegexBackedPattern(rf"^(?:{_IPV4}|{_IPV6})$")
+ip = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_ipv4, _ipv6))
+ .end_of_input()
+)
"""Callable :class:`Pattern` for IPv4 dotted-quad or any IPv6 form."""
diff --git a/edify/library/address/path.py b/edify/library/address/path.py
index 6f1a1aa..529f225 100644
--- a/edify/library/address/path.py
+++ b/edify/library/address/path.py
@@ -2,14 +2,64 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-path = RegexBackedPattern(
- r"^(?:"
- r"(?:/|(?:\./|(?:\.\./)+))?(?:[^\0\r\n/]+/?)+"
- r"|[a-zA-Z]:\\(?:[^\\/:*?\"<>|\r\n]+\\?)+"
- r"|\\\\[^\\/:*?\"<>|\r\n]+\\[^\\/:*?\"<>|\r\n]+(?:\\[^\\/:*?\"<>|\r\n]*)*"
- r")$"
+_posix = (
+ Pattern()
+ .optional()
+ .group()
+ .any_of()
+ .char("/")
+ .group()
+ .string("./")
+ .end()
+ .group()
+ .one_or_more()
+ .string("../")
+ .end()
+ .end()
+ .end()
+ .one_or_more()
+ .group()
+ .one_or_more()
+ .anything_but_chars("\x00\r\n/")
+ .optional()
+ .char("/")
+ .end()
+)
+_windows = (
+ Pattern()
+ .letter()
+ .string(":\\")
+ .one_or_more()
+ .group()
+ .one_or_more()
+ .anything_but_chars("\\/:*?\"<>|\r\n")
+ .optional()
+ .char("\\")
+ .end()
+)
+_unc = (
+ Pattern()
+ .string("\\\\")
+ .one_or_more()
+ .anything_but_chars("\\/:*?\"<>|\r\n")
+ .char("\\")
+ .one_or_more()
+ .anything_but_chars("\\/:*?\"<>|\r\n")
+ .zero_or_more()
+ .group()
+ .char("\\")
+ .zero_or_more()
+ .anything_but_chars("\\/:*?\"<>|\r\n")
+ .end()
+)
+
+path = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_posix, _windows, _unc))
+ .end_of_input()
)
"""Callable :class:`Pattern` for a filesystem path shape: POSIX
(``/absolute`` or ``relative/``), Windows drive-letter, or UNC.
diff --git a/edify/library/address/ptr.py b/edify/library/address/ptr.py
index 5ce2303..a8020d6 100644
--- a/edify/library/address/ptr.py
+++ b/edify/library/address/ptr.py
@@ -2,13 +2,42 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
+from edify.library._support.atoms import hex_any
-ptr = RegexBackedPattern(
- r"^(?:"
- r"(?:\d{1,3}\.){4}in-addr\.arpa\.?"
- r"|(?:[0-9a-fA-F]\.){32}ip6\.arpa\.?"
- r")$"
+_ipv4_ptr = (
+ Pattern()
+ .exactly(4)
+ .group()
+ .between(1, 3)
+ .digit()
+ .char(".")
+ .end()
+ .string("in-addr")
+ .char(".")
+ .string("arpa")
+ .optional()
+ .char(".")
+)
+_ipv6_ptr = (
+ Pattern()
+ .exactly(32)
+ .group()
+ .subexpression(hex_any)
+ .char(".")
+ .end()
+ .string("ip6")
+ .char(".")
+ .string("arpa")
+ .optional()
+ .char(".")
+)
+
+ptr = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_ipv4_ptr, _ipv6_ptr))
+ .end_of_input()
)
"""Callable :class:`Pattern` for the reverse-DNS PTR shape: IPv4
``d.c.b.a.in-addr.arpa`` or IPv6 32-nibble ``…ip6.arpa`` form.
diff --git a/edify/library/address/socket.py b/edify/library/address/socket.py
index 66c428e..020c7dc 100644
--- a/edify/library/address/socket.py
+++ b/edify/library/address/socket.py
@@ -2,17 +2,70 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
+from edify.library._support.atoms import octet
-socket = RegexBackedPattern(
- r"^"
- r"(?:"
- r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)"
- r"(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}"
- r"|\[[0-9a-fA-F:]+\]"
- r"|[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
- r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*"
- r")"
- r":\d{1,5}$"
+_ipv4 = (
+ Pattern()
+ .subexpression(octet)
+ .exactly(3)
+ .group()
+ .char(".")
+ .subexpression(octet)
+ .end()
+)
+_ipv6_bracket = (
+ Pattern()
+ .char("[")
+ .one_or_more()
+ .any_of()
+ .range("0", "9")
+ .range("a", "f")
+ .range("A", "F")
+ .char(":")
+ .end()
+ .char("]")
+)
+_hostname_label = (
+ Pattern()
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .optional()
+ .group()
+ .between(0, 61)
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("-")
+ .end()
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .end()
+)
+_hostname = (
+ Pattern()
+ .subexpression(_hostname_label)
+ .zero_or_more()
+ .group()
+ .char(".")
+ .subexpression(_hostname_label)
+ .end()
+)
+
+socket = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_ipv4, _ipv6_bracket, _hostname))
+ .char(":")
+ .between(1, 5)
+ .digit()
+ .end_of_input()
)
"""Callable :class:`Pattern` for the ``host:port`` socket-address shape."""
diff --git a/edify/library/address/subnet.py b/edify/library/address/subnet.py
index 3ff5262..cdf50fc 100644
--- a/edify/library/address/subnet.py
+++ b/edify/library/address/subnet.py
@@ -2,13 +2,34 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-subnet = RegexBackedPattern(
- r"^(?:255|254|252|248|240|224|192|128|0)\."
- r"(?:255|254|252|248|240|224|192|128|0)\."
- r"(?:255|254|252|248|240|224|192|128|0)\."
- r"(?:255|254|252|248|240|224|192|128|0)$"
+
+def _mask_octet() -> Pattern:
+ return any_of(
+ Pattern().string("255"),
+ Pattern().string("254"),
+ Pattern().string("252"),
+ Pattern().string("248"),
+ Pattern().string("240"),
+ Pattern().string("224"),
+ Pattern().string("192"),
+ Pattern().string("128"),
+ Pattern().char("0"),
+ )
+
+
+subnet = (
+ Pattern()
+ .start_of_input()
+ .subexpression(_mask_octet())
+ .char(".")
+ .subexpression(_mask_octet())
+ .char(".")
+ .subexpression(_mask_octet())
+ .char(".")
+ .subexpression(_mask_octet())
+ .end_of_input()
)
"""Callable :class:`Pattern` for a dotted-decimal IPv4 subnet mask
(each octet is one of ``255``, ``254``, ``252``, …, ``128``, ``0``).
diff --git a/edify/library/address/uri.py b/edify/library/address/uri.py
index cb417be..9421a98 100644
--- a/edify/library/address/uri.py
+++ b/edify/library/address/uri.py
@@ -2,9 +2,26 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-uri = RegexBackedPattern(r"^[a-zA-Z][a-zA-Z0-9+.\-]*:[^\s]+$")
+uri = (
+ Pattern()
+ .start_of_input()
+ .letter()
+ .zero_or_more()
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("+")
+ .char(".")
+ .char("-")
+ .end()
+ .char(":")
+ .one_or_more()
+ .non_whitespace_char()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for the generic URI shape:
``scheme:opaque-or-path`` where scheme starts with a letter.
"""
diff --git a/edify/library/address/url.py b/edify/library/address/url.py
index 45bde6e..9061c34 100644
--- a/edify/library/address/url.py
+++ b/edify/library/address/url.py
@@ -2,14 +2,70 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-url = RegexBackedPattern(
- r"^(?:https?://)?"
- r"(?:www\.)?"
- r"[-a-zA-Z0-9@:%._\+~#=]{1,256}"
- r"\.[a-zA-Z0-9()]{1,6}"
- r"\b(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]*)$"
+url = (
+ Pattern()
+ .start_of_input()
+ .optional()
+ .group()
+ .string("http")
+ .optional()
+ .char("s")
+ .string("://")
+ .end()
+ .optional()
+ .group()
+ .string("www.")
+ .end()
+ .between(1, 256)
+ .any_of()
+ .char("-")
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("@")
+ .char(":")
+ .char("%")
+ .char(".")
+ .char("_")
+ .char("+")
+ .char("~")
+ .char("#")
+ .char("=")
+ .end()
+ .char(".")
+ .between(1, 6)
+ .any_of()
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("(")
+ .char(")")
+ .end()
+ .word_boundary()
+ .zero_or_more()
+ .any_of()
+ .char("-")
+ .range("a", "z")
+ .range("A", "Z")
+ .range("0", "9")
+ .char("(")
+ .char(")")
+ .char("@")
+ .char(":")
+ .char("%")
+ .char("_")
+ .char("+")
+ .char(".")
+ .char("~")
+ .char("#")
+ .char("?")
+ .char("&")
+ .char("/")
+ .char("=")
+ .end()
+ .end_of_input()
)
"""Callable :class:`Pattern` for the URL shape: optional ``http[s]://``,
optional ``www.``, host with dot-separated labels, TLD, and optional path.
diff --git a/edify/library/api/atom.py b/edify/library/api/atom.py
index e2f1b88..1693b08 100644
--- a/edify/library/api/atom.py
+++ b/edify/library/api/atom.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-atom = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+atom = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive atom-related identifier."""
diff --git a/edify/library/api/graphql.py b/edify/library/api/graphql.py
index d4e7527..be30834 100644
--- a/edify/library/api/graphql.py
+++ b/edify/library/api/graphql.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-graphql = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+graphql = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive graphql-related identifier."""
diff --git a/edify/library/api/hal.py b/edify/library/api/hal.py
index d2aef10..ad4456a 100644
--- a/edify/library/api/hal.py
+++ b/edify/library/api/hal.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-hal = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+hal = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive hal-related identifier."""
diff --git a/edify/library/api/jsonapi.py b/edify/library/api/jsonapi.py
index 367c112..f02a453 100644
--- a/edify/library/api/jsonapi.py
+++ b/edify/library/api/jsonapi.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-jsonapi = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+jsonapi = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive jsonapi-related identifier."""
diff --git a/edify/library/api/oauth.py b/edify/library/api/oauth.py
index 4dd0c1f..ed0ba57 100644
--- a/edify/library/api/oauth.py
+++ b/edify/library/api/oauth.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-oauth = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+oauth = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive oauth-related identifier."""
diff --git a/edify/library/api/openapi.py b/edify/library/api/openapi.py
index 0d6cd51..dd898b3 100644
--- a/edify/library/api/openapi.py
+++ b/edify/library/api/openapi.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-openapi = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+openapi = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive openapi-related identifier."""
diff --git a/edify/library/api/openid.py b/edify/library/api/openid.py
index 20f309e..75c71c2 100644
--- a/edify/library/api/openid.py
+++ b/edify/library/api/openid.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-openid = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+openid = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive openid-related identifier."""
diff --git a/edify/library/api/rss.py b/edify/library/api/rss.py
index d5dc323..bc402f4 100644
--- a/edify/library/api/rss.py
+++ b/edify/library/api/rss.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-rss = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+rss = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive rss-related identifier."""
diff --git a/edify/library/api/saml.py b/edify/library/api/saml.py
index 2650e50..730ff42 100644
--- a/edify/library/api/saml.py
+++ b/edify/library/api/saml.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-saml = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+saml = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive saml-related identifier."""
diff --git a/edify/library/api/soap.py b/edify/library/api/soap.py
index 445f658..c13c24d 100644
--- a/edify/library/api/soap.py
+++ b/edify/library/api/soap.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-soap = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+soap = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive soap-related identifier."""
diff --git a/edify/library/api/swagger.py b/edify/library/api/swagger.py
index 6bf9d67..b169b72 100644
--- a/edify/library/api/swagger.py
+++ b/edify/library/api/swagger.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-swagger = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+swagger = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive swagger-related identifier."""
diff --git a/edify/library/api/webhook.py b/edify/library/api/webhook.py
index 80a4a82..b737d77 100644
--- a/edify/library/api/webhook.py
+++ b/edify/library/api/webhook.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-webhook = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{3,256}$")
+webhook = (
+ Pattern()
+ .start_of_input()
+ .between(3, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive webhook-related identifier."""
diff --git a/edify/library/auth/mnemonic.py b/edify/library/auth/mnemonic.py
index 366fe0c..2929175 100644
--- a/edify/library/auth/mnemonic.py
+++ b/edify/library/auth/mnemonic.py
@@ -2,9 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-mnemonic = RegexBackedPattern(r"^(?:[a-z]+ ){11,23}[a-z]+$")
+mnemonic = (
+ Pattern()
+ .start_of_input()
+ .between(11, 23)
+ .group()
+ .one_or_more()
+ .lowercase()
+ .char(" ")
+ .end()
+ .one_or_more()
+ .lowercase()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a BIP-39 mnemonic phrase: 12 to 24
lowercase words separated by single spaces.
"""
diff --git a/edify/library/color/color.py b/edify/library/color/color.py
index 27469d5..7503cc3 100644
--- a/edify/library/color/color.py
+++ b/edify/library/color/color.py
@@ -2,13 +2,131 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-color = RegexBackedPattern(
- r"^(?:#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})"
- r"|rgba?\(\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?(?:\s*,\s*[\d.]+)?\s*\)"
- r"|hsla?\(\s*\d{1,3}(?:deg)?\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%(?:\s*,\s*[\d.]+)?\s*\)"
- r"|[a-zA-Z]{3,20}"
- r")$"
+color = any_of(
+ Pattern()
+ .start_of_input()
+ .char("#")
+ .any_of()
+ .between(3, 4)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .exactly(6)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .exactly(8)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .end()
+ .end_of_input(),
+ Pattern()
+ .start_of_input()
+ .string("rgb")
+ .optional()
+ .char("a")
+ .char("(")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .optional()
+ .char("%")
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .optional()
+ .char("%")
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .optional()
+ .char("%")
+ .optional()
+ .group()
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .one_or_more()
+ .any_of()
+ .digit()
+ .char(".")
+ .end()
+ .end()
+ .zero_or_more()
+ .whitespace_char()
+ .char(")")
+ .end_of_input(),
+ Pattern()
+ .start_of_input()
+ .string("hsl")
+ .optional()
+ .char("a")
+ .char("(")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .optional()
+ .group()
+ .string("deg")
+ .end()
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .char("%")
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .between(1, 3)
+ .digit()
+ .char("%")
+ .optional()
+ .group()
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .one_or_more()
+ .any_of()
+ .digit()
+ .char(".")
+ .end()
+ .end()
+ .zero_or_more()
+ .whitespace_char()
+ .char(")")
+ .end_of_input(),
+ Pattern()
+ .start_of_input()
+ .between(3, 20)
+ .letter()
+ .end_of_input(),
)
"""Callable :class:`Pattern` for any common CSS colour shape."""
diff --git a/edify/library/color/filter.py b/edify/library/color/filter.py
index 18fb975..7391950 100644
--- a/edify/library/color/filter.py
+++ b/edify/library/color/filter.py
@@ -2,11 +2,27 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-filter = RegexBackedPattern(
- r"^(?:blur|brightness|contrast|grayscale|hue-rotate|invert|opacity"
- r"|saturate|sepia|drop-shadow)"
- r"\([^)]+\)$"
+filter = (
+ Pattern()
+ .start_of_input()
+ .any_of(
+ "blur",
+ "brightness",
+ "contrast",
+ "grayscale",
+ "hue-rotate",
+ "invert",
+ "opacity",
+ "saturate",
+ "sepia",
+ "drop-shadow",
+ )
+ .char("(")
+ .one_or_more()
+ .anything_but_chars(")")
+ .char(")")
+ .end_of_input()
)
"""Callable :class:`Pattern` for a CSS filter function call."""
diff --git a/edify/library/color/gradient.py b/edify/library/color/gradient.py
index 49bcede..c3ca027 100644
--- a/edify/library/color/gradient.py
+++ b/edify/library/color/gradient.py
@@ -2,7 +2,25 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-gradient = RegexBackedPattern(r"^(?:linear|radial|conic)-gradient\([^()]*(?:\([^()]*\)[^()]*)*\)$")
+gradient = (
+ Pattern()
+ .start_of_input()
+ .any_of("linear", "radial", "conic")
+ .string("-gradient(")
+ .zero_or_more()
+ .anything_but_chars("()")
+ .zero_or_more()
+ .group()
+ .char("(")
+ .zero_or_more()
+ .anything_but_chars("()")
+ .char(")")
+ .zero_or_more()
+ .anything_but_chars("()")
+ .end()
+ .char(")")
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a CSS gradient function call."""
diff --git a/edify/library/color/palette.py b/edify/library/color/palette.py
index 441d512..f818400 100644
--- a/edify/library/color/palette.py
+++ b/edify/library/color/palette.py
@@ -2,10 +2,45 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-palette = RegexBackedPattern(
- r"^(?:#[0-9A-Fa-f]{3,8}|[a-zA-Z]{3,20})"
- r"(?:\s*,\s*(?:#[0-9A-Fa-f]{3,8}|[a-zA-Z]{3,20})){1,15}$"
+palette = (
+ Pattern()
+ .start_of_input()
+ .any_of()
+ .group()
+ .char("#")
+ .between(3, 8)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .end()
+ .between(3, 20)
+ .letter()
+ .end()
+ .between(1, 15)
+ .group()
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .any_of()
+ .group()
+ .char("#")
+ .between(3, 8)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .end()
+ .between(3, 20)
+ .letter()
+ .end()
+ .end()
+ .end_of_input()
)
"""Callable :class:`Pattern` for a comma-separated list of 2-16 colours."""
diff --git a/edify/library/color/swatch.py b/edify/library/color/swatch.py
index 0d68bbd..1680deb 100644
--- a/edify/library/color/swatch.py
+++ b/edify/library/color/swatch.py
@@ -2,7 +2,24 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-swatch = RegexBackedPattern(r"^(?:#[0-9A-Fa-f]{3,8}|[a-zA-Z]{3,20})$")
+swatch = (
+ Pattern()
+ .start_of_input()
+ .any_of()
+ .group()
+ .char("#")
+ .between(3, 8)
+ .any_of()
+ .range("0", "9")
+ .range("A", "F")
+ .range("a", "f")
+ .end()
+ .end()
+ .between(3, 20)
+ .letter()
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a single hex colour or CSS named colour."""
diff --git a/edify/library/contact/address.py b/edify/library/contact/address.py
index 4d2bdaf..bcac596 100644
--- a/edify/library/contact/address.py
+++ b/edify/library/contact/address.py
@@ -2,9 +2,25 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-address = RegexBackedPattern(r"^\d+\s+[A-Za-z0-9\s.,'\-#/]+$")
+address = (
+ Pattern()
+ .start_of_input()
+ .one_or_more()
+ .digit()
+ .one_or_more()
+ .whitespace_char()
+ .one_or_more()
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .whitespace_char()
+ .any_of_chars(".,'-#/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive street-address shape:
one or more digits followed by whitespace and address body characters.
"""
diff --git a/edify/library/contact/email.py b/edify/library/contact/email.py
index d6fcd86..dc98cdd 100644
--- a/edify/library/contact/email.py
+++ b/edify/library/contact/email.py
@@ -2,22 +2,209 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
-
-email = RegexBackedPattern(
- r"^(?:"
- r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"
- r"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
- r"|(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"
- r"|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]"
- r"|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")"
- r"@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+"
- r"[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
- r"|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}"
- r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:"
- r"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]"
- r"|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"
- r")$"
+from edify import Pattern, any_of
+
+
+def _local_char_class() -> Pattern:
+ return (
+ Pattern()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .char("!")
+ .char("#")
+ .char("$")
+ .char("%")
+ .char("&")
+ .char("'")
+ .char("*")
+ .char("+")
+ .char("/")
+ .char("=")
+ .char("?")
+ .char("^")
+ .char("_")
+ .char("`")
+ .char("{")
+ .char("|")
+ .char("}")
+ .char("~")
+ .char("-")
+ .end()
+ )
+
+
+def _domain_label() -> Pattern:
+ return (
+ Pattern()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .end()
+ .optional()
+ .group()
+ .zero_or_more()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .char("-")
+ .end()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .end()
+ .end()
+ )
+
+
+_basic_local = (
+ Pattern()
+ .one_or_more()
+ .subexpression(_local_char_class())
+ .zero_or_more()
+ .group()
+ .char(".")
+ .one_or_more()
+ .subexpression(_local_char_class())
+ .end()
+)
+
+_basic_domain = (
+ Pattern()
+ .one_or_more()
+ .group()
+ .subexpression(_domain_label())
+ .char(".")
+ .end()
+ .subexpression(_domain_label())
+)
+
+_basic = (
+ Pattern()
+ .subexpression(_basic_local)
+ .char("@")
+ .subexpression(_basic_domain)
+)
+
+
+def _quoted_text() -> Pattern:
+ return (
+ Pattern()
+ .any_of()
+ .range("\x01", "\x08")
+ .char("\x0b")
+ .char("\x0c")
+ .range("\x0e", "\x1f")
+ .char("\x21")
+ .range("\x23", "\x5b")
+ .range("\x5d", "\x7f")
+ .end()
+ )
+
+
+def _quoted_escape() -> Pattern:
+ return (
+ Pattern()
+ .char("\\")
+ .any_of()
+ .range("\x01", "\x09")
+ .char("\x0b")
+ .char("\x0c")
+ .range("\x0e", "\x7f")
+ .end()
+ )
+
+
+_quoted_local = (
+ Pattern()
+ .char('"')
+ .zero_or_more()
+ .subexpression(any_of(_quoted_text(), _quoted_escape()))
+ .char('"')
+)
+
+
+def _octet() -> Pattern:
+ return any_of(
+ Pattern().string("25").range("0", "5"),
+ Pattern().char("2").range("0", "4").digit(),
+ Pattern().optional().any_of_chars("01").digit().optional().digit(),
+ )
+
+
+def _bracket_text() -> Pattern:
+ return (
+ Pattern()
+ .any_of()
+ .range("\x01", "\x08")
+ .char("\x0b")
+ .char("\x0c")
+ .range("\x0e", "\x1f")
+ .range("\x21", "\x5a")
+ .range("\x53", "\x7f")
+ .end()
+ )
+
+
+def _bracket_escape() -> Pattern:
+ return (
+ Pattern()
+ .char("\\")
+ .any_of()
+ .range("\x01", "\x09")
+ .char("\x0b")
+ .char("\x0c")
+ .range("\x0e", "\x7f")
+ .end()
+ )
+
+
+_ip_literal_tail = any_of(
+ _octet(),
+ (
+ Pattern()
+ .zero_or_more()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .char("-")
+ .end()
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .end()
+ .char(":")
+ .one_or_more()
+ .subexpression(any_of(_bracket_text(), _bracket_escape()))
+ ),
+)
+
+_ip_literal = (
+ Pattern()
+ .char("[")
+ .exactly(3)
+ .group()
+ .subexpression(_octet())
+ .char(".")
+ .end()
+ .subexpression(_ip_literal_tail)
+ .char("]")
+)
+
+_rfc_local = any_of(_basic_local, _quoted_local)
+_rfc_domain = any_of(_basic_domain, _ip_literal)
+_rfc = (
+ Pattern()
+ .subexpression(_rfc_local)
+ .char("@")
+ .subexpression(_rfc_domain)
+)
+
+email = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_basic, _rfc))
+ .end_of_input()
)
"""Callable :class:`Pattern` that accepts either the common permissive email
shape or the full RFC 5322 mailbox shape.
diff --git a/edify/library/contact/fax.py b/edify/library/contact/fax.py
index 84a8160..453a6d2 100644
--- a/edify/library/contact/fax.py
+++ b/edify/library/contact/fax.py
@@ -2,10 +2,51 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-fax = RegexBackedPattern(
- r"^\+?\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?"
- r"\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}$"
+fax = (
+ Pattern()
+ .start_of_input()
+ .optional()
+ .char("+")
+ .between_lazy(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .optional()
+ .char("(")
+ .between_lazy(1, 3)
+ .digit()
+ .optional()
+ .char(")")
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 9)
+ .digit()
+ .end_of_input()
)
"""Callable :class:`Pattern` for the permissive international fax-number shape."""
diff --git a/edify/library/contact/phone.py b/edify/library/contact/phone.py
index eee5b77..2293f5d 100644
--- a/edify/library/contact/phone.py
+++ b/edify/library/contact/phone.py
@@ -2,14 +2,58 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-phone = RegexBackedPattern(
- r"^(?:"
- r"\+?\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?"
- r"\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}"
- r"|\d{2,4}"
- r")$"
+_international = (
+ Pattern()
+ .optional()
+ .char("+")
+ .between_lazy(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .optional()
+ .char("(")
+ .between_lazy(1, 3)
+ .digit()
+ .optional()
+ .char(")")
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 4)
+ .digit()
+ .optional()
+ .any_of()
+ .char("-")
+ .char(".")
+ .whitespace_char()
+ .end()
+ .between(1, 9)
+ .digit()
+)
+_short = Pattern().between(2, 4).digit()
+
+phone = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_international, _short))
+ .end_of_input()
)
"""Callable :class:`Pattern` for phone-number shapes: permissive international
form (optional ``+``, country code, area code, and dash/dot/space separators)
diff --git a/edify/library/data/avro.py b/edify/library/data/avro.py
index 2771d16..d54e3e3 100644
--- a/edify/library/data/avro.py
+++ b/edify/library/data/avro.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-avro = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+avro = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for avro data-format identifier or content marker."""
diff --git a/edify/library/data/csv.py b/edify/library/data/csv.py
index 5044162..fa08131 100644
--- a/edify/library/data/csv.py
+++ b/edify/library/data/csv.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-csv = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+csv = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for csv data-format identifier or content marker."""
diff --git a/edify/library/data/hdf5.py b/edify/library/data/hdf5.py
index a3e070f..f38d5d5 100644
--- a/edify/library/data/hdf5.py
+++ b/edify/library/data/hdf5.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-hdf5 = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+hdf5 = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for hdf5 data-format identifier or content marker."""
diff --git a/edify/library/data/html.py b/edify/library/data/html.py
index ffccd73..2a60d51 100644
--- a/edify/library/data/html.py
+++ b/edify/library/data/html.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-html = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+html = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for html data-format identifier or content marker."""
diff --git a/edify/library/data/ini.py b/edify/library/data/ini.py
index 42b0ef8..5f53e04 100644
--- a/edify/library/data/ini.py
+++ b/edify/library/data/ini.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-ini = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+ini = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for ini data-format identifier or content marker."""
diff --git a/edify/library/data/json.py b/edify/library/data/json.py
index 905a5db..bd610ae 100644
--- a/edify/library/data/json.py
+++ b/edify/library/data/json.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-json = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+json = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for json data-format identifier or content marker."""
diff --git a/edify/library/data/msgpack.py b/edify/library/data/msgpack.py
index 85bed6b..fd7dae6 100644
--- a/edify/library/data/msgpack.py
+++ b/edify/library/data/msgpack.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-msgpack = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+msgpack = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for msgpack data-format identifier or content marker."""
diff --git a/edify/library/data/protobuf.py b/edify/library/data/protobuf.py
index 1677310..0c39617 100644
--- a/edify/library/data/protobuf.py
+++ b/edify/library/data/protobuf.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-protobuf = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+protobuf = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for protobuf data-format identifier or content marker."""
diff --git a/edify/library/data/toml.py b/edify/library/data/toml.py
index 32b710f..8aeb0f7 100644
--- a/edify/library/data/toml.py
+++ b/edify/library/data/toml.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-toml = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+toml = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for toml data-format identifier or content marker."""
diff --git a/edify/library/data/tsv.py b/edify/library/data/tsv.py
index 9a7dedd..d32da08 100644
--- a/edify/library/data/tsv.py
+++ b/edify/library/data/tsv.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-tsv = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+tsv = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for tsv data-format identifier or content marker."""
diff --git a/edify/library/data/xml.py b/edify/library/data/xml.py
index 37ae850..613c9c6 100644
--- a/edify/library/data/xml.py
+++ b/edify/library/data/xml.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-xml = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+xml = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for xml data-format identifier or content marker."""
diff --git a/edify/library/data/yaml.py b/edify/library/data/yaml.py
index 4b67953..01cd897 100644
--- a/edify/library/data/yaml.py
+++ b/edify/library/data/yaml.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-yaml = RegexBackedPattern(r"^[A-Za-z0-9_.\-/+]{2,256}$")
+yaml = (
+ Pattern()
+ .start_of_input()
+ .between(2, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .char("+")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for yaml data-format identifier or content marker."""
diff --git a/edify/library/document/docx.py b/edify/library/document/docx.py
index cc503f3..ad559bd 100644
--- a/edify/library/document/docx.py
+++ b/edify/library/document/docx.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-docx = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+docx = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a docx document identifier or file name."""
diff --git a/edify/library/document/epub.py b/edify/library/document/epub.py
index ea09d30..d3e547d 100644
--- a/edify/library/document/epub.py
+++ b/edify/library/document/epub.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-epub = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+epub = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a epub document identifier or file name."""
diff --git a/edify/library/document/mobi.py b/edify/library/document/mobi.py
index eb7e967..406923c 100644
--- a/edify/library/document/mobi.py
+++ b/edify/library/document/mobi.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-mobi = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+mobi = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a mobi document identifier or file name."""
diff --git a/edify/library/document/odt.py b/edify/library/document/odt.py
index 41ec7f0..b4fe810 100644
--- a/edify/library/document/odt.py
+++ b/edify/library/document/odt.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-odt = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+odt = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a odt document identifier or file name."""
diff --git a/edify/library/document/pdf.py b/edify/library/document/pdf.py
index 3b5f5c3..871ffa7 100644
--- a/edify/library/document/pdf.py
+++ b/edify/library/document/pdf.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-pdf = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+pdf = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a pdf document identifier or file name."""
diff --git a/edify/library/document/pptx.py b/edify/library/document/pptx.py
index 906e94b..c95be95 100644
--- a/edify/library/document/pptx.py
+++ b/edify/library/document/pptx.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-pptx = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+pptx = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a pptx document identifier or file name."""
diff --git a/edify/library/document/readme.py b/edify/library/document/readme.py
index 5c52ba7..f0ae663 100644
--- a/edify/library/document/readme.py
+++ b/edify/library/document/readme.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-readme = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+readme = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a readme document identifier or file name."""
diff --git a/edify/library/document/svg.py b/edify/library/document/svg.py
index 261d3a4..bf28220 100644
--- a/edify/library/document/svg.py
+++ b/edify/library/document/svg.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-svg = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+svg = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a svg document identifier or file name."""
diff --git a/edify/library/document/xlsx.py b/edify/library/document/xlsx.py
index 417c154..63c3af4 100644
--- a/edify/library/document/xlsx.py
+++ b/edify/library/document/xlsx.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-xlsx = RegexBackedPattern(r"^[A-Za-z0-9_.\-/]{1,256}$")
+xlsx = (
+ Pattern()
+ .start_of_input()
+ .between(1, 256)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char(".")
+ .char("-")
+ .char("/")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a xlsx document identifier or file name."""
diff --git a/edify/library/financial/card.py b/edify/library/financial/card.py
index 53c38ce..ba2ba07 100644
--- a/edify/library/financial/card.py
+++ b/edify/library/financial/card.py
@@ -2,9 +2,27 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-card = RegexBackedPattern(r"^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{1,7}$")
+card = (
+ Pattern()
+ .start_of_input()
+ .exactly(4)
+ .digit()
+ .optional()
+ .any_of_chars("- ")
+ .exactly(4)
+ .digit()
+ .optional()
+ .any_of_chars("- ")
+ .exactly(4)
+ .digit()
+ .optional()
+ .any_of_chars("- ")
+ .between(1, 7)
+ .digit()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for credit-card number shape:
groups of 4 digits with optional dash/space separators, 13-19 digits total.
"""
diff --git a/edify/library/financial/wallet.py b/edify/library/financial/wallet.py
index 5acf7ec..6d6b106 100644
--- a/edify/library/financial/wallet.py
+++ b/edify/library/financial/wallet.py
@@ -2,18 +2,99 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
-
-wallet = RegexBackedPattern(
- r"^(?:"
- r"[13][a-km-zA-HJ-NP-Z1-9]{25,34}"
- r"|bc1[a-z0-9]{25,89}"
- r"|0x[a-fA-F0-9]{40}"
- r"|[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}"
- r"|D[5-9A-HJ-NP-U][1-9A-HJ-NP-Za-km-z]{32}"
- r"|X[1-9A-HJ-NP-Za-km-z]{33}"
- r")$"
+from edify import Pattern, any_of
+
+_bitcoin_legacy = (
+ Pattern()
+ .any_of_chars("13")
+ .between(25, 34)
+ .any_of()
+ .range("a", "k")
+ .range("m", "z")
+ .range("A", "H")
+ .range("J", "N")
+ .range("P", "Z")
+ .range("1", "9")
+ .end()
+)
+
+_bitcoin_bech32 = (
+ Pattern()
+ .string("bc1")
+ .between(25, 89)
+ .any_of()
+ .range("a", "z")
+ .range("0", "9")
+ .end()
+)
+
+_ethereum = (
+ Pattern()
+ .string("0x")
+ .exactly(40)
+ .any_of()
+ .range("a", "f")
+ .range("A", "F")
+ .range("0", "9")
+ .end()
+)
+
+_litecoin = (
+ Pattern()
+ .any_of_chars("LM3")
+ .between(26, 33)
+ .any_of()
+ .range("a", "k")
+ .range("m", "z")
+ .range("A", "H")
+ .range("J", "N")
+ .range("P", "Z")
+ .range("1", "9")
+ .end()
+)
+
+_dogecoin = (
+ Pattern()
+ .char("D")
+ .any_of()
+ .range("5", "9")
+ .range("A", "H")
+ .range("J", "N")
+ .range("P", "U")
+ .end()
+ .exactly(32)
+ .any_of()
+ .range("1", "9")
+ .range("A", "H")
+ .range("J", "N")
+ .range("P", "Z")
+ .range("a", "k")
+ .range("m", "z")
+ .end()
+)
+
+_dash = (
+ Pattern()
+ .char("X")
+ .exactly(33)
+ .any_of()
+ .range("1", "9")
+ .range("A", "H")
+ .range("J", "N")
+ .range("P", "Z")
+ .range("a", "k")
+ .range("m", "z")
+ .end()
+)
+
+wallet = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_bitcoin_legacy, _bitcoin_bech32, _ethereum, _litecoin, _dogecoin, _dash))
+ .end_of_input()
)
"""Callable :class:`Pattern` for cryptocurrency-wallet address shapes:
Bitcoin (legacy, SegWit, Bech32), Ethereum, Litecoin, Dogecoin, Dash.
"""
+
+del _bitcoin_legacy, _bitcoin_bech32, _ethereum, _litecoin, _dogecoin, _dash
diff --git a/edify/library/geo/altitude.py b/edify/library/geo/altitude.py
index 38b89de..cdbe2eb 100644
--- a/edify/library/geo/altitude.py
+++ b/edify/library/geo/altitude.py
@@ -2,7 +2,25 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-altitude = RegexBackedPattern(r"^-?\d+(?:\.\d+)?\s?(?:m|ft|km|mi)?$")
+altitude = (
+ Pattern()
+ .start_of_input()
+ .optional()
+ .char("-")
+ .one_or_more()
+ .digit()
+ .optional()
+ .group()
+ .char(".")
+ .one_or_more()
+ .digit()
+ .end()
+ .optional()
+ .whitespace_char()
+ .optional()
+ .any_of("m", "ft", "km", "mi")
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a signed altitude value with optional unit."""
diff --git a/edify/library/geo/coordinate.py b/edify/library/geo/coordinate.py
index a6bf066..d69bdd4 100644
--- a/edify/library/geo/coordinate.py
+++ b/edify/library/geo/coordinate.py
@@ -2,10 +2,81 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-coordinate = RegexBackedPattern(
- r"^-?(?:90(?:\.0+)?|[0-8]?\d(?:\.\d+)?)\s*,\s*"
- r"-?(?:180(?:\.0+)?|(?:1[0-7]\d|[0-9]?\d)(?:\.\d+)?)$"
+_lat_ninety = (
+ Pattern()
+ .string("90")
+ .optional()
+ .group()
+ .char(".")
+ .one_or_more()
+ .char("0")
+ .end()
+)
+
+_lat_below_ninety = (
+ Pattern()
+ .optional()
+ .range("0", "8")
+ .digit()
+ .optional()
+ .group()
+ .char(".")
+ .one_or_more()
+ .digit()
+ .end()
+)
+
+_lon_one_eighty = (
+ Pattern()
+ .string("180")
+ .optional()
+ .group()
+ .char(".")
+ .one_or_more()
+ .char("0")
+ .end()
+)
+
+_lon_below_one_eighty_integer = any_of(
+ Pattern().char("1").range("0", "7").digit(),
+ Pattern().optional().range("0", "9").digit(),
+)
+
+_lon_below_one_eighty = (
+ Pattern()
+ .subexpression(_lon_below_one_eighty_integer)
+ .optional()
+ .group()
+ .char(".")
+ .one_or_more()
+ .digit()
+ .end()
+)
+
+coordinate = (
+ Pattern()
+ .start_of_input()
+ .optional()
+ .char("-")
+ .subexpression(any_of(_lat_ninety, _lat_below_ninety))
+ .zero_or_more()
+ .whitespace_char()
+ .char(",")
+ .zero_or_more()
+ .whitespace_char()
+ .optional()
+ .char("-")
+ .subexpression(any_of(_lon_one_eighty, _lon_below_one_eighty))
+ .end_of_input()
)
"""Callable :class:`Pattern` for the ``latitude,longitude`` coordinate shape."""
+
+del (
+ _lat_ninety,
+ _lat_below_ninety,
+ _lon_one_eighty,
+ _lon_below_one_eighty_integer,
+ _lon_below_one_eighty,
+)
diff --git a/edify/library/geo/place.py b/edify/library/geo/place.py
index 8b187f3..340a2ab 100644
--- a/edify/library/geo/place.py
+++ b/edify/library/geo/place.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-place = RegexBackedPattern(r"^[A-Za-z][A-Za-z .,'\-]{1,99}$")
+place = (
+ Pattern()
+ .start_of_input()
+ .letter()
+ .between(1, 99)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .char(" ")
+ .char(".")
+ .char(",")
+ .char("'")
+ .char("-")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive place-name shape."""
diff --git a/edify/library/geo/plus.py b/edify/library/geo/plus.py
index fe2176f..ca96a39 100644
--- a/edify/library/geo/plus.py
+++ b/edify/library/geo/plus.py
@@ -2,7 +2,27 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-plus = RegexBackedPattern(r"^[23456789CFGHJMPQRVWX]{2,8}\+[23456789CFGHJMPQRVWX]{2,3}(?:\s+.+)?$")
+_PLUS_CODE_ALPHABET = "23456789CFGHJMPQRVWX"
+
+plus = (
+ Pattern()
+ .start_of_input()
+ .between(2, 8)
+ .any_of_chars(_PLUS_CODE_ALPHABET)
+ .char("+")
+ .between(2, 3)
+ .any_of_chars(_PLUS_CODE_ALPHABET)
+ .optional()
+ .group()
+ .one_or_more()
+ .whitespace_char()
+ .one_or_more()
+ .any_char()
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a Google Plus Code (Open Location Code)."""
+
+del _PLUS_CODE_ALPHABET
diff --git a/edify/library/grammar/abnf.py b/edify/library/grammar/abnf.py
index a87f3d8..21c35ff 100644
--- a/edify/library/grammar/abnf.py
+++ b/edify/library/grammar/abnf.py
@@ -2,7 +2,40 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-abnf = RegexBackedPattern(r"^[A-Za-z0-9_\-<>:=|*+?()\[\]{}\s.'\"/;,]{4,65536}$")
+abnf = (
+ Pattern()
+ .start_of_input()
+ .between(4, 65536)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char("-")
+ .char("<")
+ .char(">")
+ .char(":")
+ .char("=")
+ .char("|")
+ .char("*")
+ .char("+")
+ .char("?")
+ .char("(")
+ .char(")")
+ .char("[")
+ .char("]")
+ .char("{")
+ .char("}")
+ .whitespace_char()
+ .char(".")
+ .char("'")
+ .char('"')
+ .char("/")
+ .char(";")
+ .char(",")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for abnf grammar-specification content."""
diff --git a/edify/library/grammar/bnf.py b/edify/library/grammar/bnf.py
index 40ec7c6..aeb81b6 100644
--- a/edify/library/grammar/bnf.py
+++ b/edify/library/grammar/bnf.py
@@ -2,7 +2,40 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-bnf = RegexBackedPattern(r"^[A-Za-z0-9_\-<>:=|*+?()\[\]{}\s.'\"/;,]{4,65536}$")
+bnf = (
+ Pattern()
+ .start_of_input()
+ .between(4, 65536)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char("-")
+ .char("<")
+ .char(">")
+ .char(":")
+ .char("=")
+ .char("|")
+ .char("*")
+ .char("+")
+ .char("?")
+ .char("(")
+ .char(")")
+ .char("[")
+ .char("]")
+ .char("{")
+ .char("}")
+ .whitespace_char()
+ .char(".")
+ .char("'")
+ .char('"')
+ .char("/")
+ .char(";")
+ .char(",")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for bnf grammar-specification content."""
diff --git a/edify/library/grammar/ebnf.py b/edify/library/grammar/ebnf.py
index 2492688..8055306 100644
--- a/edify/library/grammar/ebnf.py
+++ b/edify/library/grammar/ebnf.py
@@ -2,7 +2,40 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-ebnf = RegexBackedPattern(r"^[A-Za-z0-9_\-<>:=|*+?()\[\]{}\s.'\"/;,]{4,65536}$")
+ebnf = (
+ Pattern()
+ .start_of_input()
+ .between(4, 65536)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char("-")
+ .char("<")
+ .char(">")
+ .char(":")
+ .char("=")
+ .char("|")
+ .char("*")
+ .char("+")
+ .char("?")
+ .char("(")
+ .char(")")
+ .char("[")
+ .char("]")
+ .char("{")
+ .char("}")
+ .whitespace_char()
+ .char(".")
+ .char("'")
+ .char('"')
+ .char("/")
+ .char(";")
+ .char(",")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for ebnf grammar-specification content."""
diff --git a/edify/library/grammar/peg.py b/edify/library/grammar/peg.py
index 74d1ffc..f06ea00 100644
--- a/edify/library/grammar/peg.py
+++ b/edify/library/grammar/peg.py
@@ -2,7 +2,40 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-peg = RegexBackedPattern(r"^[A-Za-z0-9_\-<>:=|*+?()\[\]{}\s.'\"/;,]{4,65536}$")
+peg = (
+ Pattern()
+ .start_of_input()
+ .between(4, 65536)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char("-")
+ .char("<")
+ .char(">")
+ .char(":")
+ .char("=")
+ .char("|")
+ .char("*")
+ .char("+")
+ .char("?")
+ .char("(")
+ .char(")")
+ .char("[")
+ .char("]")
+ .char("{")
+ .char("}")
+ .whitespace_char()
+ .char(".")
+ .char("'")
+ .char('"')
+ .char("/")
+ .char(";")
+ .char(",")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for peg grammar-specification content."""
diff --git a/edify/library/grammar/pest.py b/edify/library/grammar/pest.py
index be083ae..dc1903f 100644
--- a/edify/library/grammar/pest.py
+++ b/edify/library/grammar/pest.py
@@ -2,7 +2,40 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-pest = RegexBackedPattern(r"^[A-Za-z0-9_\-<>:=|*+?()\[\]{}\s.'\"/;,]{4,65536}$")
+pest = (
+ Pattern()
+ .start_of_input()
+ .between(4, 65536)
+ .any_of()
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .char("_")
+ .char("-")
+ .char("<")
+ .char(">")
+ .char(":")
+ .char("=")
+ .char("|")
+ .char("*")
+ .char("+")
+ .char("?")
+ .char("(")
+ .char(")")
+ .char("[")
+ .char("]")
+ .char("{")
+ .char("}")
+ .whitespace_char()
+ .char(".")
+ .char("'")
+ .char('"')
+ .char("/")
+ .char(";")
+ .char(",")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for pest grammar-specification content."""
diff --git a/edify/library/medical/medical.py b/edify/library/medical/medical.py
index ef5407f..e702681 100644
--- a/edify/library/medical/medical.py
+++ b/edify/library/medical/medical.py
@@ -2,14 +2,28 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
-
-medical = RegexBackedPattern(
- r"^(?:"
- r"\d{6,18}"
- r"|[A-TV-Z][0-9][A-Z0-9](?:\.[A-Z0-9]{1,4})?"
- r"|\d{10}"
- r"|\d{1,7}-\d"
- r")$"
+from edify import Pattern, any_of
+
+_snomed = Pattern().between(6, 18).digit()
+_icd = (
+ Pattern()
+ .any_of().range("A", "T").range("V", "Z").end()
+ .digit()
+ .any_of().range("A", "Z").range("0", "9").end()
+ .optional()
+ .group()
+ .char(".")
+ .between(1, 4)
+ .any_of().range("A", "Z").range("0", "9").end()
+ .end()
+)
+_npi = Pattern().exactly(10).digit()
+_loinc = Pattern().between(1, 7).digit().char("-").digit()
+
+medical = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_snomed, _icd, _npi, _loinc))
+ .end_of_input()
)
"""Callable :class:`Pattern` for medical-coding-system codes."""
diff --git a/edify/library/numeric/fraction.py b/edify/library/numeric/fraction.py
index 3a7123b..5cefd27 100644
--- a/edify/library/numeric/fraction.py
+++ b/edify/library/numeric/fraction.py
@@ -2,9 +2,27 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-fraction = RegexBackedPattern(r"^-?(?:\d+\s+)?\d+/\d+$")
+fraction = (
+ Pattern()
+ .start_of_input()
+ .optional()
+ .char("-")
+ .optional()
+ .group()
+ .one_or_more()
+ .digit()
+ .one_or_more()
+ .whitespace_char()
+ .end()
+ .one_or_more()
+ .digit()
+ .char("/")
+ .one_or_more()
+ .digit()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a fraction shape:
``numerator/denominator`` with optional whole-number prefix.
"""
diff --git a/edify/library/product/barcode.py b/edify/library/product/barcode.py
index 5502943..d107fe5 100644
--- a/edify/library/product/barcode.py
+++ b/edify/library/product/barcode.py
@@ -2,7 +2,16 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-barcode = RegexBackedPattern(r"^[A-Z0-9]{6,48}$")
+barcode = (
+ Pattern()
+ .start_of_input()
+ .between(6, 48)
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a generic barcode value shape."""
diff --git a/edify/library/product/gtin.py b/edify/library/product/gtin.py
index deceb6a..b0d9198 100644
--- a/edify/library/product/gtin.py
+++ b/edify/library/product/gtin.py
@@ -2,7 +2,19 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-gtin = RegexBackedPattern(r"^\d{8}$|^\d{12}$|^\d{13}$|^\d{14}$")
+gtin = (
+ Pattern()
+ .start_of_input()
+ .subexpression(
+ any_of(
+ Pattern().exactly(8).digit(),
+ Pattern().exactly(12).digit(),
+ Pattern().exactly(13).digit(),
+ Pattern().exactly(14).digit(),
+ )
+ )
+ .end_of_input()
+)
"""Callable :class:`Pattern` for the GTIN family: 8-, 12-, 13-, or 14-digit barcode number."""
diff --git a/edify/library/product/mpn.py b/edify/library/product/mpn.py
index cfe42ac..8afcbe2 100644
--- a/edify/library/product/mpn.py
+++ b/edify/library/product/mpn.py
@@ -2,7 +2,23 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-mpn = RegexBackedPattern(r"^[A-Z0-9][A-Z0-9\-_.]{1,63}$")
+mpn = (
+ Pattern()
+ .start_of_input()
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .between(1, 63)
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .char("-")
+ .char("_")
+ .char(".")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive Manufacturer Part Number."""
diff --git a/edify/library/publishing/arxiv.py b/edify/library/publishing/arxiv.py
index 481479a..c65f7cb 100644
--- a/edify/library/publishing/arxiv.py
+++ b/edify/library/publishing/arxiv.py
@@ -2,9 +2,47 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-arxiv = RegexBackedPattern(
- r"^(?:\d{4}\.\d{4,5}(?:v\d+)?|[a-z]{2,10}(?:\.[A-Z]{2})?/\d{7}(?:v\d+)?)$"
+_new = (
+ Pattern()
+ .exactly(4)
+ .digit()
+ .char(".")
+ .between(4, 5)
+ .digit()
+ .optional()
+ .group()
+ .char("v")
+ .one_or_more()
+ .digit()
+ .end()
+)
+_old = (
+ Pattern()
+ .between(2, 10)
+ .lowercase()
+ .optional()
+ .group()
+ .char(".")
+ .exactly(2)
+ .uppercase()
+ .end()
+ .char("/")
+ .exactly(7)
+ .digit()
+ .optional()
+ .group()
+ .char("v")
+ .one_or_more()
+ .digit()
+ .end()
+)
+
+arxiv = (
+ Pattern()
+ .start_of_input()
+ .subexpression(any_of(_new, _old))
+ .end_of_input()
)
"""Callable :class:`Pattern` for an arXiv identifier."""
diff --git a/edify/library/publishing/doi.py b/edify/library/publishing/doi.py
index ce7c34e..39689d8 100644
--- a/edify/library/publishing/doi.py
+++ b/edify/library/publishing/doi.py
@@ -2,7 +2,29 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-doi = RegexBackedPattern(r"^10\.\d{4,9}/[-._;()/:A-Za-z0-9]+$")
+doi = (
+ Pattern()
+ .start_of_input()
+ .string("10.")
+ .between(4, 9)
+ .digit()
+ .char("/")
+ .one_or_more()
+ .any_of()
+ .char("-")
+ .char(".")
+ .char("_")
+ .char(";")
+ .char("(")
+ .char(")")
+ .char("/")
+ .char(":")
+ .range("A", "Z")
+ .range("a", "z")
+ .range("0", "9")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for the DOI shape."""
diff --git a/edify/library/publishing/isbn.py b/edify/library/publishing/isbn.py
index 379c9a8..ac9e170 100644
--- a/edify/library/publishing/isbn.py
+++ b/edify/library/publishing/isbn.py
@@ -2,7 +2,36 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern, any_of
-isbn = RegexBackedPattern(r"^(?:\d[- ]?){9}[\dXx]$|^(?:\d[- ]?){12}\d$")
+_isbn10 = (
+ Pattern()
+ .start_of_input()
+ .exactly(9)
+ .group()
+ .digit()
+ .optional()
+ .any_of_chars("- ")
+ .end()
+ .any_of()
+ .digit()
+ .char("X")
+ .char("x")
+ .end()
+ .end_of_input()
+)
+_isbn13 = (
+ Pattern()
+ .start_of_input()
+ .exactly(12)
+ .group()
+ .digit()
+ .optional()
+ .any_of_chars("- ")
+ .end()
+ .digit()
+ .end_of_input()
+)
+
+isbn = any_of(_isbn10, _isbn13)
"""Callable :class:`Pattern` for ISBN-10 or ISBN-13 shape."""
diff --git a/edify/library/publishing/issn.py b/edify/library/publishing/issn.py
index 53216d7..98a2998 100644
--- a/edify/library/publishing/issn.py
+++ b/edify/library/publishing/issn.py
@@ -2,7 +2,21 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-issn = RegexBackedPattern(r"^\d{4}-\d{3}[\dXx]$")
+issn = (
+ Pattern()
+ .start_of_input()
+ .exactly(4)
+ .digit()
+ .char("-")
+ .exactly(3)
+ .digit()
+ .any_of()
+ .digit()
+ .char("X")
+ .char("x")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for the ISSN shape."""
diff --git a/edify/library/publishing/pmc.py b/edify/library/publishing/pmc.py
index dc97b0b..f70e8a8 100644
--- a/edify/library/publishing/pmc.py
+++ b/edify/library/publishing/pmc.py
@@ -2,7 +2,14 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-pmc = RegexBackedPattern(r"^PMC\d{1,9}$")
+pmc = (
+ Pattern()
+ .start_of_input()
+ .string("PMC")
+ .between(1, 9)
+ .digit()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a PubMed Central identifier."""
diff --git a/edify/library/publishing/pmid.py b/edify/library/publishing/pmid.py
index 968ad9a..85134ef 100644
--- a/edify/library/publishing/pmid.py
+++ b/edify/library/publishing/pmid.py
@@ -2,7 +2,13 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-pmid = RegexBackedPattern(r"^\d{1,8}$")
+pmid = (
+ Pattern()
+ .start_of_input()
+ .between(1, 8)
+ .digit()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a PubMed identifier (1-8 digits)."""
diff --git a/edify/library/transport/aircraft.py b/edify/library/transport/aircraft.py
index 86ff2cd..a49f949 100644
--- a/edify/library/transport/aircraft.py
+++ b/edify/library/transport/aircraft.py
@@ -2,7 +2,20 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-aircraft = RegexBackedPattern(r"^[A-Z]{1,2}-?[A-Z0-9]{1,5}$")
+aircraft = (
+ Pattern()
+ .start_of_input()
+ .between(1, 2)
+ .uppercase()
+ .optional()
+ .char("-")
+ .between(1, 5)
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for an aircraft-registration mark."""
diff --git a/edify/library/transport/vehicle.py b/edify/library/transport/vehicle.py
index 7fc3c0c..1bc17b3 100644
--- a/edify/library/transport/vehicle.py
+++ b/edify/library/transport/vehicle.py
@@ -2,7 +2,22 @@
from __future__ import annotations
-from edify.library._support.regex import RegexBackedPattern
+from edify import Pattern
-vehicle = RegexBackedPattern(r"^[A-Z0-9][A-Z0-9\- ]{3,17}$")
+vehicle = (
+ Pattern()
+ .start_of_input()
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .end()
+ .between(3, 17)
+ .any_of()
+ .range("A", "Z")
+ .range("0", "9")
+ .char("-")
+ .char(" ")
+ .end()
+ .end_of_input()
+)
"""Callable :class:`Pattern` for a permissive transport-vehicle identifier."""