///|
/// W1-W7: Weak Type Resolution
///
/// These rules resolve weak types (EN, ES, ET, AN, CS, NSM, BN)
/// within an isolating run sequence.
///|
/// Resolve weak types within an isolating run sequence
fn resolve_weak_types(
types : Array[BidiClass],
_levels : Array[Int],
seq : IsolatingRunSequence,
) -> Unit {
let indices = seq.indices
if indices.length() == 0 {
return
}
// W1: NSM - Non-Spacing Mark
// Examine each character that is not an isolate initiator or PDI.
// If the previous character is an isolate initiator or PDI, change NSM to ON.
// Otherwise, change NSM to the type of the previous character (or sos).
let mut prev_type = seq.sos
for idx in indices {
let bc = types[idx]
if bc is NSM {
// Check if previous character was isolate initiator or PDI
// In resolved types, isolate initiators keep their type
if prev_type.is_isolate_initiator() || prev_type is PDI {
types[idx] = BidiClass::ON
} else {
types[idx] = prev_type
}
}
// Update prev_type (don't follow BN)
if !(types[idx] is BN) {
prev_type = types[idx]
}
}
// W2: EN - European Number after AL
// Track the most recent strong type instead of rescanning for every EN.
let mut last_strong = seq.sos
for idx in indices {
match types[idx] {
L => last_strong = BidiClass::L
R => last_strong = BidiClass::R
AL => last_strong = BidiClass::AL
EN => if last_strong is AL { types[idx] = BidiClass::AN }
_ => ()
}
}
// W3: AL - Arabic Letter becomes R
for idx in indices {
if types[idx] is AL {
types[idx] = BidiClass::R
}
}
// W4: ES/CS between EN/AN
// A single ES between two ENs becomes EN.
// A single CS between two ENs becomes EN.
// A single CS between two ANs becomes AN.
for i = 1; i < indices.length() - 1; i = i + 1 {
let idx = indices[i]
let bc = types[idx]
if bc is ES || bc is CS {
let prev_idx = indices[i - 1]
let next_idx = indices[i + 1]
let prev_bc = types[prev_idx]
let next_bc = types[next_idx]
if bc is ES && prev_bc is EN && next_bc is EN {
types[idx] = BidiClass::EN
} else if bc is CS {
if prev_bc is EN && next_bc is EN {
types[idx] = BidiClass::EN
} else if prev_bc is AN && next_bc is AN {
types[idx] = BidiClass::AN
}
}
}
}
// W5: ET adjacent to EN becomes EN
// A sequence of ETs adjacent to EN becomes EN.
// First, find all ENs and expand to adjacent ETs
for i = 0; i < indices.length(); i = i + 1 {
let idx = indices[i]
if types[idx] is EN {
// Look backward for ETs
let mut j = i - 1
while j >= 0 && types[indices[j]] is ET {
types[indices[j]] = BidiClass::EN
j -= 1
}
// Look forward for ETs
j = i + 1
while j < indices.length() && types[indices[j]] is ET {
types[indices[j]] = BidiClass::EN
j += 1
}
}
}
// W6: ES, ET, CS become ON
// All remaining ES, ET, and CS characters become ON.
for idx in indices {
let bc = types[idx]
if bc is ES || bc is ET || bc is CS {
types[idx] = BidiClass::ON
}
}
// W7: EN becomes L when sos is L or after L
// W3 has already changed AL to R, so only L and R need tracking here.
last_strong = seq.sos
for idx in indices {
match types[idx] {
L => last_strong = BidiClass::L
R => last_strong = BidiClass::R
EN => if last_strong is L { types[idx] = BidiClass::L }
_ => ()
}
}
}
///|
/// Re-resolve NSM after bracket pairing so combining marks follow
/// brackets that were retyped by N0.
fn resolve_nsm_after_brackets(
types : Array[BidiClass],
original_classes : Array[BidiClass],
seq : IsolatingRunSequence,
) -> Unit {
let indices = seq.indices
if indices.length() == 0 {
return
}
let mut prev_type = seq.sos
for idx in indices {
if original_classes[idx] is NSM && types[idx] is ON {
if prev_type.is_isolate_initiator() || prev_type is PDI {
types[idx] = BidiClass::ON
} else {
types[idx] = prev_type
}
}
if !(types[idx] is BN) {
prev_type = types[idx]
}
}
}