mittens 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +1 -0
  4. data/README.md +2 -2
  5. data/Rakefile +10 -3
  6. data/lib/mittens/version.rb +1 -1
  7. data/vendor/snowball/.github/workflows/ci.yml +144 -17
  8. data/vendor/snowball/.github/workflows/coverage.yml +117 -0
  9. data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
  10. data/vendor/snowball/.gitignore +33 -7
  11. data/vendor/snowball/CONTRIBUTING.rst +7 -0
  12. data/vendor/snowball/COPYING +1 -1
  13. data/vendor/snowball/GNUmakefile +736 -298
  14. data/vendor/snowball/NEWS +1394 -7
  15. data/vendor/snowball/README.rst +8 -8
  16. data/vendor/snowball/ada/generate/generate.adb +5 -5
  17. data/vendor/snowball/ada/src/stemmer.adb +177 -188
  18. data/vendor/snowball/ada/src/stemmer.ads +86 -89
  19. data/vendor/snowball/ada/src/stemwords.adb +21 -5
  20. data/vendor/snowball/algorithms/czech.sbl +255 -0
  21. data/vendor/snowball/algorithms/danish.sbl +22 -10
  22. data/vendor/snowball/algorithms/english.sbl +7 -4
  23. data/vendor/snowball/algorithms/esperanto.sbl +6 -7
  24. data/vendor/snowball/algorithms/estonian.sbl +9 -4
  25. data/vendor/snowball/algorithms/finnish.sbl +33 -21
  26. data/vendor/snowball/algorithms/german.sbl +5 -0
  27. data/vendor/snowball/algorithms/indonesian.sbl +115 -96
  28. data/vendor/snowball/algorithms/italian.sbl +26 -1
  29. data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
  30. data/vendor/snowball/algorithms/norwegian.sbl +17 -6
  31. data/vendor/snowball/algorithms/persian.sbl +387 -0
  32. data/vendor/snowball/algorithms/polish.sbl +177 -0
  33. data/vendor/snowball/algorithms/sesotho.sbl +115 -0
  34. data/vendor/snowball/algorithms/turkish.sbl +4 -4
  35. data/vendor/snowball/compiler/analyser.c +2002 -711
  36. data/vendor/snowball/compiler/driver.c +460 -298
  37. data/vendor/snowball/compiler/generator.c +439 -1965
  38. data/vendor/snowball/compiler/generator_ada.c +605 -430
  39. data/vendor/snowball/compiler/generator_c.c +2227 -0
  40. data/vendor/snowball/compiler/generator_csharp.c +365 -274
  41. data/vendor/snowball/compiler/generator_dart.c +1476 -0
  42. data/vendor/snowball/compiler/generator_go.c +348 -270
  43. data/vendor/snowball/compiler/generator_java.c +330 -233
  44. data/vendor/snowball/compiler/generator_js.c +534 -385
  45. data/vendor/snowball/compiler/generator_pascal.c +361 -320
  46. data/vendor/snowball/compiler/generator_php.c +1445 -0
  47. data/vendor/snowball/compiler/generator_python.c +399 -299
  48. data/vendor/snowball/compiler/generator_rust.c +370 -261
  49. data/vendor/snowball/compiler/generator_zig.c +1474 -0
  50. data/vendor/snowball/compiler/header.h +153 -86
  51. data/vendor/snowball/compiler/space.c +121 -63
  52. data/vendor/snowball/compiler/tokeniser.c +286 -182
  53. data/vendor/snowball/compiler/tokens.h +71 -0
  54. data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
  55. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
  56. data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
  57. data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
  58. data/vendor/snowball/cxx/.gitignore +4 -0
  59. data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
  60. data/vendor/snowball/cxx/stemmer.h +12 -0
  61. data/vendor/snowball/cxx/stemwords.cxx +176 -0
  62. data/vendor/snowball/cxx/utilities.cxx +2 -0
  63. data/vendor/snowball/dart/.gitignore +4 -0
  64. data/vendor/snowball/dart/analysis_options.yaml +1 -0
  65. data/vendor/snowball/dart/example/test_app.dart +65 -0
  66. data/vendor/snowball/dart/generate_algorithms.pl +21 -0
  67. data/vendor/snowball/dart/lib/snowball.dart +18 -0
  68. data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
  69. data/vendor/snowball/dart/pubspec.yaml +17 -0
  70. data/vendor/snowball/doc/libstemmer_dart_README +37 -0
  71. data/vendor/snowball/doc/libstemmer_js_README +2 -2
  72. data/vendor/snowball/doc/libstemmer_php_README +38 -0
  73. data/vendor/snowball/doc/libstemmer_python_README +3 -3
  74. data/vendor/snowball/examples/stemwords.c +18 -3
  75. data/vendor/snowball/go/README.md +12 -4
  76. data/vendor/snowball/go/env.go +12 -14
  77. data/vendor/snowball/go/stemwords/main.go +2 -1
  78. data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
  79. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
  80. data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
  81. data/vendor/snowball/javascript/base-stemmer.js +219 -252
  82. data/vendor/snowball/javascript/stemwords.js +86 -58
  83. data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
  84. data/vendor/snowball/libstemmer/modules.txt +4 -0
  85. data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
  86. data/vendor/snowball/php/base-stemmer.php +453 -0
  87. data/vendor/snowball/php/stemwords.php +25 -0
  88. data/vendor/snowball/python/create_init.py +16 -21
  89. data/vendor/snowball/python/pyproject.toml +3 -0
  90. data/vendor/snowball/python/setup.py +5 -7
  91. data/vendor/snowball/python/snowballstemmer/among.py +1 -2
  92. data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
  93. data/vendor/snowball/python/stemwords.py +72 -63
  94. data/vendor/snowball/runtime/api.c +11 -42
  95. data/vendor/snowball/runtime/api.h +11 -9
  96. data/vendor/snowball/runtime/snowball_runtime.h +110 -0
  97. data/vendor/snowball/runtime/utilities.c +282 -106
  98. data/vendor/snowball/rust/src/main.rs +2 -2
  99. data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
  100. data/vendor/snowball/tests/compilertest +62 -0
  101. data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
  102. data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
  103. data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
  104. data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
  105. data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
  106. data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
  107. data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
  108. data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
  109. data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
  110. data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
  111. data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
  112. data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
  113. data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
  114. data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
  115. data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
  116. data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
  117. data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
  118. data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
  119. data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
  120. data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
  121. data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
  122. data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
  123. data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
  124. data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
  125. data/vendor/snowball/tests/runtime/among.sbl +19 -0
  126. data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
  127. data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
  128. data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
  129. data/vendor/snowball/tests/runtime/externals.sbl +22 -0
  130. data/vendor/snowball/tests/runtime/hop.sbl +12 -0
  131. data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
  132. data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
  133. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  134. data/vendor/snowball/tests/runtime/not.sbl +30 -0
  135. data/vendor/snowball/tests/runtime/or.sbl +17 -0
  136. data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
  137. data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
  138. data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
  139. data/vendor/snowball/tests/runtime/slice.sbl +27 -0
  140. data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
  141. data/vendor/snowball/tests/runtime/strings.sbl +66 -0
  142. data/vendor/snowball/tests/runtime/test.sbl +19 -0
  143. data/vendor/snowball/tests/stemtest.c +41 -1
  144. data/vendor/snowball/tests/syntax/canon.sbl +32 -0
  145. data/vendor/snowball/tests/syntax/canon.stderr +0 -0
  146. data/vendor/snowball/tests/syntax/canon.syntax +57 -0
  147. data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
  148. data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
  149. data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
  150. data/vendor/snowball/tests/syntax/inline.sbl +144 -0
  151. data/vendor/snowball/tests/syntax/inline.stderr +0 -0
  152. data/vendor/snowball/tests/syntax/inline.syntax +121 -0
  153. data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
  154. data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
  155. data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
  156. data/vendor/snowball/tests/syntax/loops.sbl +14 -0
  157. data/vendor/snowball/tests/syntax/loops.stderr +9 -0
  158. data/vendor/snowball/tests/syntax/loops.syntax +29 -0
  159. data/vendor/snowball/tests/syntax/noops.sbl +76 -0
  160. data/vendor/snowball/tests/syntax/noops.stderr +27 -0
  161. data/vendor/snowball/tests/syntax/noops.syntax +135 -0
  162. data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
  163. data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
  164. data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
  165. data/vendor/snowball/tests/syntax/unused.sbl +18 -0
  166. data/vendor/snowball/tests/syntax/unused.stderr +7 -0
  167. data/vendor/snowball/tests/syntax/unused.syntax +3 -0
  168. data/vendor/snowball/zig/env.zig +599 -0
  169. data/vendor/snowball/zig/generate_algorithms.pl +19 -0
  170. data/vendor/snowball/zig/stemwords.zig +123 -0
  171. metadata +102 -4
  172. data/vendor/snowball/compiler/syswords.h +0 -86
  173. data/vendor/snowball/runtime/header.h +0 -62
