hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };