Naming a Unicode property: \p{...}¶
One line: \p{...} asks a question about a character — is it a letter (\p{L}), is it Greek (\p{Greek}), is it alphabetic (\p{Alphabetic}) — instead of listing the characters you hope are; the engines do not disagree about what Greek is, they disagree about how to spell it, and two of them accept a misspelling without a word.
Level: 301 · you write patterns against names, amounts and free text that are not only ASCII
Keywords on this page: \p{…} · \X · [[:alpha:]] · […] — Topics: Unicode text
What it replaces¶
[A-Za-z] is a list. \p{L} is a question the engine answers from the Unicode Character Database it already ships. The list has 52 entries and is wrong for most of the world's names; the question is right by construction and stays right when the table is updated.
| You mean | The list you probably wrote | The property |
|---|---|---|
| a letter | [A-Za-z] |
\p{L} |
| a decimal digit | [0-9] |
\p{Nd} |
| an uppercase letter | [A-Z] |
\p{Lu} |
| punctuation | [.,;:!?-] |
\p{P} |
| whitespace | [ \t] |
\p{White_Space} |
The rest of this page is about which of those the engine in front of you accepts, and what it does with the ones it accepts but does not understand.
The three things a property can name¶
Perl has the fullest and plainest spelling, so it is the clearest place to see all three kinds at once. Every character below is written and reported as a code point, never printed: an answer key that printed GREEK SMALL LETTER ALPHA would depend on the terminal, the locale and the runtime's build. The cast is deliberately boring — Latin, Greek, Cyrillic, Han, two digits, two spaces and a dash — because every one of those has had the same general category and the same script since the 1990s, and a key built on a character whose classification moved recently would differ between Perl 5.38 and 5.42, or between Python 3.12 and 3.14.
Verified output of property_kinds_pl.pl — regenerated by tools/run_examples.py, never hand-typed.
the cast
0009 CHARACTER TABULATION
0037 DIGIT SEVEN
0041 LATIN CAPITAL LETTER A
00A0 NO-BREAK SPACE
00E9 LATIN SMALL LETTER E WITH ACUTE
03B1 GREEK SMALL LETTER ALPHA
0394 GREEK CAPITAL LETTER DELTA
0416 CYRILLIC CAPITAL LETTER ZHE
0660 ARABIC-INDIC DIGIT ZERO
2014 EM DASH
4E2D CJK UNIFIED IDEOGRAPH-4E2D
general category -- what KIND of character it is
\p{L} 0041 00E9 03B1 0394 0416 4E2D
\p{Lu} 0041 0394 0416
\p{Ll} 00E9 03B1
\p{Lo} 4E2D
\p{Nd} 0037 0660
\p{Zs} 00A0
\p{P} 2014
\P{L} 0009 0037 00A0 0660 2014
script -- what WRITING SYSTEM it belongs to
\p{Greek} 03B1 0394
\p{Cyrillic} 0416
\p{Script=Han} 4E2D
\p{Latin} 0041 00E9
binary property -- one yes/no fact the standard records
\p{Alphabetic} 0041 00E9 03B1 0394 0416 4E2D
\p{Uppercase} 0041 0394 0416
\p{White_Space} 0009 00A0
composed inside a character class
[\p{L}\p{Nd}] 0037 0041 00E9 03B1 0394 0416 0660 4E2D
[\p{Greek}\p{Cyrillic}] 03B1 0394 0416
[^\p{L}] 0009 0037 00A0 0660 2014
grapheme clusters in U+0065 U+0301
\X 1
. 2
Four things to take from that run.
A general category is what kind of character it is. One letter is the union (\p{L} took all six letters), two letters is the subdivision (\p{Lu} took the three uppercase ones, \p{Lo} took only U+4E2D). Every character has exactly one.
A script is what writing system it belongs to. \p{Greek} took U+03B1 and U+0394 and left U+4E2D alone. A script is not "foreign" and not "non-ASCII" — U+0041 LATIN CAPITAL LETTER A is \p{Latin}, and so is U+00E9.
A binary property is one yes/no fact, on a different axis from the category. Look at the two whitespace rows: \p{Zs} took only U+00A0, while \p{White_Space} took U+0009 CHARACTER TABULATION as well. Tab's general category is Cc, a control character; the standard separately records that it is whitespace. A binary property is not a coarser category, it is a different question.
\P{...} is the negation and a class composes them. Capital P inverts — \P{L} picked up exactly the five non-letters. Inside [...] several properties union, and [^\p{L}] is the same set as \P{L}.
Who has it, and under what spelling¶
Verified output of property_spellings_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
does it COMPILE? python perl node u ruby java go
\p{L} no yes yes yes yes yes
\pL no yes no yes yes yes
\p{Lu} no yes yes yes yes yes
\p{Nd} no yes yes yes yes yes
\p{P} no yes yes yes yes yes
\p{Greek} no yes no yes no yes
\p{Script=Greek} no yes yes no yes no
\p{sc=Greek} no yes yes no yes no
\p{IsGreek} no yes no no yes no
\p{Alphabetic} no yes yes yes no no
\p{IsAlphabetic} no yes no no yes no
\p{White_Space} no yes yes yes no no
\p{Alpha} no yes yes yes yes no
[\p{L}\p{Nd}] no yes yes yes yes yes
\X no yes no yes yes no
and where it compiles, does it MEAN what it says?
(the two answers are U+0041 and U+03B1; a dash is: did not compile)
spelling python perl node u ruby java go
\pL - - yes yes - - no no yes yes yes yes
\p{Alpha} - - yes yes yes yes yes yes yes no - -
\p{Greek} - - no yes - - no yes - - no yes
compiled, but ruby warned while doing it:
\pL
and what go calls a property it has never heard of:
invalid character class range
invalid escape sequence
The general category rows are the only ones where everybody agrees: \p{L}, \p{Lu}, \p{Nd}, \p{P} compile in all five engines that have properties at all. Below that, three stripes:
- The bare script name
\p{Greek}— Perl, Ruby and Go take it; Java and JavaScript refuse it. - The
Script=form — Perl, JavaScript and Java take it; Ruby and Go refuse it. - Perl's
Isprefix — Perl and Java take it, and no one else; PCRE2 refuses it too, which is the divergence that matters most and is measured further down.
Binary properties are where Go stops. \p{Alphabetic} and \p{White_Space} compile in Perl, JavaScript and Ruby, need the Is prefix in Java, and do not exist in Go's regexp at all. The last block is what Go says about them: invalid character class range, which does not sound like "I have never heard of that property" but is what it means, and invalid escape sequence for \X. Neither message names a property, so a reader who does not already know tends to go looking for a typo in the brackets.
The second table is the more important one, because compiling is not understanding:
- Ruby accepts
\pLand means the literal textpL. It matched neither U+0041 nor U+03B1. Ruby does warn while compiling it, and the third block is that warning — captured as a flag rather than as a message, because the message carries a file name and a line number. JavaScript underurefuses\pLoutright; Perl, Java and Go read it as\p{L}, which is what it was meant to be. \p{Alpha}compiles in four engines and means two different things. Perl, JavaScript and Ruby read it as the Unicode binary propertyAlphabeticand match U+03B1. Java reads it as the POSIX class over US-ASCII and does not.
Two silent failures¶
Python's re has no properties at all — and refuses to pretend¶
Verified output of property_absent_py.py — regenerated by tools/run_examples.py, never hand-typed.
re, asked for a Unicode property
\p{L} re.error: bad escape \p at position 0
\P{L} re.error: bad escape \P at position 0
[\p{L}] re.error: bad escape \p at position 1
\pL re.error: bad escape \p at position 0
\p{Greek} re.error: bad escape \p at position 0
what the standard library offers instead
code point isalpha() isdecimal() category
0009 False False Cc
0037 False True Nd
0041 True False Lu
00A0 False False Zs
00E9 True False Ll
03B1 True False Ll
0394 True False Lu
0416 True False Lu
0660 False True Nd
2014 False False Pd
4E2D True False Lo
one script, spelled as an explicit range
[U+0370-U+03FF] matches: 03B1 0394
bad escape \p at position 0 arrives from re.compile, with no subject in sight, and it is the same message on Python 3.12 and 3.14. That is the good outcome: a pattern lifted out of a Perl or PCRE codebase fails on the line that compiles it, not six months later on the one customer whose name is Greek. It is the same kindness Python shows for forward references.
What to reach for instead, in order of how close it stays to the original pattern:
- The third-party
regexmodule, which is a drop-in replacement forreand does have\p{...}. Nothing on this page uses it, because a lesson whose examples need an install is teaching something the reader's interpreter cannot do. - The
strpredicates andunicodedata. In the table above,isalpha()is True on exactly the characters whose category begins withL, andisdecimal()on exactly the two whose category isNd— so they stand in for\p{L}and\p{Nd}respectively, not for the binary properties of the same name.unicodedata.category()hands you the two-letter category itself, which turns a general-category test into a comparison rather than a pattern. - An explicit range, which is exact when you can name the block and wrong the moment a script spills outside one.
[U+0370-U+03FF]caught both Greek letters in the cast, and would miss every polytonic Greek letter in U+1F00..U+1FFF. A block is not a script; the range is a stand-in, not a translation.
JavaScript needs the u flag, and says nothing without it¶
Verified output of property_u_flag_js.js — regenerated by tools/run_examples.py, never hand-typed.
without the u flag, \p is the letter p
/^\p{L}$/ on the 4-character text p{L} true
/^\p{L}$/ on U+03B1 false
/^\P{L}$/ on the 4-character text P{L} true
with the u flag, does it compile?
\p{L} compiles
\pL refused
\p{Lu} compiles
\p{Nd} compiles
\p{P} compiles
\p{Greek} refused
\p{Script=Greek} compiles
\p{sc=Greek} compiles
\p{Script_Extensions=Greek} compiles
\p{Alphabetic} compiles
\p{Alpha} compiles
\p{White_Space} compiles
\p{gc=Lu} compiles
[\p{L}\p{Nd}] compiles
\X refused
and what the ones that compile match
\p{L} 0041 00E9 03B1 0394 0416 4E2D
\p{Lu} 0041 0394 0416
\p{Nd} 0037 0660
\p{P} 2014
\p{Script=Greek} 03B1 0394
\p{sc=Greek} 03B1 0394
\p{Script_Extensions=Greek} 03B1 0394
\p{Alphabetic} 0041 00E9 03B1 0394 0416 4E2D
\p{Alpha} 0041 00E9 03B1 0394 0416 4E2D
\p{White_Space} 0009 00A0
\p{gc=Lu} 0041 0394 0416
[\p{L}\p{Nd}] 0037 0041 00E9 03B1 0394 0416 0660 4E2D
/^\p{L}$/ matched the four-character text p{L} and did not match U+03B1. In a legacy (non-u) pattern, Annex B's identity-escape rule turns \p into the plain letter p, and {L} is not a valid quantifier so it stays literal too. Nothing warns. A pattern written from Perl habits, tested against ASCII, passes review, ships, and quietly never matches a Greek name.
With the flag, properties are ES2018 and the rules tighten: \pL without braces is refused, \p{Greek} is refused because JavaScript wants scripts spelled \p{Script=Greek} or \p{sc=Greek}, and \p{Alpha} is accepted as an alias for the binary property Alphabetic — so it matches U+03B1, which is the exact opposite of what that same spelling means in Java.
ES2024's v flag adds set operations inside a class and properties of strings. This page does not probe it: the answer key has to hold on both node versions this library runs against, and v is not in the older one.
Java: the same syntax, a different dictionary¶
Verified output of property_posix_alpha_java.java — regenerated by tools/run_examples.py, never hand-typed.
general category: the one spelling everybody agrees on
\p{L} 0041 00E9 03B1 0394 0416 4E2D
\pL 0041 00E9 03B1 0394 0416 4E2D
\p{Lu} 0041 0394 0416
\p{Nd} 0037 0660
\p{P} 2014
script: Java wants Is, or script=, and refuses the bare name
\p{Greek} refused: Unknown character property name {Greek}
\p{IsGreek} 03B1 0394
\p{script=Greek} 03B1 0394
\p{sc=Greek} 03B1 0394
\p{InGreek} 03B1 0394
\p{IsHan} 4E2D
binary property: Is again
\p{Alphabetic} refused: Unknown character property name {Alphabetic}
\p{IsAlphabetic} 0041 00E9 03B1 0394 0416 4E2D
\p{White_Space} refused: Unknown character property name {White_Space}
\p{IsWhite_Space} 0009 00A0
\p{gc=Lu} 0041 0394 0416
the POSIX names -- same syntax, US-ASCII meaning
\p{Alpha} 0041
\p{Digit} 0037
\p{Upper} 0041
\p{Punct} (nothing)
the same POSIX names under UNICODE_CHARACTER_CLASS, or inline (?U)
\p{Alpha} 0041 00E9 03B1 0394 0416 4E2D
\p{Digit} 0037 0660
\p{Upper} 0041 0394 0416
\p{Punct} 2014
grapheme clusters in U+0065 U+0301
\X 1
. 2
Java refuses \p{Greek} outright — Unknown character property name {Greek} — and wants \p{IsGreek}, \p{script=Greek} or \p{sc=Greek}. The same Is is needed for binary properties: \p{Alphabetic} is an error and \p{IsAlphabetic} is not. \p{InGreek} compiles too, and it is a block rather than a script: a block is a contiguous range of code points, a script is a property carried by each character. They agree on this cast and they part company as soon as a script spills out of its block, which is the same trap as the explicit range Python was left with above.
Then the collision this section exists for. \p{Alpha}, \p{Digit}, \p{Upper} and \p{Punct} are POSIX classes defined over US-ASCII. \p{Alpha} matched U+0041 and nothing else. \p{Punct} matched nothing at all, because EM DASH is not ASCII punctuation. Identical syntax to a Unicode property, a completely different meaning, no diagnostic — and a reviewer who knows Perl will read it as the Unicode one. Pattern.UNICODE_CHARACTER_CLASS, or inline (?U), re-points all four at Unicode, and the block after it measures exactly that.
Rust's regex accepts every spelling¶
Verified output of property_sets_rs_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
general category
\p{L} 0041 00E9 03B1 0394 0416 4E2D
\pL 0041 00E9 03B1 0394 0416 4E2D
\p{Lu} 0041 0394 0416
\p{Nd} 0037 0660
\p{Zs} 00A0
\p{P} 2014
\P{L} 0009 0037 00A0 0660 2014
script -- every spelling this page has met
\p{Greek} 03B1 0394
\p{Script=Greek} 03B1 0394
\p{sc=Greek} 03B1 0394
\p{IsGreek} 03B1 0394
\p{Script_Extensions=Greek} 03B1 0394
\p{Han} 4E2D
binary property
\p{Alphabetic} 0041 00E9 03B1 0394 0416 4E2D
\p{Uppercase} 0041 0394 0416
\p{White_Space} 0009 00A0
\p{Alpha} 0041 00E9 03B1 0394 0416 4E2D
\p{Any} 0009 0037 0041 00A0 00E9 03B1 0394 0416 0660 2014 4E2D
set operations inside a class -- regex alone among these engines
[\p{L}\p{Nd}] 0037 0041 00E9 03B1 0394 0416 0660 4E2D
[\p{Greek}&&\p{Lu}] 0394
[\p{L}--\p{Greek}] 0041 00E9 0416 4E2D
[\p{Greek}~~\p{Lu}] 0041 03B1 0416
no grapheme cluster escape in either crate
regex error: unrecognized escape sequence
fancy-regex Parsing error at position 0: Invalid escape: \X
regex is the most complete property implementation this library measures. Every script spelling this page has met compiles, and they all agree — the bare name, Script=, sc=, Perl's Is, and Script_Extensions= besides. Binary properties are there, \p{Alpha} means the Unicode one, and \p{Any} is every code point.
It is also the only engine here whose ordinary character class does set operations: [\p{Greek}&&\p{Lu}] intersected down to U+0394 alone, [\p{L}--\p{Greek}] subtracted the Greek letters out, and [\p{Greek}~~\p{Lu}] took the symmetric difference — every Greek or uppercase character that is not both. Perl reaches the same place with a separate construct, (?[ ... ]), and JavaScript's v flag adds one too; neither is measured on this page.
What regex does not have is \X, and neither does fancy-regex.
PCRE2 — and therefore ABAP¶
ABAP's PCRE addition is the PCRE2 library in the kernel, so PCRE2's answers are ABAP's answers. Each file below holds exactly one character, so that pcre2grep -l prints which characters matched using only ASCII:
Real run — PCRE2 10.48 (pcre2grep), macOS 26.6.2 x86-64, 2026-09-22. Each U_XXXX.txt contains the single character U+XXXX and nothing else.
$ pcre2grep -u -l '\p{Greek}' U_*.txt
U_0394.txt
U_03B1.txt
$ pcre2grep -u -l '\p{Script=Greek}' U_*.txt
U_0394.txt
U_03B1.txt
$ pcre2grep -u -l '\p{IsGreek}' U_*.txt
pcre2grep: Error in command-line regex at offset 11: unknown property after \P or \p
$ pcre2grep -u -l '\p{Alphabetic}' U_*.txt
U_0041.txt
U_00E9.txt
U_0394.txt
U_03B1.txt
U_0416.txt
U_4E2D.txt
$ pcre2grep -u -l '\p{Alpha}' U_*.txt
U_0041.txt
U_00E9.txt
U_0394.txt
U_03B1.txt
U_0416.txt
U_4E2D.txt
$ pcre2grep -u -l '\p{Nd}' U_*.txt
U_0037.txt
U_0660.txt
$ printf 'e\xcc\x81\n' > combining.txt
$ pcre2grep -u -o '\X' combining.txt | wc -l
1
$ pcre2grep -u -o '.' combining.txt | wc -l
2
The last three commands are the grapheme-cluster question again: combining.txt holds U+0065 LATIN SMALL LETTER E followed by U+0301 COMBINING ACUTE ACCENT, and PCRE2 counts two dots and one \X, exactly as Perl and Java did.
PCRE2 takes the bare script name and the Script= form, has the binary properties, and reads \p{Alpha} as the Unicode one — so ABAP agrees with Perl, Ruby, JavaScript and Rust on that spelling, and disagrees with Java. It does not take Perl's Is prefix — and that is the single most likely spelling to arrive in an ABAP program, because it arrives by being copied out of Perl, where it works. \p{IsGreek} compiles in Perl, compiles in Java, and is a runtime error in ABAP.
z_unicode_property.prog.abap in full — pasted here by tools/run_examples.py from the file CI runs.
REPORT z_unicode_property.
" Unicode properties in ABAP, through the PCRE addition available as of 7.55.
" Nothing in this repository can run ABAP, so this file is checked by abaplint
" and printed here as source only -- there is no recorded output beside it.
"
" ABAP does not have a regex engine of its own: the PCRE addition hands the
" pattern to the PCRE2 library in the kernel. So the answers are PCRE2's, and
" a `pcre2grep` on any machine predicts them. The page beside this file records
" one such run.
DATA(amount) = `1234`.
DATA(name) = `Andreas`.
" A numeric field. \p{Nd} is every DECIMAL DIGIT in every script, so this
" accepts U+0660 ARABIC-INDIC DIGIT ZERO as readily as U+0037 DIGIT SEVEN --
" which is very probably not what a posting amount should accept. [0-9] is
" the honest pattern for a field that a numeric conversion has to survive.
IF matches( val = amount pcre = `\A[0-9]+\z` ).
WRITE / `amount is ASCII digits only`.
ENDIF.
IF matches( val = amount pcre = `\A\p{Nd}+\z` ).
WRITE / `amount is decimal digits in some script -- not the same test`.
ENDIF.
" A name field. \p{L} is every letter in every script; [A-Za-z] is a rule that
" rejects half of the customer master.
IF matches( val = name pcre = `\A[\p{L}\p{Zs}'-]+\z` ).
WRITE / `name is letters, spaces, apostrophe and hyphen`.
ENDIF.
" Scripts. PCRE2 takes the bare name and the Script= form. It does NOT take
" Perl's Is prefix: `\p{IsGreek}` is "unknown property after \P or \p" in
" PCRE2 10.48, which is the single spelling most likely to arrive in a pattern
" copied out of a Perl program.
FIND ALL OCCURRENCES OF PCRE `\p{Greek}+` IN name RESULTS DATA(greek).
FIND ALL OCCURRENCES OF PCRE `\p{Script=Han}+` IN name RESULTS DATA(han).
" \X is one grapheme cluster: what a user calls one character, however many
" code points it takes. A truncation to a fixed length that counts with . or
" with strlen can cut a combining mark away from its base letter; counting
" with \X cannot.
FIND ALL OCCURRENCES OF PCRE `\X` IN name RESULTS DATA(graphemes).
WRITE / |{ lines( greek ) } greek runs, { lines( han ) } han runs, { lines( graphemes ) } graphemes|.
" CL_ABAP_REGEX=>CREATE_PCRE( ) and CL_ABAP_MATCHER are the object-oriented
" way in, and they take the same property syntax, because it is the same
" library underneath.
What it buys you¶
A numeric field. \p{Nd} matched U+0037 DIGIT SEVEN and U+0660 ARABIC-INDIC DIGIT ZERO. That is the correct reading of "a decimal digit", and it is very often not the requirement: [0-9] says "a digit the parser behind this field will certainly accept". They are different questions and the pattern should say which one you meant.
A name field. \p{L} took all six letters in the cast; [A-Za-z] would have taken U+0041 alone — not even U+00E9 LATIN SMALL LETTER E WITH ACUTE, which is Latin. A validation rule of [A-Za-z' -]+ on a customer name does not reject foreign alphabets, it rejects accents, and the rows it rejects are the ones a support ticket gets opened about.
\p{Lu} is not "uppercase". It matched U+0041, U+0394 and U+0416 — and not U+4E2D, whose category is Lo because Han has no case at all. A rule that says "the first letter must be \p{Lu}" does not mean "capitalised" in a caseless script; it means "rejected". If what you want is "starts with a letter", \p{L} is the test. The same trap runs the other way for \p{Ll}.
\X, briefly¶
\X matches one grapheme cluster — one character as a person counts them, however many code points that takes. U+0065 LATIN SMALL LETTER E followed by U+0301 COMBINING ACUTE ACCENT is two code points and one thing on the page: . matched 2 and \X matched 1, in Perl, in Java and in PCRE2. JavaScript, Go, Rust's regex and Python's re have no \X; Ruby does.
That is as far as this page goes, because "what counts as one character" is a question about text encoding rather than about patterns, and it has a library of its own: Encodings learning library ↗.
The measured summary¶
Every cell not marked otherwise comes from a block above.
| Engine | \p{L} |
Script spelled | Binary property | \p{Alpha} means |
\X |
Set ops in a class |
|---|---|---|---|---|---|---|
| Perl 5.38+ | yes | \p{Greek}, \p{Script=Greek}, \p{IsGreek} |
yes | Unicode | yes | (?[ ... ]), not measured here |
PCRE2 10.48 / ABAP PCRE |
yes | \p{Greek}, \p{Script=Greek} — not Is |
yes | Unicode | yes | no |
| Java 25 | yes | \p{IsGreek}, \p{sc=Greek} — not \p{Greek} |
Is prefix only |
US-ASCII | yes | no |
| Ruby 3.4+ | yes | \p{Greek} — not Script= |
yes | Unicode | yes | no |
JavaScript, u flag |
yes | \p{Script=Greek} — not \p{Greek} |
yes | Unicode | no | v flag, not measured here |
Go regexp |
yes | \p{Greek} — not Script= |
none | refused | no | no |
Rust regex |
yes | every spelling on this page | yes | Unicode | no | yes — &&, --, ~~ |
Python re |
no — bad escape \p |
— | — | — | no | — |
JavaScript, no u flag |
silently the literal p{L} |
— | — | — | — | — |
Sort those nine rows by how the failure arrives and the ordering is the useful one. Python's is a compile error you cannot miss. Go's and Java's are compile errors whose wording does not say "property". Ruby's \pL is a warning on a pattern that then matches nothing. And the last row does not fail at all: it compiles, it runs, and it matches the wrong thing for as long as nobody types a non-ASCII letter into the field.
See also¶
- What
\wand\bmatch — the same Unicode question asked of the shorthands you already use, where the default is ASCII more often than you would guess. - Unicode — the chapter these two lessons belong to.
- Who supports what — the same measured-by-compiling method over eleven other constructs, and where each engine's family is described.
- Forward references — the other page in this library about one pattern that compiles everywhere and means different things.
- Resources — where each engine documents itself.
- perluniprops ↗ — the full list of property names Perl accepts, including every
Isalias. - PCRE2 pattern syntax ↗ — the properties ABAP inherits, and the ones it does not.
- java.util.regex.Pattern ↗ — the table where the POSIX classes and the Unicode properties sit next to each other under the same syntax.
- Unicode support in the
regexcrate ↗ — the spellings, the set operations, and what theunicodefeature flags cost in binary size. - Go
regexp/syntax↗ — the short list of what RE2 supports, which is where the missing binary properties are documented by absence. - Unicode Technical Standard #18 ↗ — the standard that defines what a conformant engine should call these things.