runes.ha (1839B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use encoding::utf8; 5 6 // Returns a slice of runes for a string in O(n). The caller must free the 7 // return value. 8 export fn torunes(s: str) []rune = { 9 let sl: []rune = []; 10 let iter = iter(s); 11 for (let r => next(&iter)) { 12 append(sl, r)!; 13 }; 14 return sl; 15 }; 16 17 // Returns a string from a slice of runes. The caller must free the return value. 18 export fn fromrunes(runes: []rune) str = { 19 let bytes: []u8 = []; 20 for (let r .. runes) { 21 const bs = utf8::encoderune(r)!; 22 append(bytes, bs...)!; 23 }; 24 return fromutf8_unsafe(bytes); 25 }; 26 27 // Returns a string's rune-length; the number of runes encoded in a string. 28 export fn runelen(s: str) size = { 29 let it = iter(s); 30 let n = 0z; 31 for (true) { 32 match (next(&it)) { 33 case rune => 34 n += 1; 35 case done => 36 break; 37 }; 38 }; 39 return n; 40 }; 41 42 @test fn fromrunes() void = { 43 const tests: [_](str, []rune) = [ 44 ("Harriet", ['H', 'a', 'r', 'r', 'i', 'e', 't']), 45 ("", []), 46 (".", ['.']), 47 ("\a\b\f\n\r\t\v", ['\a', '\b', '\f', '\n', '\r', '\t', '\v']), 48 ("Hello, world!", ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']), 49 ("¡Hola Mundo!", ['¡', 'H', 'o', 'l', 'a', ' ', 'M', 'u', 'n', 'd', 'o', '!']), 50 ("Γειά σου Κόσμε!", ['Γ', 'ε', 'ι', 'ά', ' ', 'σ', 'ο', 'υ', ' ', 'Κ', 'ό', 'σ', 'μ', 'ε', '!']), 51 ("Привет, мир!", ['П', 'р', 'и', 'в', 'е', 'т', ',', ' ', 'м', 'и', 'р', '!']), 52 ("こんにちは世界!", ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']), 53 ]; 54 55 for (let (string, runes) .. tests) { 56 const s = fromrunes(runes); 57 defer free(s); 58 assert(s == string); 59 60 const rs = torunes(s); 61 defer free(rs); 62 assert(len(rs) == len(runes)); 63 64 for (let j = 0z; j < len(rs); j += 1) { 65 assert(rs[j] == runes[j]); 66 }; 67 }; 68 };