// Copyright (c) 2026 Christian Hansen
//
// Copyright (c) 2026 fundon
//
// Unicode trie data and DFA transition table (UTS #51 v17.0.0).
// DO NOT EDIT — Generated by ucdgen.
//
// Licensed under the MIT License.
///|
fn trie_lookup(
cp : Int,
idx0 : ReadOnlyArray[Int],
idx1 : ReadOnlyArray[Int],
data : ReadOnlyArray[Int],
block_shift : Int,
page_shift : Int,
block_mask : Int,
page_mask : Int,
) -> Bool {
let page = (cp >> (block_shift + page_shift)) & 255
let off1 = (cp >> block_shift) & page_mask
let off2 = cp & block_mask
let i0 = idx0[page]
let i1 = idx1[(i0 << page_shift) + off1]
let data_idx = (i1 << block_shift) + off2
((data[data_idx >> 3] >> (data_idx & 7)) & 1) == 1
}
// ── Modifier Base trie (520 bytes) ─────────────────────────────────────
///|
let modifier_base_idx0 : ReadOnlyArray[Int] = [
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
///|
let modifier_base_idx1 : ReadOnlyArray[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 3, 4, 5, 6, 0, 0, 7, 8, 0, 0, 0, 0, 0,
]
///|
let modifier_base_data : ReadOnlyArray[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 156, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 204, 255, 1, 0, 192, 255, 255, 17, 238, 128, 2, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 4, 0, 0, 97,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 248, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 112, 0, 1, 16, 0, 0, 0, 0, 0, 0, 0, 144, 0, 255,
64, 0, 255, 115, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 96, 11, 0, 224, 254,
63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 56, 0, 0, 0, 0, 0, 255, 1,
]
///|
pub fn is_modifier_base(cp : Int) -> Bool {
trie_lookup(
cp, modifier_base_idx0, modifier_base_idx1, modifier_base_data, 8, 5, 255, 31,
)
}
// ── Emoji trie (952 bytes) ──────────────────────────────────────────────
///|
let emoji_idx0 : ReadOnlyArray[Int] = [
0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2,
]
///|
let emoji_idx1 : ReadOnlyArray[Int] = [
0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 4, 5, 2, 2, 6, 7, 2, 8, 2, 9, 10, 11, 12, 13,
2, 2, 14, 2, 2, 2, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 2, 2, 2, 2, 17, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 2, 23, 24, 25, 26, 27, 28,
29, 30, 2, 31, 2, 2, 32, 25, 33, 34, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
]
///|
let emoji_data : ReadOnlyArray[Int] = [
0, 0, 0, 0, 8, 4, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 240, 3, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 254, 15, 7, 0, 0,
0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0,
0, 0, 0, 120, 31, 64, 50, 33, 77, 196, 0, 7, 5, 255, 15, 128, 105, 1, 0, 200, 0,
0, 252, 26, 131, 12, 3, 96, 48, 193, 26, 0, 0, 6, 191, 39, 36, 191, 84, 32, 2,
1, 24, 0, 144, 80, 184, 0, 24, 0, 0, 0, 0, 0, 224, 0, 2, 0, 1, 128, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 24, 0,
0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 128, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 64, 254, 7, 0, 0, 0, 0, 0, 0, 0, 0,
192, 255, 255, 255, 6, 0, 0, 4, 0, 128, 252, 7, 0, 0, 3, 0, 0, 0, 0, 0, 255, 255,
255, 255, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
207, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 185, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 255, 255,
255, 255, 255, 255, 255, 63, 0, 126, 255, 255, 255, 128, 249, 7, 128, 60, 97, 0,
48, 1, 6, 16, 28, 0, 14, 112, 10, 129, 8, 252, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 63, 248,
231, 241, 63, 26, 249, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, 1, 0, 0,
240, 255, 255, 255, 255, 255, 247, 191, 255, 255, 255, 255, 255, 255, 255, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 31, 255, 199, 255, 255, 255, 255, 255,
255, 127, 225, 255, 159, 255, 135, 255, 1,
]
///|
pub fn is_emoji(cp : Int) -> Bool {
trie_lookup(cp, emoji_idx0, emoji_idx1, emoji_data, 7, 6, 127, 63)
}
// ── Emoji_Presentation trie (744 bytes) ─────────────────────────────────
///|
let presentation_idx0 : ReadOnlyArray[Int] = [
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
///|
let presentation_idx1 : ReadOnlyArray[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 5, 6, 7, 0,
0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10,
0, 11, 12, 0, 13, 14, 15, 16, 17, 18, 19, 20, 0, 21, 0, 0, 22, 23, 24, 25, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]
///|
let presentation_data : ReadOnlyArray[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 9, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 48, 0, 0, 0, 0, 0, 0, 255, 15, 0, 0,
0, 0, 128, 0, 0, 8, 0, 2, 12, 0, 96, 48, 64, 16, 0, 0, 4, 44, 36, 32, 12, 0, 0,
0, 1, 0, 0, 0, 80, 184, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 1, 128, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
0, 64, 254, 7, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 2, 0, 0, 4, 0, 128,
124, 7, 0, 0, 3, 0, 0, 0, 0, 0, 255, 255, 255, 255, 1, 224, 191, 255, 255, 255,
255, 255, 255, 255, 255, 223, 255, 255, 15, 0, 255, 255, 255, 255, 255, 135, 15,
0, 255, 255, 17, 255, 255, 255, 255, 255, 255, 255, 255, 127, 253, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 159, 255, 255, 255, 255, 255, 255, 255, 63, 0, 120, 255, 255, 255,
0, 0, 4, 0, 0, 96, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
255, 63, 16, 231, 241, 0, 24, 240, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
15, 1, 0, 0, 240, 255, 255, 255, 255, 255, 247, 191, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 31, 255, 199, 255, 255, 255,
255, 255, 255, 127, 225, 255, 159, 255, 135, 255, 1,
]
///|
pub fn is_presentation(cp : Int) -> Bool {
trie_lookup(
cp, presentation_idx0, presentation_idx1, presentation_data, 7, 6, 127, 63,
)
}
// ── DFA states and classes ──────────────────────────────────────────────
///|
// ── DFA states and classes ──────────────────────────────────────────────
pub(all) enum State {
REJECT
START
TERMINAL
EMOJI
MODIFIER_BASE
OPTIONAL_ZWJ
KEYCAP_VS
TAG_BASE
RI
TAG_SPEC
TAG_EMPTY
KEYCAP_BASE
ZWJ
} derive(Eq, Debug, Compare)
///|
pub(all) enum Class {
OTHER
EMOJI
MODIFIER_BASE
MODIFIER
VS15
VS16
RI
KEYCAP_BASE
KEYCAP_TERM
TAG_BASE
TAG_SPEC
TAG_TERM
ZWJ
} derive(Eq, Debug)
///|
/// Convert State enum to its integer discriminant.
pub fn state_to_int(s : State) -> Int {
match s {
State::REJECT => 0
State::START => 1
State::TERMINAL => 2
State::EMOJI => 3
State::MODIFIER_BASE => 4
State::OPTIONAL_ZWJ => 5
State::KEYCAP_VS => 6
State::TAG_BASE => 7
State::RI => 8
State::TAG_SPEC => 9
State::TAG_EMPTY => 10
State::KEYCAP_BASE => 11
State::ZWJ => 12
}
}
///|
/// Convert Class enum to its integer discriminant.
pub fn class_to_int(c : Class) -> Int {
match c {
Class::OTHER => 0
Class::EMOJI => 1
Class::MODIFIER_BASE => 2
Class::MODIFIER => 3
Class::VS15 => 4
Class::VS16 => 5
Class::RI => 6
Class::KEYCAP_BASE => 7
Class::KEYCAP_TERM => 8
Class::TAG_BASE => 9
Class::TAG_SPEC => 10
Class::TAG_TERM => 11
Class::ZWJ => 12
}
}
///|
/// Convert an integer back to State enum.
pub fn int_to_state(n : Int) -> State {
match n {
0 => State::REJECT
1 => State::START
2 => State::TERMINAL
3 => State::EMOJI
4 => State::MODIFIER_BASE
5 => State::OPTIONAL_ZWJ
6 => State::KEYCAP_VS
7 => State::TAG_BASE
8 => State::RI
9 => State::TAG_SPEC
10 => State::TAG_EMPTY
11 => State::KEYCAP_BASE
12 => State::ZWJ
_ => State::REJECT
}
}
///|
pub const RECORD_STATE_SHIFT : Int = 16
// ── DFA transition table ────────────────────────────────────────────────
///|
let dfa_table : ReadOnlyArray[ReadOnlyArray[Int]] = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 3, 4, 2, 1, 1, 8, 11, 0, 7, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 12],
[0, 0, 0, 5, 2, 5, 0, 0, 0, 0, 0, 0, 12],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12],
[0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 9, 10, 12],
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 6, 6, 0, 0, 2, 0, 0, 0, 0],
[0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]