import "./core.zc" import "./vec.zc" import "./option.zc" fn _z_internal_str_case_cmp(s1: char*, s2: char*) -> int { let i = 0; while (true) { let c1 = (int)((byte)s1[i]); let c2 = (int)((byte)s2[i]); // Convert to lowercase for comparison let lc1 = if (c1 >= (int)'A' && c1 <= (int)'Z') { c1 + 32 } else { c1 }; let lc2 = if (c2 >= (int)'A' && c2 <= (int)'Z') { c2 + 32 } else { c2 }; if (lc1 != lc2) { return lc1 - lc2; } if (c1 == 0) { break; } i = i + 1; } return 0; } struct String { vec: Vec; } struct StringCharsIter { data: char*; len: usize; pos: usize; } impl StringCharsIter { fn next(self) -> Option { if (self.pos >= self.len) { return Option::None(); } let c = (byte)self.data[self.pos]; let seq = String::_utf8_seq_len((char)c); if (self.pos + seq > self.len) { self.pos = self.len; return Option::None(); } let val: rune = 0; if (seq == 1) { val = (rune)c; } else if (seq == 2) { val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.data[self.pos+1] & 0x3F)); } else if (seq == 3) { val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.data[self.pos+1] & 0x3F) << 6) | ((int)(byte)self.data[self.pos+2] & 0x3F)); } else if (seq == 4) { val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.data[self.pos+1] & 0x3F) << 12) | (((int)(byte)self.data[self.pos+2] & 0x3F) << 6) | ((int)(byte)self.data[self.pos+3] & 0x3F)); } self.pos = self.pos + seq; return Option::Some(val); } /// Returns an iterator over the code points. fn iterator(self) -> StringCharsIter { return self; } } impl String { /// Creates a new String from a C string. fn new(s: char*) -> String { let len = strlen(s); let v = Vec::with_capacity(len + 1); if (len > 0) { memcpy(v.data, s, len * sizeof(char)); } v.len = len + 1; v.data[len] = 0; let d = v.data; let l = v.len; let c = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c } }; } /// Creates a new String from a C string (alias for `new`). fn from(s: char*) -> String { return String::new(s); } /// Creates a String from a single rune. fn from_rune(r: rune) -> String { let s = String::new(""); s.push_rune(r); return s; } /// Creates a String from an array of runes. fn from_runes(runes: rune*, count: usize) -> String { let s = String::new(""); for (let i: usize = 0; i < count; i = i + 1) { s.push_rune(runes[i]); } return s; } /// Returns the underlying NUL-terminated C string. fn c_str(self) -> char* { return self.vec.data; } /// Returns a `char*` view of the string. fn to_string(self) -> char* { return self.c_str(); } /// Returns an independent copy with its own buffer. fn clone(self) -> String { return String { vec: self.vec.clone() }; } /// Frees the string memory (deprecated; use `free`). fn destroy(self) { self.free(); } /// Detaches the internal buffer without freeing it. fn forget(self) { self.vec.forget(); } /// Appends another String in place. fn append(self, other: String*) { if (self.vec.len > 0) { self.vec.len = self.vec.len - 1; } let other_len = (*other).vec.len; let new_len = self.vec.len + other_len; self.vec.grow_to_fit(new_len); memcpy(self.vec.data + self.vec.len, (*other).vec.data, other_len * sizeof(char)); self.vec.len = new_len; } /// Appends a C string in place. fn append_c(self, s: char*) { if (self.vec.len > 0) { self.vec.len = self.vec.len - 1; } let slen = strlen(s); let new_len = self.vec.len + slen + 1; self.vec.grow_to_fit(new_len); memcpy(self.vec.data + self.vec.len, s, slen * sizeof(char)); self.vec.len = new_len; self.vec.data[new_len - 1] = 0; } /// Appends a rune (UTF-8 encoded). fn push_rune(self, r: rune) { if (self.vec.len > 0) { self.vec.len = self.vec.len - 1; } let val = (uint32_t)r; if (val < 0x80) { self.vec.push((char)val); } else if (val < 0x800) { self.vec.push((char)(0xC0 | (val >> 6))); self.vec.push((char)(0x80 | (val & 0x3F))); } else if (val < 0x10000) { self.vec.push((char)(0xE0 | (val >> 12))); self.vec.push((char)(0x80 | ((val >> 6) & 0x3F))); self.vec.push((char)(0x80 | (val & 0x3F))); } else { self.vec.push((char)(0xF0 | (val >> 18))); self.vec.push((char)(0x80 | ((val >> 12) & 0x3F))); self.vec.push((char)(0x80 | ((val >> 6) & 0x3F))); self.vec.push((char)(0x80 | (val & 0x3F))); } self.vec.push(0); } fn append_c_ptr(ptr: String*, s: char*) { if (ptr.vec.len > 0) { ptr.vec.len = ptr.vec.len - 1; } let slen = strlen(s); let new_len = ptr.vec.len + slen + 1; ptr.vec.grow_to_fit(new_len); memcpy(ptr.vec.data + ptr.vec.len, s, slen * sizeof(char)); ptr.vec.len = new_len; ptr.vec.data[new_len - 1] = 0; } /// Returns a new String concatenating self and other. fn add(self, other: String*) -> String { let new_s = String::from(self.c_str()); (&new_s).append(other); let d = new_s.vec.data; let l = new_s.vec.len; let c = new_s.vec.cap; new_s.forget(); return String { vec: Vec { data: d, len: l, cap: c } }; } /// Appends other in place. fn add_assign(self, other: String*) { self.append(other); } /// Returns whether this String equals another. fn eq(self, other: String*) -> bool { let zero: c_int = 0; return strcmp(self.c_str(), (*other).c_str()) == zero; } /// Returns whether this String differs from another. fn neq(self, other: String*) -> bool { return !self.eq(other); } /// Lexicographic comparison: returns <0, 0, or >0. fn compare(self, other: String*) -> int { return strcmp(self.c_str(), (*other).c_str()); } /// Returns whether this String is less than another. fn lt(self, other: String*) -> bool { return self.compare(other) < 0; } /// Returns whether this String is greater than another. fn gt(self, other: String*) -> bool { return self.compare(other) > 0; } /// Returns whether this String is <= another. fn le(self, other: String*) -> bool { return self.compare(other) <= 0; } /// Returns whether this String is >= another. fn ge(self, other: String*) -> bool { return self.compare(other) >= 0; } /// Case-insensitive lexicographic comparison. fn compare_ignore_case(self, other: String*) -> int { return _z_internal_str_case_cmp(self.vec.data, (*other).vec.data); } /// Case-insensitive equality. fn eq_ignore_case(self, other: String*) -> bool { let zero: int = 0; return self.compare_ignore_case(other) == zero; } /// Returns whether this String equals a C string. fn eq_str(self, s: char*) -> bool { let zero: c_int = 0; return strcmp(self.c_str(), s) == zero; } /// Returns the length in bytes (not code points). fn length(self) -> usize { if (self.vec.len == 0) { return 0; } return self.vec.len - 1; } /// Returns a new String of `len` bytes starting at `start`. fn substring(self, start: usize, len: usize) -> String { if (start + len > self.length()) { panic("substring out of bounds"); } let v = Vec::with_capacity(len + 1); if (len > 0) { memcpy(v.data, self.vec.data + start, len * sizeof(char)); } v.len = len + 1; v.data[len] = 0; let d = v.data; let l = v.len; let c = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c } }; } /// Returns whether the substring `target` occurs. fn contains_str(self, target: char*) -> bool { return self.find_str(target).is_some(); } /// Returns the first index of `target`, or `None`. fn find_str(self, target: char*) -> Option { let t_len = strlen(target); if t_len == 0 { return Option::Some(0); } let s_len = self.length(); if t_len > s_len { return Option::None(); } for (let i: usize = 0; i <= s_len - t_len; i = i + 1) { let is_match = true; for (let k: usize = 0; k < t_len; k = k + 1) { if self.vec.get(i + k) != target[k] { is_match = false; break; } } if is_match { return Option::Some(i); } } return Option::None(); } /// Returns all indices where `target` occurs. fn find_all_str(self, target: char*) -> Vec { let indices = Vec::new(); let t_len = strlen(target); if t_len == 0 { return indices; } let s_len = self.length(); if t_len > s_len { return indices; } for (let i: usize = 0; i <= s_len - t_len; i = i + 1) { let is_match = true; for (let k: usize = 0; k < t_len; k = k + 1) { if self.vec.get(i + k) != target[k] { is_match = false; break; } } if is_match { indices.push(i); // Optional: i = i + t_len - 1; (Un-comment to avoid overlapping matches) } } return indices; } /// Returns a new lowercase copy. fn to_lowercase(self) -> String { let len = self.length(); let v = Vec::with_capacity(len + 1); for (let i: usize = 0; i < len; i = i + 1) { let c = self.vec.get(i); if (c >= 'A' && c <= 'Z') { v.push((char)((int)c + 32)); } else { v.push(c); } } v.push(0); let d = v.data; let l = v.len; let c_cap = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c_cap } }; } /// Returns a new string padded on the right to `target_len`. fn pad_right(self, target_len: usize, pad_char: char) -> String { let current_len = self.length(); if (current_len >= target_len) { return String::from(self.c_str()); } let v = Vec::with_capacity(target_len + 1); if (current_len > 0) { memcpy(v.data, self.vec.data, current_len * sizeof(char)); } v.len = current_len; for (let i: usize = current_len; i < target_len; i = i + 1) { v.push(pad_char); } v.push(0); let d = v.data; let l = v.len; let c_cap = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c_cap } }; } /// Returns a new string padded on the left to `target_len`. fn pad_left(self, target_len: usize, pad_char: char) -> String { let current_len = self.length(); if (current_len >= target_len) { return String::from(self.c_str()); } let v = Vec::with_capacity(target_len + 1); let diff = target_len - current_len; for (let i: usize = 0; i < diff; i = i + 1) { v.push(pad_char); } if (current_len > 0) { memcpy(v.data + diff, self.vec.data, current_len * sizeof(char)); } v.len = v.len + current_len; v.push(0); let d = v.data; let l = v.len; let c_cap = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c_cap } }; } /// Returns a new uppercase copy. fn to_uppercase(self) -> String { let len = self.length(); let v = Vec::with_capacity(len + 1); for (let i: usize = 0; i < len; i = i + 1) { let c = self.vec.get(i); if (c >= 'a' && c <= 'z') { v.push((char)((int)c - 32)); } else { v.push(c); } } v.push(0); let d = v.data; let l = v.len; let c_cap = v.cap; v.forget(); return String { vec: Vec { data: d, len: l, cap: c_cap } }; } /// Returns the first index of the character `target`, or `None`. fn find(self, target: char) -> Option { let len = self.length(); for (let i: usize = 0; i < len; i = i + 1) { if (self.vec.get(i) == target) { return Option::Some(i); } } return Option::None(); } /// Prints the string to stdout. fn print(self) { printf("%s", self.c_str()); fflush(stdout); } /// Prints the string to stdout followed by a newline. fn println(self) { printf("%s\n", self.c_str()); } /// Returns whether the string is empty. fn is_empty(self) -> bool { return self.length() == 0; } /// Returns whether the character `target` occurs. fn contains(self, target: char) -> bool { return self.find(target).is_some(); } /// Returns whether the string starts with `prefix`. fn starts_with(self, prefix: char*) -> bool { let plen = strlen(prefix); if plen > self.length() { return false; } let zero: c_int = 0; return strncmp(self.c_str(), prefix, plen) == zero; } /// Returns whether the string ends with `suffix`. fn ends_with(self, suffix: char*) -> bool { let slen = strlen(suffix); let len = self.length(); if slen > len { return false; } let offset = (int)(len - slen); let zero: c_int = 0; return strcmp(self.c_str() + offset, suffix) == zero; } /// Ensures capacity for at least `cap` bytes. fn reserve(self, cap: usize) { self.vec.grow_to_fit(cap + 1); } /// Frees the string memory. fn free(self) { self.vec.free(); } fn _utf8_seq_len(first_byte: char) -> usize { let b = (int)first_byte; if ((b & 0x80) == 0) { return 1; } if ((b & 0xE0) == 0xC0) { return 2; } if ((b & 0xF0) == 0xE0) { return 3; } if ((b & 0xF8) == 0xF0) { return 4; } return 1; // Fallback } /// Returns the number of Unicode code points. fn utf8_len(self) -> usize { let count: usize = 0; let i: usize = 0; let len = self.length(); while i < len { let c = self.vec.get(i); i = i + String::_utf8_seq_len(c); count = count + 1; } return count; } /// Returns the code point at byte index `idx` as a String. fn utf8_at(self, idx: usize) -> String { let count: usize = 0; let i: usize = 0; let len = self.length(); while i < len { let c = self.vec.get(i); let seq = String::_utf8_seq_len(c); if (count == idx) { return self.substring(i, seq); } i = i + seq; count = count + 1; } return String::new(""); } /// Returns the code point at byte index `idx` as a rune. fn utf8_get(self, idx: usize) -> rune { let count: usize = 0; let i: usize = 0; let len = self.length(); while i < len { let c = (byte)self.vec.get(i); let seq = String::_utf8_seq_len((char)c); if (count == idx) { if (seq == 1) { return (rune)c; } if (seq == 2) { return (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F)); } if (seq == 3) { return (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F)); } if (seq == 4) { return (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F)); } } i = i + seq; count = count + 1; } return (rune)0; } /// Returns a Vec of the code points. fn runes(self) -> Vec { let v = Vec::new(); let i: usize = 0; let len = self.length(); while i < len { let c = (byte)self.vec.get(i); let seq = String::_utf8_seq_len((char)c); let val: rune = 0; if (seq == 1) { val = (rune)c; } else if (seq == 2) { val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F)); } else if (seq == 3) { val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F)); } else if (seq == 4) { val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F)); } v.push(val); i = i + seq; } return v; } /// Returns an iterator over the code points. fn iterator(self) -> StringCharsIter { return self.chars(); } /// Returns an iterator over the code points. fn chars(self) -> StringCharsIter { return StringCharsIter { data: self.c_str(), len: self.length(), pos: 0 }; } /// Creates a String from a Vec of runes. fn from_runes_vec(runes: Vec) -> String { let s = String::new(""); for (let i: usize = 0; i < runes.len; i = i + 1) { s.push_rune(runes.get(i)); } return s; } /// Inserts a rune at byte index `idx`. fn insert_rune(self, idx: usize, r: rune) { let i: usize = 0; let count: usize = 0; let len = self.length(); while i < len && count < idx { i = i + String::_utf8_seq_len(self.vec.get(i)); count = count + 1; } let val = (uint32_t)r; if (val < 0x80) { self.vec.insert(i, (char)val); } else if (val < 0x800) { self.vec.insert(i, (char)(0xC0 | (val >> 6))); self.vec.insert(i + 1, (char)(0x80 | (val & 0x3F))); } else if (val < 0x10000) { self.vec.insert(i, (char)(0xE0 | (val >> 12))); self.vec.insert(i + 1, (char)(0x80 | ((val >> 6) & 0x3F))); self.vec.insert(i + 2, (char)(0x80 | (val & 0x3F))); } else { self.vec.insert(i, (char)(0xF0 | (val >> 18))); self.vec.insert(i + 1, (char)(0x80 | ((val >> 12) & 0x3F))); self.vec.insert(i + 2, (char)(0x80 | ((val >> 6) & 0x3F))); self.vec.insert(i + 3, (char)(0x80 | (val & 0x3F))); } } /// Removes and returns the rune at byte index `idx`. fn remove_rune_at(self, idx: usize) -> rune { let i: usize = 0; let count: usize = 0; let len = self.length(); while i < len && count < idx { i = i + String::_utf8_seq_len(self.vec.get(i)); count = count + 1; } if (i >= len) { return (rune)0; } let c = (byte)self.vec.get(i); let seq = String::_utf8_seq_len((char)c); let val: rune = 0; if (seq == 1) { val = (rune)c; } else if (seq == 2) { val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F)); } else if (seq == 3) { val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F)); } else if (seq == 4) { val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F)); } for (let j: usize = 0; j < seq; j = j + 1) { self.vec.remove(i); } return val; } /// Returns a substring of `num_chars` code points from `start_idx`. fn utf8_substr(self, start_idx: usize, num_chars: usize) -> String { if (num_chars == 0) { return String::new(""); } let byte_start: usize = 0; let byte_len: usize = 0; let count: usize = 0; let i: usize = 0; let len = self.length(); let found_start = false; while i < len { // Check if we reached the start char if (!found_start && count == start_idx) { byte_start = i; found_start = true; // Reset count to track chars collected count = 0; } else if (!found_start) { // Still seeking start let c = self.vec.get(i); i = i + String::_utf8_seq_len(c); count = count + 1; continue; } // If we are here, we are collecting chars if (count < num_chars) { let c = self.vec.get(i); let seq = String::_utf8_seq_len(c); byte_len = byte_len + seq; i = i + seq; count = count + 1; } else { break; } } if (!found_start) { return String::new(""); } return self.substring(byte_start, byte_len); } /// Splits on the delimiter character into a Vec of substrings. fn split(self, delim: char) -> Vec { let parts = Vec::new(); let len = self.length(); if (len == 0) { return parts; } let start: usize = 0; let i: usize = 0; while i < len { if (self.vec.get(i) == delim) { // Found delimiter parts.push(self.substring(start, i - start)); start = i + 1; } i = i + 1; } // Push last segment if (start <= len) { parts.push(self.substring(start, len - start)); } return parts; } /// Returns a copy with leading/trailing whitespace removed. fn trim(self) -> String { let start: usize = 0; let len = self.length(); let end = len; // Find start while (start < len) { let c = self.vec.get(start); if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { break; } start = start + 1; } if (start == len) { return String::new(""); } // Find end while (end > start) { let c = self.vec.get(end - 1); if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { break; } end = end - 1; } return self.substring(start, end - start); } /// Returns a copy with all `target` replaced by `replacement`. fn replace(self, target: char*, replacement: char*) -> String { let t_len = strlen(target); if (t_len == 0) { return self.substring(0, self.length()); // clone } let s_len = self.length(); let result = String::new(""); let i: usize = 0; while (i < s_len) { // Check if match if (i + t_len <= s_len) { let is_match = true; // Manual strncmp against vec data for (let k: usize = 0; k < t_len; k = k + 1) { if (self.vec.get(i + k) != target[k]) { is_match = false; break; } } if (is_match) { let r_str = String::new(replacement); result.append(&r_str); i = i + t_len; continue; } } // Append single char let v = Vec::new(); v.push(self.vec.get(i)); v.push(0); let ch_s = String::new(v.data); result.append(&ch_s); ch_s.free(); i = i + 1; } return result; } // Parses the string as a decimal integer. Returns 0 when the string is // not a valid integer. /// Parses the string as a decimal integer (0 when invalid). fn to_int(self) -> int { let len = self.length(); if (len == 0) { return 0; } let i: usize = 0; let neg = false; if (self.vec.get(0) == '-') { neg = true; i = 1; } else if (self.vec.get(0) == '+') { i = 1; } if (i >= len) { return 0; } let result: int = 0; while (i < len) { let c = self.vec.get(i); if (c < '0' || c > '9') { return 0; } result = result * 10 + (int)(c - '0'); i = i + 1; } if (neg) { return -result; } return result; } // Returns a new string with this string repeated `count` times. /// Returns a copy repeated `count` times. fn repeated(self, count: usize) -> String { let len = self.length(); let out = String::new(""); if (count == 0 || len == 0) { return out; } out.reserve(len * count); for (let i: usize = 0; i < count; i = i + 1) { out.append(self); } return out; } // Returns a new string with the characters in reverse order. /// Returns a copy with the characters reversed. fn reverse(self) -> String { let len = self.length(); let out = String::new(""); if (len == 0) { return out; } out.reserve(len); let i = len; while (i > 0) { i = i - 1; let v = Vec::new(); v.push(self.vec.get(i)); v.push(0); let ch_s = String::new(v.data); out.append(&ch_s); ch_s.free(); } return out; } // Returns a copy with leading whitespace removed. /// Returns a copy with leading whitespace removed. fn trim_start(self) -> String { let len = self.length(); let start: usize = 0; while (start < len) { let c = self.vec.get(start); if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { break; } start = start + 1; } if (start == len) { return String::new(""); } return self.substring(start, len - start); } // Returns a copy with trailing whitespace removed. /// Returns a copy with trailing whitespace removed. fn trim_end(self) -> String { let len = self.length(); let end = len; while (end > 0) { let c = self.vec.get(end - 1); if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { break; } end = end - 1; } return self.substring(0, end); } }