diff options
Diffstat (limited to 'src/code_point.zig')
| -rw-r--r-- | src/code_point.zig | 55 |
1 files changed, 46 insertions, 9 deletions
diff --git a/src/code_point.zig b/src/code_point.zig index ed5fede..5aa12b7 100644 --- a/src/code_point.zig +++ b/src/code_point.zig | |||
| @@ -42,7 +42,7 @@ pub fn decode(bytes: []const u8, offset: u32) ?CodePoint { | |||
| 42 | .offset = offset, | 42 | .offset = offset, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | // Return replacement if we don' have a complete codepoint remaining. Consumes only one byte | 45 | // Return replacement if we don't have a complete codepoint remaining. Consumes only one byte. |
| 46 | if (cp.len > bytes.len) { | 46 | if (cp.len > bytes.len) { |
| 47 | // Unicode replacement code point. | 47 | // Unicode replacement code point. |
| 48 | return .{ | 48 | return .{ |
| @@ -76,21 +76,30 @@ pub const Iterator = struct { | |||
| 76 | bytes: []const u8, | 76 | bytes: []const u8, |
| 77 | i: u32 = 0, | 77 | i: u32 = 0, |
| 78 | 78 | ||
| 79 | pub fn next(self: *Iterator) ?CodePoint { | 79 | pub fn next(iter: *Iterator) ?CodePoint { |
| 80 | if (self.i >= self.bytes.len) return null; | 80 | if (iter.i >= iter.bytes.len) return null; |
| 81 | 81 | ||
| 82 | const res = decode(self.bytes[self.i..], self.i); | 82 | const res = decode(iter.bytes[iter.i..], iter.i); |
| 83 | if (res) |cp| { | 83 | if (res) |cp| { |
| 84 | self.i += cp.len; | 84 | iter.i += cp.len; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | return res; | 87 | return res; |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | pub fn peek(self: *Iterator) ?CodePoint { | 90 | pub fn peek(iter: *Iterator) ?CodePoint { |
| 91 | const saved_i = self.i; | 91 | const saved_i = iter.i; |
| 92 | defer self.i = saved_i; | 92 | defer iter.i = saved_i; |
| 93 | return self.next(); | 93 | return iter.next(); |
| 94 | } | ||
| 95 | |||
| 96 | /// Create a backward iterator at this point. It will repeat | ||
| 97 | /// the last CodePoint seen. | ||
| 98 | pub fn reverseIterator(iter: *Iterator) ReverseIterator { | ||
| 99 | if (iter.i == iter.bytes.len) { | ||
| 100 | return .init(iter.bytes); | ||
| 101 | } | ||
| 102 | return .{ .i = iter.i, .bytes = iter.bytes }; | ||
| 94 | } | 103 | } |
| 95 | }; | 104 | }; |
| 96 | 105 | ||
| @@ -127,6 +136,17 @@ pub const ReverseIterator = struct { | |||
| 127 | defer iter.i = saved_i; | 136 | defer iter.i = saved_i; |
| 128 | return iter.prev(); | 137 | return iter.prev(); |
| 129 | } | 138 | } |
| 139 | |||
| 140 | /// Create a forward iterator at this point. It will repeat the | ||
| 141 | /// last CodePoint seen. | ||
| 142 | pub fn forwardIterator(iter: *ReverseIterator) Iterator { | ||
| 143 | if (iter.i) |i| { | ||
| 144 | var fwd: Iterator = .{ .i = i, .bytes = iter.bytes }; | ||
| 145 | _ = fwd.next(); | ||
| 146 | return fwd; | ||
| 147 | } | ||
| 148 | return .{ .i = 0, .bytes = iter.bytes }; | ||
| 149 | } | ||
| 130 | }; | 150 | }; |
| 131 | 151 | ||
| 132 | inline fn followbyte(b: u8) bool { | 152 | inline fn followbyte(b: u8) bool { |
| @@ -182,6 +202,23 @@ test ReverseIterator { | |||
| 182 | try testing.expectEqual(@as(?CodePoint, null), r_iter.prev()); | 202 | try testing.expectEqual(@as(?CodePoint, null), r_iter.prev()); |
| 183 | try testing.expectEqual(@as(?CodePoint, null), r_iter.prev()); | 203 | try testing.expectEqual(@as(?CodePoint, null), r_iter.prev()); |
| 184 | } | 204 | } |
| 205 | { | ||
| 206 | var r_iter: ReverseIterator = .init("123"); | ||
| 207 | try testing.expectEqual(@as(u21, '3'), r_iter.prev().?.code); | ||
| 208 | try testing.expectEqual(@as(u21, '2'), r_iter.prev().?.code); | ||
| 209 | try testing.expectEqual(@as(u21, '1'), r_iter.prev().?.code); | ||
| 210 | var iter = r_iter.forwardIterator(); | ||
| 211 | try testing.expectEqual(@as(u21, '1'), iter.next().?.code); | ||
| 212 | try testing.expectEqual(@as(u21, '2'), iter.next().?.code); | ||
| 213 | try testing.expectEqual(@as(u21, '3'), iter.next().?.code); | ||
| 214 | r_iter = iter.reverseIterator(); | ||
| 215 | try testing.expectEqual(@as(u21, '3'), r_iter.prev().?.code); | ||
| 216 | try testing.expectEqual(@as(u21, '2'), r_iter.prev().?.code); | ||
| 217 | iter = r_iter.forwardIterator(); | ||
| 218 | r_iter = iter.reverseIterator(); | ||
| 219 | try testing.expectEqual(@as(u21, '2'), iter.next().?.code); | ||
| 220 | try testing.expectEqual(@as(u21, '2'), r_iter.prev().?.code); | ||
| 221 | } | ||
| 185 | } | 222 | } |
| 186 | 223 | ||
| 187 | const testing = std.testing; | 224 | const testing = std.testing; |