index.ha (4531B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use encoding::utf8; 6 7 // Returns the index of the first occurance of 'needle' in the 'haystack', or 8 // void if not present. The index returned is the rune-wise index, not the 9 // byte-wise index. 10 export fn index(haystack: str, needle: (str | rune)) (size | void) = { 11 match (needle) { 12 case let r: rune => 13 return index_rune(haystack, r); 14 case let s: str => 15 return index_string(haystack, s); 16 }; 17 }; 18 19 // Returns the index of the last occurance of 'needle' in the 'haystack', or 20 // void if not present. The index returned is the rune-wise index, not the 21 // byte-wise index. 22 export fn rindex(haystack: str, needle: (str | rune)) (size | void) = { 23 match (needle) { 24 case let r: rune => 25 return rindex_rune(haystack, r); 26 case let s: str => 27 return rindex_string(haystack, s); 28 }; 29 }; 30 31 fn index_rune(s: str, r: rune) (size | void) = { 32 let iter = iter(s); 33 for (let i = 0z; true; i += 1) { 34 match (next(&iter)) { 35 case let n: rune => 36 if (r == n) { 37 return i; 38 }; 39 case done => 40 break; 41 }; 42 }; 43 }; 44 45 fn rindex_rune(s: str, r: rune) (size | void) = { 46 let iter = riter(s); 47 for (let i = len(s) - 1; true; i -= 1) { 48 match (next(&iter)) { 49 case let n: rune => 50 if (r == n) { 51 return i; 52 }; 53 case done => 54 break; 55 }; 56 }; 57 }; 58 59 fn index_string(s: str, needle: str) (size | void) = { 60 let s_iter = iter(s); 61 for (let i = 0z; true; i += 1) { 62 let rest_iter = s_iter; 63 let needle_iter = iter(needle); 64 for (true) { 65 const rest_rune = next(&rest_iter); 66 const needle_rune = next(&needle_iter); 67 if (rest_rune is done && !(needle_rune is done)) { 68 break; 69 }; 70 if (needle_rune is done) { 71 return i; 72 }; 73 if ((rest_rune as rune) != (needle_rune as rune)) { 74 break; 75 }; 76 }; 77 if (next(&s_iter) is done) { 78 break; 79 }; 80 }; 81 }; 82 83 fn rindex_string(s: str, needle: str) (size | void) = { 84 let s_iter = riter(s); 85 for (let i = len(s); true; i -= 1) { 86 let rest_iter = s_iter; 87 let needle_iter = riter(needle); 88 for (true) { 89 const rest_rune = next(&rest_iter); 90 const needle_rune = next(&needle_iter); 91 if (rest_rune is done && !(needle_rune is done)) { 92 break; 93 }; 94 if (needle_rune is done) { 95 return i - len(needle); 96 }; 97 if ((rest_rune as rune) != (needle_rune as rune)) { 98 break; 99 }; 100 }; 101 if (next(&s_iter) is done) { 102 break; 103 }; 104 }; 105 }; 106 107 108 @test fn index() void = { 109 assert(index("hello world", 'w') as size == 6); 110 assert(index("こんにちは", 'ち') as size == 3); 111 assert(index("こんにちは", 'q') is void); 112 113 assert(index("hello", "hello") as size == 0); 114 assert(index("hello world!", "hello") as size == 0); 115 assert(index("hello world!", "world") as size == 6); 116 assert(index("hello world!", "orld!") as size == 7); 117 assert(index("hello world!", "word") is void); 118 assert(index("こんにちは", "ちは") as size == 3); 119 assert(index("こんにちは", "きょうは") is void); 120 121 assert(index("hello world!", "o") as size == 4); 122 assert(rindex("hello world!", "o") as size == 7); 123 }; 124 125 // Returns the byte-wise index of the first occurance of 'needle' in the 126 // 'haystack', or void if not present. 127 export fn byteindex(haystack: str, needle: (str | rune)) (size | void) = { 128 return bytes::index(toutf8(haystack), match (needle) { 129 case let s: str => 130 yield toutf8(s); 131 case let r: rune => 132 yield if (r: u32 <= 0x7f) r: u8 else utf8::encoderune(r); 133 }); 134 }; 135 136 // Returns the byte-wise index of the last occurance of 'needle' in the 137 // 'haystack', or void if not present. 138 export fn rbyteindex(haystack: str, needle: (str | rune)) (size | void) = { 139 return bytes::rindex(toutf8(haystack), match (needle) { 140 case let s: str => 141 yield toutf8(s); 142 case let r: rune => 143 yield if (r: u32 <= 0x7f) r: u8 else utf8::encoderune(r); 144 }); 145 }; 146 147 @test fn byteindex() void = { 148 assert(byteindex("hello world", 'w') as size == 6); 149 assert(byteindex("こんにちは", 'ち') as size == 9); 150 assert(byteindex("こんにちは", 'q') is void); 151 152 assert(byteindex("hello", "hello") as size == 0); 153 assert(byteindex("hello world!", "hello") as size == 0); 154 assert(byteindex("hello world!", "world") as size == 6); 155 assert(byteindex("hello world!", "orld!") as size == 7); 156 assert(byteindex("hello world!", "word") is void); 157 assert(byteindex("こんにちは", "ちは") as size == 9); 158 assert(byteindex("こんにちは", "きょうは") is void); 159 160 assert(byteindex("またあったね", "た") as size == 3); 161 assert(rbyteindex("またあったね", "た") as size == 12); 162 };