///|
/// Small, dependency-free text helpers. The project deliberately avoids a
/// regex/Unicode dependency: the input domain is Han characters plus
/// punctuation, and keeping the parsing rules explicit makes the checker
/// auditable.
fn split_on(s : String, sep : Char) -> Array[String] {
let out : Array[String] = []
let sb = StringBuilder()
for c in s {
if c == sep {
out.push(sb.to_string())
let _ = sb.reset()
} else {
sb.write_char(c)
}
}
out.push(sb.to_string())
out
}
///|
fn has_prefix(s : String, prefix : String) -> Bool {
let a = s.to_array()
let b = prefix.to_array()
if a.length() < b.length() {
return false
}
let mut i = 0
while i < b.length() {
if a[i] != b[i] {
return false
}
i = i + 1
}
true
}
///|
pub fn parse_int_opt(s : String) -> Int32? {
if s.length() == 0 {
return None
}
let mut value = 0
for c in s {
let d = c.to_int() - '0'.to_int()
if d < 0 || d > 9 {
return None
}
value = value * 10 + d
}
Some(value)
}
///|
/// True for the CJK Unified Ideographs blocks we accept as poem characters.
/// Punctuation, Latin letters and digits act as line separators instead.
pub fn is_han(c : Char) -> Bool {
let n = c.to_int()
(n >= 0x4E00 && n <= 0x9FFF) ||
(n >= 0x3400 && n <= 0x4DBF) ||
(n >= 0xF900 && n <= 0xFAFF)
}
///|
/// Split a pasted poem into lines of Han characters.
///
/// Every non-Han character (,。、;:!?, newline, spaces, annotations) ends
/// the current line. This mirrors how people actually paste poems and makes
/// the checker independent of a particular punctuation style.
pub fn poem_lines(text : String) -> Array[String] {
let out : Array[String] = []
let sb = StringBuilder()
let mut pending = 0
for c in text {
if is_han(c) {
sb.write_char(c)
pending = pending + 1
} else if pending > 0 {
out.push(sb.to_string())
let _ = sb.reset()
pending = 0
}
}
let tail = sb.to_string()
if tail.length() > 0 {
out.push(tail)
}
out
}
///|
pub fn chars_of(s : String) -> Array[Char] {
s.to_array()
}
///|
pub fn string_of_chars(cs : Array[Char]) -> String {
let sb = StringBuilder()
for c in cs {
sb.write_char(c)
}
sb.to_string()
}
///|
pub fn repeat_char(c : Char, n : Int32) -> String {
let sb = StringBuilder()
let mut i = 0
while i < n {
sb.write_char(c)
i = i + 1
}
sb.to_string()
}