@@ -0,0 +1,599 @@
1
+ const std = @import("std");
2
+
3
+ pub const MaxInt = std.math.maxInt(i32);
4
+ pub const MinInt = std.math.minInt(i32);
5
+
6
+ pub const Among = struct {
7
+ s: []const u8,
8
+ substring_i: i32,
9
+ result: i32,
10
+ method: ?*const fn (*Env, *anyopaque) bool,
11
+ };
12
+
13
+ pub const String = struct {
14
+ value: []u8 = &.{},
15
+
16
+ pub fn deinit(self: *String, allocator: std.mem.Allocator) void {
17
+ if (self.value.len > 0) {
18
+ allocator.free(self.value);
19
+ }
20
+ self.value = &.{};
21
+ }
22
+
23
+ pub fn assign(self: *String, allocator: std.mem.Allocator, s: []const u8) !void {
24
+ const new_value = try dupeOrEmpty(allocator, s);
25
+ self.deinit(allocator);
26
+ self.value = new_value;
27
+ }
28
+
29
+ pub fn slice(self: *const String) []const u8 {
30
+ return self.value;
31
+ }
32
+ };
33
+
34
+ pub const Env = struct {
35
+ current: []u8,
36
+ cursor: usize,
37
+ limit: usize,
38
+ limit_backward: usize,
39
+ bra: usize,
40
+ ket: usize,
41
+ allocator: std.mem.Allocator,
42
+
43
+ pub fn init(allocator: std.mem.Allocator) Env {
44
+ return .{
45
+ .current = &.{},
46
+ .cursor = 0,
47
+ .limit = 0,
48
+ .limit_backward = 0,
49
+ .bra = 0,
50
+ .ket = 0,
51
+ .allocator = allocator,
52
+ };
53
+ }
54
+
55
+ pub fn deinit(self: *Env) void {
56
+ if (self.current.len > 0) {
57
+ self.allocator.free(self.current);
58
+ }
59
+ }
60
+
61
+ pub fn setCurrent(self: *Env, s: []const u8) !void {
62
+ const new_current = try dupeOrEmpty(self.allocator, s);
63
+ if (self.current.len > 0) {
64
+ self.allocator.free(self.current);
65
+ }
66
+ self.current = new_current;
67
+ self.cursor = 0;
68
+ self.limit = s.len;
69
+ self.limit_backward = 0;
70
+ self.bra = 0;
71
+ self.ket = s.len;
72
+ }
73
+
74
+ pub fn getCurrent(self: *const Env) []const u8 {
75
+ return self.current[0..self.limit];
76
+ }
77
+
78
+ pub fn replaceS(self: *Env, bra_arg: usize, ket_arg: usize, s: []const u8) !i32 {
79
+ const adjustment: i32 = @as(i32, @intCast(s.len)) - (@as(i32, @intCast(ket_arg)) - @as(i32, @intCast(bra_arg)));
80
+
81
+ // Always build a new buffer: current[0..bra] + s + current[rsplit..]
82
+ // Must include content beyond limit (setlimit may be active).
83
+ const rsplit = if (ket_arg < bra_arg) bra_arg else ket_arg;
84
+ const tail_len = self.current.len - rsplit;
85
+ const new_len = bra_arg + s.len + tail_len;
86
+
87
+ const new_buf = try allocOrEmpty(self.allocator, new_len);
88
+ std.mem.copyForwards(u8, new_buf[0..bra_arg], self.current[0..bra_arg]);
89
+ std.mem.copyForwards(u8, new_buf[bra_arg..][0..s.len], s);
90
+ std.mem.copyForwards(u8, new_buf[bra_arg + s.len ..][0..tail_len], self.current[rsplit..][0..tail_len]);
91
+ if (self.current.len > 0) {
92
+ self.allocator.free(self.current);
93
+ }
94
+ self.current = new_buf;
95
+
96
+ self.limit = adjustIndex(self.limit, adjustment);
97
+
98
+ if (self.cursor >= ket_arg) {
99
+ self.cursor = adjustIndex(self.cursor, adjustment);
100
+ } else if (self.cursor > bra_arg) {
101
+ self.cursor = bra_arg;
102
+ }
103
+
104
+ return adjustment;
105
+ }
106
+
107
+ pub fn eqS(self: *Env, s: []const u8) bool {
108
+ if (self.cursor >= self.limit) return false;
109
+ if (self.cursor + s.len > self.limit) return false;
110
+ if (!std.mem.eql(u8, self.current[self.cursor..][0..s.len], s)) return false;
111
+ self.cursor += s.len;
112
+ return true;
113
+ }
114
+
115
+ pub fn eqSB(self: *Env, s: []const u8) bool {
116
+ if (@as(i32, @intCast(self.cursor)) - @as(i32, @intCast(self.limit_backward)) < @as(i32, @intCast(s.len))) return false;
117
+ if (!std.mem.eql(u8, self.current[self.cursor - s.len ..][0..s.len], s)) return false;
118
+ self.cursor -= s.len;
119
+ return true;
120
+ }
121
+
122
+ pub fn sliceFrom(self: *Env, s: []const u8) !void {
123
+ const bra_val = self.bra;
124
+ _ = try self.replaceS(bra_val, self.ket, s);
125
+ self.ket = bra_val + s.len;
126
+ }
127
+
128
+ pub fn sliceDel(self: *Env) !void {
129
+ try self.sliceFrom("");
130
+ }
131
+
132
+ pub fn insert(self: *Env, bra_arg: usize, ket_arg: usize, s: []const u8) !void {
133
+ const adjustment = try self.replaceS(bra_arg, ket_arg, s);
134
+ if (bra_arg <= self.bra) {
135
+ self.bra = adjustIndex(self.bra, adjustment);
136
+ }
137
+ if (bra_arg <= self.ket) {
138
+ self.ket = adjustIndex(self.ket, adjustment);
139
+ }
140
+ }
141
+
142
+ pub fn sliceTo(self: *const Env) []const u8 {
143
+ return self.current[self.bra..self.ket];
144
+ }
145
+
146
+ pub fn assignTo(self: *const Env) []const u8 {
147
+ return self.getCurrent();
148
+ }
149
+
150
+ pub fn nextChar(self: *Env) void {
151
+ self.cursor += 1;
152
+ while (self.cursor < self.current.len and !onCharBoundary(self.current, self.cursor)) {
153
+ self.cursor += 1;
154
+ }
155
+ }
156
+
157
+ pub fn prevChar(self: *Env) void {
158
+ self.cursor -= 1;
159
+ while (self.cursor > 0 and !onCharBoundary(self.current, self.cursor)) {
160
+ self.cursor -= 1;
161
+ }
162
+ }
163
+
164
+ pub fn hop(self: *Env, delta: i32) bool {
165
+ var d = delta;
166
+ var res = self.cursor;
167
+ while (d > 0) {
168
+ d -= 1;
169
+ if (res >= self.limit) return false;
170
+ res += 1;
171
+ while (res < self.limit and !onCharBoundary(self.current, res)) {
172
+ res += 1;
173
+ }
174
+ }
175
+ self.cursor = res;
176
+ return true;
177
+ }
178
+
179
+ pub fn hopChecked(self: *Env, delta: i32) bool {
180
+ return delta >= 0 and self.hop(delta);
181
+ }
182
+
183
+ pub fn hopBack(self: *Env, delta: i32) bool {
184
+ var d = delta;
185
+ var res = self.cursor;
186
+ while (d > 0) {
187
+ d -= 1;
188
+ if (res <= self.limit_backward) return false;
189
+ res -= 1;
190
+ while (res > self.limit_backward and !onCharBoundary(self.current, res)) {
191
+ res -= 1;
192
+ }
193
+ }
194
+ self.cursor = res;
195
+ return true;
196
+ }
197
+
198
+ pub fn hopBackChecked(self: *Env, delta: i32) bool {
199
+ return delta >= 0 and self.hopBack(delta);
200
+ }
201
+
202
+ pub fn inGrouping(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
203
+ if (self.cursor >= self.limit) return false;
204
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
205
+ if (r > max_ch or r < min_ch) return false;
206
+ const idx = r - min_ch;
207
+ if (!inBitmap(chars, idx)) return false;
208
+ self.nextChar();
209
+ return true;
210
+ }
211
+
212
+ pub fn inGroupingB(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
213
+ if (self.cursor <= self.limit_backward) return false;
214
+ const c = self.cursor;
215
+ self.prevChar();
216
+ const r = decodeRune(self.current[self.cursor..]) orelse {
217
+ self.cursor = c;
218
+ return false;
219
+ };
220
+ if (r > max_ch or r < min_ch) {
221
+ self.cursor = c;
222
+ return false;
223
+ }
224
+ const idx = r - min_ch;
225
+ if (!inBitmap(chars, idx)) {
226
+ self.cursor = c;
227
+ return false;
228
+ }
229
+ return true;
230
+ }
231
+
232
+ pub fn outGrouping(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
233
+ if (self.cursor >= self.limit) return false;
234
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
235
+ if (r > max_ch or r < min_ch) {
236
+ self.nextChar();
237
+ return true;
238
+ }
239
+ const idx = r - min_ch;
240
+ if (!inBitmap(chars, idx)) {
241
+ self.nextChar();
242
+ return true;
243
+ }
244
+ return false;
245
+ }
246
+
247
+ pub fn outGroupingB(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
248
+ if (self.cursor <= self.limit_backward) return false;
249
+ const c = self.cursor;
250
+ self.prevChar();
251
+ const r = decodeRune(self.current[self.cursor..]) orelse {
252
+ self.cursor = c;
253
+ return false;
254
+ };
255
+ if (r > max_ch or r < min_ch) return true;
256
+ const idx = r - min_ch;
257
+ if (!inBitmap(chars, idx)) return true;
258
+ self.cursor = c;
259
+ return false;
260
+ }
261
+
262
+ pub fn goInGrouping(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
263
+ while (self.cursor < self.limit) {
264
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
265
+ if (r > max_ch or r < min_ch) return true;
266
+ const idx = r - min_ch;
267
+ if (!inBitmap(chars, idx)) return true;
268
+ self.nextChar();
269
+ }
270
+ return false;
271
+ }
272
+
273
+ pub fn goInGroupingB(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
274
+ while (self.cursor > self.limit_backward) {
275
+ const c = self.cursor;
276
+ self.prevChar();
277
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
278
+ if (r > max_ch or r < min_ch) {
279
+ self.cursor = c;
280
+ return true;
281
+ }
282
+ const idx = r - min_ch;
283
+ if (!inBitmap(chars, idx)) {
284
+ self.cursor = c;
285
+ return true;
286
+ }
287
+ }
288
+ return false;
289
+ }
290
+
291
+ pub fn goOutGrouping(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
292
+ while (self.cursor < self.limit) {
293
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
294
+ if (r <= max_ch and r >= min_ch) {
295
+ const idx = r - min_ch;
296
+ if (inBitmap(chars, idx)) return true;
297
+ }
298
+ self.nextChar();
299
+ }
300
+ return false;
301
+ }
302
+
303
+ pub fn goOutGroupingB(self: *Env, chars: []const u8, min_ch: i32, max_ch: i32) bool {
304
+ while (self.cursor > self.limit_backward) {
305
+ const c = self.cursor;
306
+ self.prevChar();
307
+ const r = decodeRune(self.current[self.cursor..]) orelse return false;
308
+ if (r <= max_ch and r >= min_ch) {
309
+ const idx = r - min_ch;
310
+ if (inBitmap(chars, idx)) {
311
+ self.cursor = c;
312
+ return true;
313
+ }
314
+ }
315
+ }
316
+ return false;
317
+ }
318
+
319
+ pub fn findAmong(self: *Env, amongs: []const Among, ctx: *anyopaque) i32 {
320
+ var i: i32 = 0;
321
+ var j: i32 = @intCast(amongs.len);
322
+
323
+ const c = self.cursor;
324
+ const l = self.limit;
325
+
326
+ var common_i: usize = 0;
327
+ var common_j: usize = 0;
328
+
329
+ var first_key_inspected = false;
330
+ while (true) {
331
+ const k: usize = @intCast(i + @divTrunc(j - i, 2));
332
+ var diff: i32 = 0;
333
+ var common = @min(common_i, common_j);
334
+ const w = amongs[k];
335
+ var lvar: usize = common;
336
+ while (lvar < w.s.len) : (lvar += 1) {
337
+ if (c + common == l) {
338
+ diff -= 1;
339
+ break;
340
+ }
341
+ diff = @as(i32, @intCast(self.current[c + common])) - @as(i32, @intCast(w.s[lvar]));
342
+ if (diff != 0) break;
343
+ common += 1;
344
+ }
345
+ if (diff < 0) {
346
+ j = @intCast(k);
347
+ common_j = common;
348
+ } else {
349
+ i = @intCast(k);
350
+ common_i = common;
351
+ }
352
+ if (j - i <= 1) {
353
+ if (i > 0) break;
354
+ if (j == i) break;
355
+ if (first_key_inspected) break;
356
+ first_key_inspected = true;
357
+ }
358
+ }
359
+
360
+ while (true) {
361
+ const w = amongs[@intCast(i)];
362
+ if (common_i >= w.s.len) {
363
+ self.cursor = c + w.s.len;
364
+ if (w.method) |method| {
365
+ if (method(self, ctx)) {
366
+ self.cursor = c + w.s.len;
367
+ return w.result;
368
+ }
369
+ } else {
370
+ return w.result;
371
+ }
372
+ }
373
+ i = w.substring_i;
374
+ if (i < 0) return 0;
375
+ }
376
+ }
377
+
378
+ pub fn findAmongB(self: *Env, amongs: []const Among, ctx: *anyopaque) i32 {
379
+ var i: i32 = 0;
380
+ var j: i32 = @intCast(amongs.len);
381
+
382
+ const c = self.cursor;
383
+ const lb = self.limit_backward;
384
+
385
+ var common_i: usize = 0;
386
+ var common_j: usize = 0;
387
+
388
+ var first_key_inspected = false;
389
+ while (true) {
390
+ const k: usize = @intCast(i + @divTrunc(j - i, 2));
391
+ var diff: i32 = 0;
392
+ var common = @min(common_i, common_j);
393
+ const w = amongs[k];
394
+ {
395
+ var lvar_signed: i32 = @as(i32, @intCast(w.s.len)) - @as(i32, @intCast(common)) - 1;
396
+ while (lvar_signed >= 0) : (lvar_signed -= 1) {
397
+ const lvar: usize = @intCast(lvar_signed);
398
+ if (c - common == lb) {
399
+ diff -= 1;
400
+ break;
401
+ }
402
+ diff = @as(i32, @intCast(self.current[c - common - 1])) - @as(i32, @intCast(w.s[lvar]));
403
+ if (diff != 0) break;
404
+ common += 1;
405
+ }
406
+ }
407
+ if (diff < 0) {
408
+ j = @intCast(k);
409
+ common_j = common;
410
+ } else {
411
+ i = @intCast(k);
412
+ common_i = common;
413
+ }
414
+ if (j - i <= 1) {
415
+ if (i > 0) break;
416
+ if (j == i) break;
417
+ if (first_key_inspected) break;
418
+ first_key_inspected = true;
419
+ }
420
+ }
421
+
422
+ while (true) {
423
+ const w = amongs[@intCast(i)];
424
+ if (common_i >= w.s.len) {
425
+ self.cursor = c - w.s.len;
426
+ if (w.method) |method| {
427
+ if (method(self, ctx)) {
428
+ self.cursor = c - w.s.len;
429
+ return w.result;
430
+ }
431
+ } else {
432
+ return w.result;
433
+ }
434
+ }
435
+ i = w.substring_i;
436
+ if (i < 0) return 0;
437
+ }
438
+ }
439
+
440
+ pub fn clone(self: *const Env) !Env {
441
+ var c = self.*;
442
+ c.current = try dupeOrEmpty(self.allocator, self.current);
443
+ return c;
444
+ }
445
+
446
+ pub fn copyFrom(self: *Env, other: *const Env) !void {
447
+ if (self == other) return;
448
+ const new_current = try dupeOrEmpty(self.allocator, other.current);
449
+ if (self.current.len > 0) {
450
+ self.allocator.free(self.current);
451
+ }
452
+ self.current = new_current;
453
+ self.cursor = other.cursor;
454
+ self.limit = other.limit;
455
+ self.limit_backward = other.limit_backward;
456
+ self.bra = other.bra;
457
+ self.ket = other.ket;
458
+ }
459
+
460
+ pub fn debug(self: *const Env, count: i32, line_number: i32) void {
461
+ _ = self;
462
+ std.log.debug("snowball debug, count: {d}, line: {d}", .{ count, line_number });
463
+ }
464
+ };
465
+
466
+ fn allocOrEmpty(allocator: std.mem.Allocator, len: usize) ![]u8 {
467
+ if (len == 0) return &.{};
468
+ return allocator.alloc(u8, len);
469
+ }
470
+
471
+ fn dupeOrEmpty(allocator: std.mem.Allocator, s: []const u8) ![]u8 {
472
+ if (s.len == 0) return &.{};
473
+ return allocator.dupe(u8, s);
474
+ }
475
+
476
+ fn adjustIndex(value: usize, adjustment: i32) usize {
477
+ if (adjustment >= 0) {
478
+ return value + @as(usize, @intCast(adjustment));
479
+ }
480
+ return value - @as(usize, @intCast(-adjustment));
481
+ }
482
+
483
+ fn inBitmap(chars: []const u8, idx: i32) bool {
484
+ const u_idx: u32 = @intCast(idx);
485
+ return (chars[u_idx >> 3] & (@as(u8, 1) << @intCast(@as(u3, @truncate(u_idx))))) != 0;
486
+ }
487
+
488
+ fn onCharBoundary(s: []const u8, pos: usize) bool {
489
+ if (pos == 0 or pos >= s.len) return true;
490
+ // A byte is a UTF-8 start byte if its top two bits are not 10xxxxxx.
491
+ return (s[pos] & 0xC0) != 0x80;
492
+ }
493
+
494
+ fn decodeRune(s: []const u8) ?i32 {
495
+ if (s.len == 0) return null;
496
+ const b0 = s[0];
497
+ if (b0 < 0x80) return @intCast(b0);
498
+ if (b0 < 0xC0) return null; // continuation byte
499
+ if (b0 < 0xE0) {
500
+ if (s.len < 2) return null;
501
+ return (@as(i32, b0 & 0x1F) << 6) | @as(i32, s[1] & 0x3F);
502
+ }
503
+ if (b0 < 0xF0) {
504
+ if (s.len < 3) return null;
505
+ return (@as(i32, b0 & 0x0F) << 12) | (@as(i32, s[1] & 0x3F) << 6) | @as(i32, s[2] & 0x3F);
506
+ }
507
+ if (s.len < 4) return null;
508
+ return (@as(i32, b0 & 0x07) << 18) | (@as(i32, s[1] & 0x3F) << 12) | (@as(i32, s[2] & 0x3F) << 6) | @as(i32, s[3] & 0x3F);
509
+ }
510
+
511
+ pub fn runeCountInString(s: []const u8) i32 {
512
+ var count: i32 = 0;
513
+ var i: usize = 0;
514
+ while (i < s.len) {
515
+ if (s[i] < 0x80) {
516
+ i += 1;
517
+ } else if (s[i] < 0xE0) {
518
+ i += 2;
519
+ } else if (s[i] < 0xF0) {
520
+ i += 3;
521
+ } else {
522
+ i += 4;
523
+ }
524
+ count += 1;
525
+ }
526
+ return count;
527
+ }
528
+
529
+ test "setCurrent preserves existing state on allocation failure" {
530
+ var failing_allocator = std.testing.FailingAllocator.init(std.testing.allocator, .{
531
+ .fail_index = 1,
532
+ });
533
+ var env = Env.init(failing_allocator.allocator());
534
+ defer env.deinit();
535
+
536
+ try env.setCurrent("abc");
537
+ env.cursor = 1;
538
+ env.limit_backward = 1;
539
+ env.bra = 1;
540
+ env.ket = 2;
541
+
542
+ try std.testing.expectError(error.OutOfMemory, env.setCurrent("wxyz"));
543
+ try std.testing.expectEqualStrings("abc", env.current);
544
+ try std.testing.expectEqual(@as(usize, 1), env.cursor);
545
+ try std.testing.expectEqual(@as(usize, 3), env.limit);
546
+ try std.testing.expectEqual(@as(usize, 1), env.limit_backward);
547
+ try std.testing.expectEqual(@as(usize, 1), env.bra);
548
+ try std.testing.expectEqual(@as(usize, 2), env.ket);
549
+ }
550
+
551
+ test "copyFrom preserves existing state on allocation failure" {
552
+ var failing_allocator = std.testing.FailingAllocator.init(std.testing.allocator, .{
553
+ .fail_index = 2,
554
+ });
555
+ const allocator = failing_allocator.allocator();
556
+
557
+ var env = Env.init(allocator);
558
+ defer env.deinit();
559
+ try env.setCurrent("abc");
560
+ env.cursor = 1;
561
+ env.limit_backward = 1;
562
+ env.bra = 1;
563
+ env.ket = 2;
564
+
565
+ var other = Env.init(allocator);
566
+ defer other.deinit();
567
+ try other.setCurrent("wxyz");
568
+ other.cursor = 3;
569
+ other.limit_backward = 1;
570
+ other.bra = 2;
571
+ other.ket = 4;
572
+
573
+ try std.testing.expectError(error.OutOfMemory, env.copyFrom(&other));
574
+ try std.testing.expectEqualStrings("abc", env.current);
575
+ try std.testing.expectEqual(@as(usize, 1), env.cursor);
576
+ try std.testing.expectEqual(@as(usize, 3), env.limit);
577
+ try std.testing.expectEqual(@as(usize, 1), env.limit_backward);
578
+ try std.testing.expectEqual(@as(usize, 1), env.bra);
579
+ try std.testing.expectEqual(@as(usize, 2), env.ket);
580
+ }
581
+
582
+ test "copyFrom supports self copy" {
583
+ var env = Env.init(std.testing.allocator);
584
+ defer env.deinit();
585
+
586
+ try env.setCurrent("abc");
587
+ env.cursor = 1;
588
+ env.limit_backward = 1;
589
+ env.bra = 1;
590
+ env.ket = 2;
591
+
592
+ try env.copyFrom(&env);
593
+ try std.testing.expectEqualStrings("abc", env.current);
594
+ try std.testing.expectEqual(@as(usize, 1), env.cursor);
595
+ try std.testing.expectEqual(@as(usize, 3), env.limit);
596
+ try std.testing.expectEqual(@as(usize, 1), env.limit_backward);
597
+ try std.testing.expectEqual(@as(usize, 1), env.bra);
598
+ try std.testing.expectEqual(@as(usize, 2), env.ket);
599
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env perl
2
+ use strict;
3
+ use warnings;
4
+
5
+ my @algorithms = @ARGV;
6
+
7
+ print("// Generated by generate_algorithms.pl - DO NOT EDIT\n\n");
8
+ print("const snowball = \@import(\"env.zig\");\n\n");
9
+ foreach my $alg (@algorithms) {
10
+ # Suffix constant with `_stemmer` to avoid generating bad code if
11
+ # the algorithm name is a zig reserved word.
12
+ print("const ${alg}_stemmer = \@import(\"${alg}_stemmer.zig\");\n");
13
+ }
14
+ print("\npub const StemFn = *const fn (*snowball.Env) bool;\n\n");
15
+ print("pub const algorithms = [_]struct { name: []const u8, stem: StemFn }{\n");
16
+ foreach my $alg (@algorithms) {
17
+ print(" .{ .name = \"$alg\", .stem = ${alg}_stemmer.stem },\n");
18
+ }
19
+ print("};\n");