EMOJI MODULE
============
Standard: UTS #51 (Unicode Emoji)
Files: src/lingenic_text-emoji_spec.ads (ghost specification)
src/lingenic_text-emoji.ads (public API)
src/lingenic_text-emoji.adb (implementation)
PURPOSE
-------
The Emoji module provides boolean emoji property lookups and classifies
UTF-8 byte sequences into emoji sequence types per the UTS #51 grammar.
GHOST SPECIFICATION (Emoji_Spec)
---------------------------------
Sequence Types:
type Emoji_Sequence_Type is
(Not_Emoji,
Emoji_Character,
Emoji_Presentation_Seq,
Emoji_Keycap_Seq,
Emoji_Modifier_Seq,
Emoji_Flag_Seq,
Emoji_Tag_Seq,
Emoji_ZWJ_Seq);
Structural Codepoint Constants:
VS16 = 16#FE0F# Variation Selector 16 (emoji presentation)
Keycap = 16#20E3# Combining Enclosing Keycap
ZWJ = 16#200D# Zero Width Joiner
Cancel_Tag = 16#E007F# Cancel Tag
RI_First = 16#1F1E6# First Regional Indicator
RI_Last = 16#1F1FF# Last Regional Indicator
Tag_First = 16#E0020# First Tag character
Tag_Last = 16#E007E# Last Tag character
Structural Predicates:
Is_Keycap_Base(CP) CP is a valid keycap base (0-9, #, *)
Is_Regional_Indicator(CP) CP in RI_First..RI_Last
Is_Tag_Spec(CP) CP in Tag_First..Tag_Last
Max_Sequence_Bytes = 256 Maximum input size for classification
Classification Logic:
Classify_From_Flags
(Is_Emoji_CP, Is_Keycap_B, Is_Mod_Base, Is_RI_First,
Has_ZWJ, RI_Count, Has_Keycap, Tag_Done, Has_Mod, Has_VS16)
Priority-ordered decision based on accumulated flags from the scan:
Has_ZWJ and Is_Emoji_CP -> Emoji_ZWJ_Seq
RI_Count = 2 and Is_RI_First -> Emoji_Flag_Seq
Has_Keycap and Is_Keycap_B -> Emoji_Keycap_Seq
Tag_Done and Is_Emoji_CP -> Emoji_Tag_Seq
Has_Mod and Is_Mod_Base -> Emoji_Modifier_Seq
Has_VS16 and Is_Emoji_CP -> Emoji_Presentation_Seq
Is_Emoji_CP -> Emoji_Character
otherwise -> Not_Emoji
INITIALIZATION
--------------
procedure Initialize
(UCD_Dir : String;
Success : out Boolean);
Post: if Success then Initialized
Reads from UCD_Dir:
emoji-data.txt 5 boolean emoji properties
Loads flat Boolean arrays indexed by codepoint:
Emoji Has the Emoji property
Emoji_Presentation Has the Emoji_Presentation property
Emoji_Modifier Has the Emoji_Modifier property
Emoji_Modifier_Base Has the Emoji_Modifier_Base property
Emoji_Component Has the Emoji_Component property
Does NOT depend on Properties.Initialized. The emoji module is
self-contained. Extended_Pictographic remains in the Properties module
(needed by grapheme segmentation at Properties init time).
PUBLIC API
----------
Boolean Property Lookups:
Is_Emoji(CP) -> Boolean
Is_Emoji_Presentation(CP) -> Boolean
Is_Emoji_Modifier(CP) -> Boolean
Is_Emoji_Modifier_Base(CP) -> Boolean
Is_Emoji_Component(CP) -> Boolean
All are O(1) flat array lookups. Pre: Initialized.
Classify_Sequence:
function Classify_Sequence
(Input : Byte_Array) return Emoji_Sequence_Type;
Pre: Initialized
Input'Length >= 1
Input'Length <= Max_Sequence_Bytes
Input'Last < Positive'Last
Post: Result = Ghost_Classify_All(Input)
Classifies a UTF-8 byte sequence (typically one grapheme cluster as
returned by Next_Grapheme_Cluster_Break) into one of 8 emoji types.
The classifier implements the UTS #51 EBNF grammar:
possible_emoji := zwj_element (\x{200D} zwj_element)*
flag_sequence := \p{RI} \p{RI}
zwj_element := \p{Emoji} emoji_modification?
| flag_sequence
emoji_modification := \p{EMod}
| \x{FE0F} \x{20E3}?
| tag_modifier
tag_modifier := [\x{E0020}-\x{E007E}]+ \x{E007F}
The implementation is a single forward scan that accumulates flags
in an Emoji_Scan_State record, then applies Classify_From_Flags.
Invalid UTF-8 or non-emoji input returns Not_Emoji.
GHOST SPECIFICATION STRUCTURE
-----------------------------
Scan State Record (Emoji_Scan_State):
First_CP Codepoint of the first character in the sequence
Has_ZWJ True if ZWJ was seen (and not in an RI pair)
Has_VS16 True if VS16 was seen
Has_Keycap True if Keycap (U+20E3) was seen
Has_Mod True if an Emoji_Modifier was seen
Tag_Done True if a tag sequence was completed (Cancel_Tag after tags)
In_Tag True if currently in a tag sequence
RI_Count Count of Regional_Indicator codepoints
CP_Count Total codepoints processed
Ghost Functions:
Ghost_Update_Scan(St, CP, Max_CP) -> Emoji_Scan_State
Updates the scan state for one codepoint. Mirrors the body's
flag-setting logic exactly.
Ghost_Scan_Result(St) -> Emoji_Sequence_Type
Final classification from accumulated state.
Calls Classify_From_Flags with booleans derived from First_CP.
Ghost_Scan(Input, Cur, St) -> Emoji_Sequence_Type
Recursive ghost function simulating the forward scan.
Terminates because Ghost_Step_Length >= 1 (Cur strictly increases).
Ghost_Classify_All(Input) -> Emoji_Sequence_Type
= Ghost_Scan(Input, Input'First, Ghost_Initial_State)
Top-level entry point referenced by Classify_Sequence's postcondition.
USAGE PATTERN
-------------
Typical usage: classify grapheme clusters returned by the segmentation
module:
Pos := Text'First;
while Pos <= Text'Last loop
Graphemes.Next_Grapheme_Cluster_Break (Text, Pos, Next_Pos);
-- Classify the cluster
Cluster := Text (Pos .. Next_Pos - 1);
if Cluster'Length <= Emoji_Spec.Max_Sequence_Bytes then
Seq_Type := Emoji.Classify_Sequence (Cluster);
case Seq_Type is
when Emoji_ZWJ_Seq =>
-- ZWJ emoji sequence (e.g., family emoji)
when Emoji_Flag_Seq =>
-- Flag emoji (pair of regional indicators)
when others =>
null;
end case;
end if;
Pos := Next_Pos;
end loop;