Sign in

    emoji

    Unicode Emoji Segmentation — MoonBit implementation of UTS #51 v17.0.0

    emoji
    unicode
    uts51
    segmentation
    Download zip
    Author
    Version
    0.3.0
    License
    MIT
    Last updated
    5 months ago
    Downloads
    29

    #moonwell-emoji — Unicode Emoji Segmentation

    A MoonBit library for Unicode emoji segmentation implementing UTS #51 v17.0.0.

    MoonBit port of c-emoji.

    The core is a DFA that classifies and segments emoji sequences in a single O(n) pass with no heap allocation beyond result storage. Two scanning modes are provided — strict and greedy — that differ in how they handle incomplete sequences at boundaries.

    #Usage

    let text : Array[Int] = [0x1F468, 0x1F3FB, 0x200D, 0x1F4BB] // 👨🏻‍💻
    let seqs = @emoji.scan_strict(text)

    for i = 0; i < seqs.length(); i = i + 1 {
    println("emoji at [\{seqs[i].start}, \{seqs[i].end}]")
    }
    // emoji at [0, 3]

    start and end are inclusive indices into the codepoint array.

    #Strict vs greedy

    Inputstrictgreedy
    👨 ZWJ (trailing ZWJ)∅{0,0}
    👨 ZWJ ZWJ 👩 (double ZWJ){3,3}{0,0} {3,3}
    👨 ZWJ A (invalid ZWJ target)∅{0,0}
    🏴 gb eng (tag without cancel)∅{0,0}

    #API

    #Scanning

    • scan_strict(codepoints : Array[Int]) -> Array[ScanRange] — conservative scan
    • scan_greedy(codepoints : Array[Int]) -> Array[ScanRange] — permissive scan

    #Sequence classification

    • classify_type(bitmask : Int) -> EmojiSequenceType — from recorded bitmask
    • classify_style(bitmask : Int) -> EmojiPresentationStyle — from recorded bitmask

    #Presentation resolution

    • resolve(type, style, leading_cp) -> EmojiPresentationStyle — full resolution
    • resolve_default(type, leading_cp) -> EmojiPresentationStyle — when no VS present

    #UCD predicates

    • is_emoji(cp : Int) -> Bool
    • is_presentation(cp : Int) -> Bool
    • is_modifier_base(cp : Int) -> Bool
    • is_modifier(cp : Int) -> Bool
    • is_keycap_base(cp : Int) -> Bool
    • is_regional_indicator(cp : Int) -> Bool

    #Static memory

    ComponentSize
    Emoji trie952 bytes
    Modifier base trie520 bytes
    Presentation trie744 bytes
    DFA transition table169 bytes
    Total static data2,385 bytes

    #Unicode version

    Unicode 17.0.0.

    #License

    MIT License.

    Copyright (c) 2026 Christian Hansen chansen@cpan.org.

    Copyright (c) 2026 fundon fundon@pindash.io.

    Class

    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
    )

    EmojiPresentationStyle

    pub(all) enum EmojiPresentationStyle {
    DEFAULT
    TEXT
    EMOJI
    } derive(Eq,
    Debug
    )

    EmojiSequenceType

    pub(all) enum EmojiSequenceType {
    BASIC
    KEYCAP
    FLAG
    TAG
    MODIFIER
    ZWJ
    } derive(Eq,
    Debug
    )

    ScanRange

    pub struct ScanRange {
    start : Int
    end : Int
    }

    State

    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(Compare, Eq,
    Debug
    )

    RECORD_STATE_SHIFT

    let RECORD_STATE_SHIFT : Int

    class_to_int

    fn class_to_int(c : Class) -> Int

    Convert Class enum to its integer discriminant.

    classify_style

    fn classify_style(bitmask : Int) -> EmojiPresentationStyle

    classify_type

    fn classify_type(bitmask : Int) -> EmojiSequenceType

    dfa_is_accepting

    fn dfa_is_accepting(state : State) -> Bool

    dfa_is_boundary

    fn dfa_is_boundary(state : State) -> Bool

    dfa_is_start

    fn dfa_is_start(state : State) -> Bool

    dfa_step

    fn dfa_step(state : State, klass : Class) -> State

    dfa_step_record

    fn dfa_step_record(state : State, klass : Class, bitmask : Int) -> (State, Int)

    Step the DFA and record class/state bits into the bitmask accumulator.

    int_to_state

    fn int_to_state(n : Int) -> State

    Convert an integer back to State enum.

    is_emoji

    fn is_emoji(cp : Int) -> Bool

    is_emoji_presentation_selector

    fn is_emoji_presentation_selector(cp : Int) -> Bool

    is_enclosing_keycap

    fn is_enclosing_keycap(cp : Int) -> Bool

    is_keycap_base

    fn is_keycap_base(cp : Int) -> Bool

    is_modifier

    fn is_modifier(cp : Int) -> Bool

    is_modifier_base

    fn is_modifier_base(cp : Int) -> Bool

    is_presentation

    fn is_presentation(cp : Int) -> Bool

    is_regional_indicator

    fn is_regional_indicator(cp : Int) -> Bool

    is_tag_base

    fn is_tag_base(cp : Int) -> Bool

    is_tag_spec

    fn is_tag_spec(cp : Int) -> Bool

    is_tag_term

    fn is_tag_term(cp : Int) -> Bool

    is_text_presentation_selector

    fn is_text_presentation_selector(cp : Int) -> Bool

    resolve

    fn resolve(type_ : EmojiSequenceType, style : EmojiPresentationStyle, leading_cp : Int) -> EmojiPresentationStyle

    resolve_default

    fn resolve_default(type_ : EmojiSequenceType, leading_cp : Int) -> EmojiPresentationStyle

    scan_greedy

    fn scan_greedy(codepoints : Array[Int]) -> Array[ScanRange]

    scan_strict

    fn scan_strict(codepoints : Array[Int]) -> Array[ScanRange]

    state_to_int

    fn state_to_int(s : State) -> Int

    Convert State enum to its integer discriminant.

    ucd_classify

    fn ucd_classify(cp : Int) -> Class