mittens 0.3.0 → 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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +2 -1
  4. data/README.md +2 -2
  5. data/Rakefile +12 -5
  6. data/ext/mittens/extconf.rb +3 -1
  7. data/lib/mittens/version.rb +1 -1
  8. data/vendor/snowball/.github/workflows/ci.yml +144 -17
  9. data/vendor/snowball/.github/workflows/coverage.yml +117 -0
  10. data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
  11. data/vendor/snowball/.gitignore +33 -7
  12. data/vendor/snowball/CONTRIBUTING.rst +7 -0
  13. data/vendor/snowball/COPYING +1 -1
  14. data/vendor/snowball/GNUmakefile +736 -298
  15. data/vendor/snowball/NEWS +1394 -7
  16. data/vendor/snowball/README.rst +8 -8
  17. data/vendor/snowball/ada/generate/generate.adb +5 -5
  18. data/vendor/snowball/ada/src/stemmer.adb +177 -188
  19. data/vendor/snowball/ada/src/stemmer.ads +86 -89
  20. data/vendor/snowball/ada/src/stemwords.adb +21 -5
  21. data/vendor/snowball/algorithms/czech.sbl +255 -0
  22. data/vendor/snowball/algorithms/danish.sbl +22 -10
  23. data/vendor/snowball/algorithms/english.sbl +7 -4
  24. data/vendor/snowball/algorithms/esperanto.sbl +6 -7
  25. data/vendor/snowball/algorithms/estonian.sbl +9 -4
  26. data/vendor/snowball/algorithms/finnish.sbl +33 -21
  27. data/vendor/snowball/algorithms/german.sbl +5 -0
  28. data/vendor/snowball/algorithms/indonesian.sbl +115 -96
  29. data/vendor/snowball/algorithms/italian.sbl +26 -1
  30. data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
  31. data/vendor/snowball/algorithms/norwegian.sbl +17 -6
  32. data/vendor/snowball/algorithms/persian.sbl +387 -0
  33. data/vendor/snowball/algorithms/polish.sbl +177 -0
  34. data/vendor/snowball/algorithms/sesotho.sbl +115 -0
  35. data/vendor/snowball/algorithms/turkish.sbl +4 -4
  36. data/vendor/snowball/compiler/analyser.c +2002 -711
  37. data/vendor/snowball/compiler/driver.c +460 -298
  38. data/vendor/snowball/compiler/generator.c +439 -1965
  39. data/vendor/snowball/compiler/generator_ada.c +605 -430
  40. data/vendor/snowball/compiler/generator_c.c +2227 -0
  41. data/vendor/snowball/compiler/generator_csharp.c +365 -274
  42. data/vendor/snowball/compiler/generator_dart.c +1476 -0
  43. data/vendor/snowball/compiler/generator_go.c +348 -270
  44. data/vendor/snowball/compiler/generator_java.c +330 -233
  45. data/vendor/snowball/compiler/generator_js.c +534 -385
  46. data/vendor/snowball/compiler/generator_pascal.c +361 -320
  47. data/vendor/snowball/compiler/generator_php.c +1445 -0
  48. data/vendor/snowball/compiler/generator_python.c +399 -299
  49. data/vendor/snowball/compiler/generator_rust.c +370 -261
  50. data/vendor/snowball/compiler/generator_zig.c +1474 -0
  51. data/vendor/snowball/compiler/header.h +153 -86
  52. data/vendor/snowball/compiler/space.c +121 -63
  53. data/vendor/snowball/compiler/tokeniser.c +286 -182
  54. data/vendor/snowball/compiler/tokens.h +71 -0
  55. data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
  56. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
  57. data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
  58. data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
  59. data/vendor/snowball/cxx/.gitignore +4 -0
  60. data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
  61. data/vendor/snowball/cxx/stemmer.h +12 -0
  62. data/vendor/snowball/cxx/stemwords.cxx +176 -0
  63. data/vendor/snowball/cxx/utilities.cxx +2 -0
  64. data/vendor/snowball/dart/.gitignore +4 -0
  65. data/vendor/snowball/dart/analysis_options.yaml +1 -0
  66. data/vendor/snowball/dart/example/test_app.dart +65 -0
  67. data/vendor/snowball/dart/generate_algorithms.pl +21 -0
  68. data/vendor/snowball/dart/lib/snowball.dart +18 -0
  69. data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
  70. data/vendor/snowball/dart/pubspec.yaml +17 -0
  71. data/vendor/snowball/doc/libstemmer_dart_README +37 -0
  72. data/vendor/snowball/doc/libstemmer_js_README +2 -2
  73. data/vendor/snowball/doc/libstemmer_php_README +38 -0
  74. data/vendor/snowball/doc/libstemmer_python_README +3 -3
  75. data/vendor/snowball/examples/stemwords.c +18 -3
  76. data/vendor/snowball/go/README.md +12 -4
  77. data/vendor/snowball/go/env.go +12 -14
  78. data/vendor/snowball/go/stemwords/main.go +2 -1
  79. data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
  80. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
  81. data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
  82. data/vendor/snowball/javascript/base-stemmer.js +219 -252
  83. data/vendor/snowball/javascript/stemwords.js +86 -58
  84. data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
  85. data/vendor/snowball/libstemmer/modules.txt +4 -0
  86. data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
  87. data/vendor/snowball/php/base-stemmer.php +453 -0
  88. data/vendor/snowball/php/stemwords.php +25 -0
  89. data/vendor/snowball/python/create_init.py +16 -21
  90. data/vendor/snowball/python/pyproject.toml +3 -0
  91. data/vendor/snowball/python/setup.py +5 -7
  92. data/vendor/snowball/python/snowballstemmer/among.py +1 -2
  93. data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
  94. data/vendor/snowball/python/stemwords.py +72 -63
  95. data/vendor/snowball/runtime/api.c +11 -42
  96. data/vendor/snowball/runtime/api.h +11 -9
  97. data/vendor/snowball/runtime/snowball_runtime.h +110 -0
  98. data/vendor/snowball/runtime/utilities.c +282 -106
  99. data/vendor/snowball/rust/src/main.rs +2 -2
  100. data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
  101. data/vendor/snowball/tests/compilertest +62 -0
  102. data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
  103. data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
  104. data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
  105. data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
  106. data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
  107. data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
  108. data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
  109. data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
  110. data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
  111. data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
  112. data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
  113. data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
  114. data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
  115. data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
  116. data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
  117. data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
  118. data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
  119. data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
  120. data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
  121. data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
  122. data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
  123. data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
  124. data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
  125. data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
  126. data/vendor/snowball/tests/runtime/among.sbl +19 -0
  127. data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
  128. data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
  129. data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
  130. data/vendor/snowball/tests/runtime/externals.sbl +22 -0
  131. data/vendor/snowball/tests/runtime/hop.sbl +12 -0
  132. data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
  133. data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
  134. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  135. data/vendor/snowball/tests/runtime/not.sbl +30 -0
  136. data/vendor/snowball/tests/runtime/or.sbl +17 -0
  137. data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
  138. data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
  139. data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
  140. data/vendor/snowball/tests/runtime/slice.sbl +27 -0
  141. data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
  142. data/vendor/snowball/tests/runtime/strings.sbl +66 -0
  143. data/vendor/snowball/tests/runtime/test.sbl +19 -0
  144. data/vendor/snowball/tests/stemtest.c +41 -1
  145. data/vendor/snowball/tests/syntax/canon.sbl +32 -0
  146. data/vendor/snowball/tests/syntax/canon.stderr +0 -0
  147. data/vendor/snowball/tests/syntax/canon.syntax +57 -0
  148. data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
  149. data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
  150. data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
  151. data/vendor/snowball/tests/syntax/inline.sbl +144 -0
  152. data/vendor/snowball/tests/syntax/inline.stderr +0 -0
  153. data/vendor/snowball/tests/syntax/inline.syntax +121 -0
  154. data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
  155. data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
  156. data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
  157. data/vendor/snowball/tests/syntax/loops.sbl +14 -0
  158. data/vendor/snowball/tests/syntax/loops.stderr +9 -0
  159. data/vendor/snowball/tests/syntax/loops.syntax +29 -0
  160. data/vendor/snowball/tests/syntax/noops.sbl +76 -0
  161. data/vendor/snowball/tests/syntax/noops.stderr +27 -0
  162. data/vendor/snowball/tests/syntax/noops.syntax +135 -0
  163. data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
  164. data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
  165. data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
  166. data/vendor/snowball/tests/syntax/unused.sbl +18 -0
  167. data/vendor/snowball/tests/syntax/unused.stderr +7 -0
  168. data/vendor/snowball/tests/syntax/unused.syntax +3 -0
  169. data/vendor/snowball/zig/env.zig +599 -0
  170. data/vendor/snowball/zig/generate_algorithms.pl +19 -0
  171. data/vendor/snowball/zig/stemwords.zig +123 -0
  172. metadata +102 -4
  173. data/vendor/snowball/compiler/syswords.h +0 -86
  174. data/vendor/snowball/runtime/header.h +0 -62
@@ -0,0 +1,453 @@
1
+ <?php
2
+ abstract class SnowballStemmer {
3
+
4
+ protected string $current = '';
5
+ protected int $cursor = 0;
6
+ protected int $limit = 0;
7
+ protected int $limit_backward = 0;
8
+ protected int $bra = 0;
9
+ protected int $ket = 0;
10
+
11
+
12
+ abstract public function stem(): bool;
13
+
14
+
15
+ protected function copyFrom(self $other): void {
16
+ $this->current = $other->current;
17
+ $this->cursor = $other->cursor;
18
+ $this->limit = $other->limit;
19
+ $this->limit_backward = $other->limit_backward;
20
+ $this->bra = $other->bra;
21
+ $this->ket = $other->ket;
22
+ }
23
+
24
+
25
+ /**
26
+ * @param int[] $s
27
+ */
28
+ protected function in_grouping(array $s): bool {
29
+ if ($this->cursor >= $this->limit) {
30
+ return false;
31
+ }
32
+ $ch = $this->charAt();
33
+ if (!array_key_exists($ch, $s)) {
34
+ return false;
35
+ }
36
+ $this->cursor += strlen($ch);
37
+ return true;
38
+ }
39
+
40
+
41
+ /**
42
+ * @param int[] $s
43
+ */
44
+ protected function go_in_grouping(array $s): bool {
45
+ while ($this->cursor < $this->limit) {
46
+ $ch = $this->charAt();
47
+ if (!array_key_exists($ch, $s)) {
48
+ return true;
49
+ }
50
+ $this->cursor += strlen($ch);
51
+ }
52
+ return false;
53
+ }
54
+
55
+
56
+
57
+ /**
58
+ * @param int[] $s
59
+ */
60
+ protected function in_grouping_b(array $s): bool {
61
+ if ($this->cursor <= $this->limit_backward) {
62
+ return false;
63
+ }
64
+ $ch = $this->charBefore();
65
+ if (!array_key_exists($ch, $s)) {
66
+ return false;
67
+ }
68
+ $this->cursor -= strlen($ch);
69
+ return true;
70
+ }
71
+
72
+
73
+ /**
74
+ * @param int[] $s
75
+ */
76
+ protected function go_in_grouping_b(array $s): bool {
77
+ while ($this->cursor > $this->limit_backward) {
78
+ $ch = $this->charBefore();
79
+ if (!array_key_exists($ch, $s)) {
80
+ return true;
81
+ }
82
+ $this->cursor -= strlen($ch);
83
+ }
84
+ return false;
85
+ }
86
+
87
+
88
+ /**
89
+ * @param int[] $s
90
+ */
91
+ protected function out_grouping(array $s): bool {
92
+ if ($this->cursor >= $this->limit) {
93
+ return false;
94
+ }
95
+ $ch = $this->charAt();
96
+ if (!array_key_exists($ch, $s)) {
97
+ $this->cursor += strlen($ch);
98
+ return true;
99
+ }
100
+ return false;
101
+ }
102
+
103
+
104
+ /**
105
+ * @param int[] $s
106
+ */
107
+ protected function go_out_grouping(array $s): bool {
108
+ while ($this->cursor < $this->limit) {
109
+ $ch = $this->charAt();
110
+ if (array_key_exists($ch, $s)) {
111
+ return true;
112
+ }
113
+ $this->cursor += strlen($ch);
114
+ }
115
+ return false;
116
+ }
117
+
118
+
119
+ /**
120
+ * @param int[] $s
121
+ */
122
+ protected function out_grouping_b(array $s): bool {
123
+ if ($this->cursor <= $this->limit_backward) {
124
+ return false;
125
+ }
126
+ $ch = $this->charBefore();
127
+ if (!array_key_exists($ch, $s)) {
128
+ $this->cursor -= strlen($ch);
129
+ return true;
130
+ }
131
+ return false;
132
+ }
133
+
134
+
135
+ /**
136
+ * @param int[] $s
137
+ */
138
+ protected function go_out_grouping_b(array $s): bool {
139
+ while ($this->cursor > $this->limit_backward) {
140
+ $ch = $this->charBefore();
141
+ if (array_key_exists($ch, $s)) {
142
+ return true;
143
+ }
144
+ $this->cursor -= strlen($ch);
145
+ }
146
+ return false;
147
+ }
148
+
149
+
150
+ protected function eq_s(string $s): bool {
151
+ $slength = strlen($s);
152
+ if ($this->limit - $this->cursor < $slength) {
153
+ return false;
154
+ }
155
+ if (substr_compare($this->current, $s, $this->cursor, $slength) != 0) {
156
+ return false;
157
+ }
158
+ $this->cursor += $slength;
159
+ return true;
160
+ }
161
+
162
+
163
+ protected function eq_s_b(string $s): bool {
164
+ $slength = strlen($s);
165
+ if ($this->cursor - $this->limit_backward < $slength) {
166
+ return false;
167
+ }
168
+ if (substr_compare($this->current, $s, $this->cursor - $slength, $slength) != 0) {
169
+ return false;
170
+ }
171
+ $this->cursor -= $slength;
172
+ return true;
173
+ }
174
+
175
+
176
+ /**
177
+ * @param array[] $v
178
+ */
179
+ protected function find_among(array $v): int {
180
+ $i = 0;
181
+ $j = count($v);
182
+
183
+ $c = $this->cursor;
184
+ $l = $this->limit;
185
+
186
+ $common_i = 0;
187
+ $common_j = 0;
188
+
189
+ $first_key_inspected = false;
190
+
191
+ while (true) {
192
+ $k = $i + (($j-$i) >> 1);
193
+ $diff = 0;
194
+ $common = min($common_i, $common_j); // smaller
195
+ // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
196
+ $w = $v[$k];
197
+ $w0length = strlen($w[0]);
198
+ for ($i2 = $common; $i2 < $w0length; $i2++) {
199
+ if ($c + $common === $l) {
200
+ $diff = -1;
201
+ break;
202
+ }
203
+ $diff = strcmp($this->current[$c+$common], $w[0][$i2]);
204
+ if ($diff !== 0) {
205
+ break;
206
+ }
207
+ $common++;
208
+ }
209
+ if ($diff < 0) {
210
+ $j = $k;
211
+ $common_j = $common;
212
+ }
213
+ else {
214
+ $i = $k;
215
+ $common_i = $common;
216
+ }
217
+ if ($j - $i <= 1) {
218
+ if ($i > 0) {
219
+ break;
220
+ }
221
+ // v->s has been inspected
222
+ if ($j === $i) {
223
+ break;
224
+ }
225
+ // only one item in v
226
+
227
+ // - but now we need to go round once more to get
228
+ // v->s inspected. This looks messy, but is actually
229
+ // the optimal approach.
230
+
231
+ if ($first_key_inspected) {
232
+ break;
233
+ }
234
+ $first_key_inspected = true;
235
+ }
236
+ }
237
+ do {
238
+ $w = $v[$i];
239
+ $w0length = strlen($w[0]);
240
+ if ($common_i >= $w0length) {
241
+ $this->cursor = $c + $w0length;
242
+ if (count($w) < 4) {
243
+ return $w[2];
244
+ }
245
+ $res = $this->{$w[3]}();
246
+ $this->cursor = $c + $w0length;
247
+ if ($res) {
248
+ return $w[2];
249
+ }
250
+ }
251
+ $i = $w[1];
252
+ } while ($i >= 0);
253
+ return 0;
254
+ }
255
+
256
+
257
+ /**
258
+ * find_among_b is for backwards processing. Same comments apply
259
+ */
260
+ protected function find_among_b(array $v): int {
261
+ $i = 0;
262
+ $j = count($v);
263
+
264
+ $c = $this->cursor;
265
+ $lb = $this->limit_backward;
266
+
267
+ $common_i = 0;
268
+ $common_j = 0;
269
+
270
+ $first_key_inspected = false;
271
+
272
+ while (true) {
273
+ $k = $i + (($j-$i) >> 1);
274
+ $diff = 0;
275
+ $common = min($common_i, $common_j);
276
+ $w = $v[$k];
277
+ $w0length = strlen($w[0]);
278
+ for ($i2 = $w0length - 1 - $common; $i2 >= 0; $i2--) {
279
+ if ($c - $common == $lb) {
280
+ $diff = -1;
281
+ break;
282
+ }
283
+ $diff = strcmp($this->current[$c - 1 - $common], $w[0][$i2]);
284
+ if ($diff != 0) {
285
+ break;
286
+ }
287
+ $common++;
288
+ }
289
+ if ($diff < 0) {
290
+ $j = $k;
291
+ $common_j = $common;
292
+ }
293
+ else {
294
+ $i = $k;
295
+ $common_i = $common;
296
+ }
297
+ if ($j - $i <= 1) {
298
+ if ($i > 0 || $j === $i || $first_key_inspected) {
299
+ break;
300
+ }
301
+ $first_key_inspected = true;
302
+ }
303
+ }
304
+ do {
305
+ $w = $v[$i];
306
+ $w0length = strlen($w[0]);
307
+ if ($common_i >= $w0length) {
308
+ $this->cursor = $c - $w0length;
309
+ if (count($w) < 4) {
310
+ return $w[2];
311
+ }
312
+ $res = $this->{$w[3]}();
313
+ $this->cursor = $c - $w0length;
314
+ if ($res) {
315
+ return $w[2];
316
+ }
317
+ }
318
+ $i = $w[1];
319
+ } while ($i >= 0);
320
+ return 0;
321
+ }
322
+
323
+
324
+ /**
325
+ * to replace chars between $c_bra and $c_ket in $this->current by the chars in $s.
326
+ */
327
+ private function replace_s(int $c_bra, int $c_ket, string $s): int {
328
+ $slength = strlen($s);
329
+ $adjustment = $slength - ($c_ket - $c_bra);
330
+ $this->current = substr_replace($this->current, $s, $c_bra, $c_ket - $c_bra);
331
+ $this->limit += $adjustment;
332
+ if ($this->cursor >= $c_ket) {
333
+ $this->cursor += $adjustment;
334
+ } elseif ($this->cursor > $c_bra) {
335
+ $this->cursor = $c_bra;
336
+ }
337
+ return $adjustment;
338
+ }
339
+
340
+
341
+ private function slice_check(): void {
342
+ if (
343
+ $this->bra < 0 ||
344
+ $this->bra > $this->ket ||
345
+ $this->ket > $this->limit ||
346
+ $this->limit > strlen($this->current)
347
+ ) {
348
+ throw new LogicException('Faulty slice operation');
349
+ }
350
+ }
351
+
352
+
353
+ protected function slice_from(string $s): void {
354
+ $this->slice_check();
355
+ $this->replace_s($this->bra, $this->ket, $s);
356
+ $this->ket = $this->bra + strlen($s);
357
+ }
358
+
359
+
360
+ protected function slice_del(): void {
361
+ $this->slice_from('');
362
+ }
363
+
364
+
365
+ protected function insert(int $c_bra, int $c_ket, string $s): void {
366
+ $adjustment = $this->replace_s($c_bra, $c_ket, $s);
367
+ $c_bra <= $this->bra and $this->bra += $adjustment;
368
+ $c_bra <= $this->ket and $this->ket += $adjustment;
369
+ }
370
+
371
+
372
+ protected function slice_to(): string {
373
+ $this->slice_check();
374
+ return substr($this->current, $this->bra, $this->ket - $this->bra);
375
+ }
376
+
377
+ private function charAt(): string {
378
+ $s = $this->current[$this->cursor];
379
+ $c = ord($s);
380
+ if ($c < 0xc0) return $s;
381
+ if ($c < 0xe0) return substr($this->current, $this->cursor, 2);
382
+ if ($c < 0xf0) return substr($this->current, $this->cursor, 3);
383
+ return substr($this->current, $this->cursor, 4);
384
+ }
385
+
386
+ private function charBefore(): string {
387
+ $s = $this->current[$this->cursor - 1];
388
+ if (ord($s) < 0x80) return $s;
389
+ $o = $this->cursor - 1;
390
+ while (--$o && ord($this->current[$o]) < 0xc0) { }
391
+ return substr($this->current, $o, $this->cursor - $o);
392
+ }
393
+
394
+ protected function inc_cursor(): void {
395
+ do ++$this->cursor; while ($this->cursor < $this->limit && (ord($this->current[$this->cursor]) & 0xc0) == 0x80);
396
+ }
397
+
398
+ protected function dec_cursor(): void {
399
+ do --$this->cursor; while ($this->cursor > $this->limit_backward && (ord($this->current[$this->cursor]) & 0xc0) == 0x80);
400
+ }
401
+
402
+ protected function hop(int $delta): bool {
403
+ $res = $this->cursor;
404
+ while ($delta > 0) {
405
+ $delta--;
406
+ if ($res >= $this->limit) {
407
+ return false;
408
+ }
409
+ do {
410
+ $res++;
411
+ } while ($res < $this->limit && (ord($this->current[$res]) & 0xc0) == 0x80);
412
+ }
413
+ $this->cursor = $res;
414
+ return true;
415
+ }
416
+
417
+ protected function hop_checked(int $delta): bool {
418
+ return $delta >= 0 && $this->hop($delta);
419
+ }
420
+
421
+ protected function hop_back(int $delta): bool {
422
+ $res = $this->cursor;
423
+ while ($delta > 0) {
424
+ $delta--;
425
+ if ($res <= $this->limit_backward) {
426
+ return false;
427
+ }
428
+ do {
429
+ $res--;
430
+ } while ($res > $this->limit_backward && (ord($this->current[$res]) & 0xc0) == 0x80);
431
+ }
432
+ $this->cursor = $res;
433
+ return true;
434
+ }
435
+
436
+ protected function hop_back_checked(int $delta): bool {
437
+ return $delta >= 0 && $this->hop_back($delta);
438
+ }
439
+
440
+ /**
441
+ * Public entry point for stemming a word
442
+ */
443
+ public function stemWord(string $word): string {
444
+ $this->current = $word;
445
+ $this->cursor = 0;
446
+ $this->limit = strlen($word);
447
+ $this->limit_backward = 0;
448
+ $this->bra = $this->cursor;
449
+ $this->ket = $this->limit;
450
+ $this->stem();
451
+ return $this->current;
452
+ }
453
+ }
@@ -0,0 +1,25 @@
1
+ <?php
2
+
3
+ $lang = $argv[1] ?? 'english';
4
+ $parent = realpath(__DIR__.'/..');
5
+ $phpfile = $parent.'/php_out/'.$lang.'-stemmer.php';
6
+ if (!file_exists($phpfile)) {
7
+ fwrite(STDERR, "PHP stemmer not found at $phpfile\n");
8
+ exit(1);
9
+ }
10
+
11
+ require __DIR__.'/base-stemmer.php';
12
+ require $phpfile;
13
+
14
+ $class = 'Snowball'.implode('', array_map('ucfirst', explode('_', $lang))).'Stemmer';
15
+ if (!class_exists($class)) {
16
+ fwrite(STDERR, "$class not included from $phpfile\n");
17
+ exit(1);
18
+ }
19
+ $stemmer = new $class;
20
+
21
+ while ($word = fgets(STDIN)) {
22
+ $word = strtolower(rtrim($word, "\n"));
23
+ $stem = $stemmer->stemWord($word);
24
+ echo $stem, "\n";
25
+ }
@@ -16,8 +16,8 @@ for pyscript in os.listdir(python_out_folder):
16
16
  if (match):
17
17
  langname = match.group(1)
18
18
  titlecase = re.sub(r"_", "", langname.title())
19
- languages.append(" '%(lang)s': %(title)sStemmer," % {'lang': langname, 'title': titlecase})
20
- imports.append('from .%(lang)s_stemmer import %(title)sStemmer' % {'lang': langname, 'title': titlecase})
19
+ languages.append(" '%(lang)s': %(title)sStemmer," % {'lang': langname, 'title': titlecase})
20
+ imports.append(' from .%(lang)s_stemmer import %(title)sStemmer' % {'lang': langname, 'title': titlecase})
21
21
  imports.sort()
22
22
  languages.sort()
23
23
 
@@ -26,31 +26,26 @@ if len(languages) == 0:
26
26
 
27
27
  src = '''__all__ = ('language', 'stemmer')
28
28
 
29
- %(imports)s
30
-
31
- _languages = {
32
- %(languages)s
33
- }
34
-
35
29
  try:
36
30
  import Stemmer
37
- cext_available = True
31
+ algorithms = Stemmer.algorithms
32
+ stemmer = Stemmer.Stemmer
38
33
  except ImportError:
39
- cext_available = False
34
+ %(imports)s
35
+
36
+ _languages = {
37
+ %(languages)s
38
+ }
40
39
 
41
- def algorithms():
42
- if cext_available:
43
- return Stemmer.language()
44
- else:
40
+ def algorithms():
45
41
  return list(_languages.keys())
46
42
 
47
- def stemmer(lang):
48
- if cext_available:
49
- return Stemmer.Stemmer(lang)
50
- if lang.lower() in _languages:
51
- return _languages[lang.lower()]()
52
- else:
53
- raise KeyError("Stemming algorithm '%%s' not found" %% lang)
43
+ def stemmer(lang):
44
+ lang = lang.lower()
45
+ if lang in _languages:
46
+ return _languages[lang]()
47
+ else:
48
+ raise KeyError("Stemming algorithm '%%s' not found" %% lang)
54
49
  ''' % {'imports': '\n'.join(imports), 'languages': '\n'.join(languages)}
55
50
 
56
51
  with open(os.path.join(python_out_folder, '__init__.py'), 'w') as out:
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
@@ -3,7 +3,7 @@
3
3
  from setuptools import setup
4
4
  import re
5
5
 
6
- SNOWBALL_VERSION = '3.0.1'
6
+ SNOWBALL_VERSION = '3.1.1'
7
7
 
8
8
  n_stemmers = 0
9
9
 
@@ -36,21 +36,18 @@ desc = 'This package provides ' + str(n_stemmers) + ' stemmers for ' + \
36
36
  classifiers = [
37
37
  'Development Status :: 5 - Production/Stable',
38
38
  'Intended Audience :: Developers',
39
- 'License :: OSI Approved :: BSD License'
40
39
  ]
41
40
 
42
41
  for lang in langs:
43
42
  lang_titlecase = lang.title()
44
43
  # Only classifiers listed in https://pypi.org/classifiers/ are allowed
45
44
  # Remove them here or submit them to https://github.com/pypa/trove-classifiers
46
- classifiers.append('Natural Language :: ' + lang_titlecase)
45
+ if lang_titlecase != 'Sesotho':
46
+ classifiers.append('Natural Language :: ' + lang_titlecase)
47
47
 
48
48
  classifiers.extend([
49
49
  'Operating System :: OS Independent',
50
50
  'Programming Language :: Python',
51
- 'Programming Language :: Python :: 2',
52
- 'Programming Language :: Python :: 2.6',
53
- 'Programming Language :: Python :: 2.7',
54
51
  'Programming Language :: Python :: 3',
55
52
  'Programming Language :: Python :: 3.3',
56
53
  'Programming Language :: Python :: 3.4',
@@ -63,6 +60,7 @@ classifiers.extend([
63
60
  'Programming Language :: Python :: 3.11',
64
61
  'Programming Language :: Python :: 3.12',
65
62
  'Programming Language :: Python :: 3.13',
63
+ 'Programming Language :: Python :: 3.14',
66
64
  'Programming Language :: Python :: Implementation :: CPython',
67
65
  'Programming Language :: Python :: Implementation :: PyPy',
68
66
  'Topic :: Database',
@@ -81,6 +79,6 @@ setup(name='snowballstemmer',
81
79
  license="BSD-3-Clause",
82
80
  packages=['snowballstemmer'],
83
81
  package_dir={"snowballstemmer": "src/snowballstemmer"},
84
- python_requires='!=3.0.*, !=3.1.*, !=3.2.*',
82
+ python_requires='>=3.3',
85
83
  classifiers = classifiers
86
84
  )
@@ -1,5 +1,4 @@
1
-
2
- class Among(object):
1
+ class Among:
3
2
  def __init__(self, s, substring_i, result, method=None):
4
3
  """
5
4
  @ivar s search string
@@ -1,4 +1,4 @@
1
- class BaseStemmer(object):
1
+ class BaseStemmer:
2
2
  def __init__(self):
3
3
  self.set_current("")
4
4
 
@@ -88,20 +88,16 @@ class BaseStemmer(object):
88
88
  return False
89
89
 
90
90
  def eq_s(self, s):
91
- if self.limit - self.cursor < len(s):
92
- return False
93
- if self.current[self.cursor:self.cursor + len(s)] != s:
94
- return False
95
- self.cursor += len(s)
96
- return True
91
+ if self.current.startswith(s, self.cursor, self.limit):
92
+ self.cursor += len(s)
93
+ return True
94
+ return False
97
95
 
98
96
  def eq_s_b(self, s):
99
- if self.cursor - self.limit_backward < len(s):
100
- return False
101
- if self.current[self.cursor - len(s):self.cursor] != s:
102
- return False
103
- self.cursor -= len(s)
104
- return True
97
+ if self.current.endswith(s, self.limit_backward, self.cursor):
98
+ self.cursor -= len(s)
99
+ return True
100
+ return False
105
101
 
106
102
  def find_among(self, v):
107
103
  i = 0
@@ -151,9 +147,8 @@ class BaseStemmer(object):
151
147
  self.cursor = c + len(w.s)
152
148
  if w.method is None:
153
149
  return w.result
154
- res = w.method(self)
155
- self.cursor = c + len(w.s)
156
- if res:
150
+ if w.method(self):
151
+ self.cursor = c + len(w.s)
157
152
  return w.result
158
153
  i = w.substring_i
159
154
  if i < 0:
@@ -208,9 +203,8 @@ class BaseStemmer(object):
208
203
  self.cursor = c - len(w.s)
209
204
  if w.method is None:
210
205
  return w.result
211
- res = w.method(self)
212
- self.cursor = c - len(w.s)
213
- if res:
206
+ if w.method(self):
207
+ self.cursor = c - len(w.s)
214
208
  return w.result
215
209
  i = w.substring_i
216
210
  if i < 0:
@@ -235,20 +229,16 @@ class BaseStemmer(object):
235
229
  self.cursor = c_bra
236
230
  return adjustment
237
231
 
238
- def slice_check(self):
239
- if self.bra < 0 or self.bra > self.ket or self.ket > self.limit or self.limit > len(self.current):
240
- return False
241
- return True
242
-
243
232
  def slice_from(self, s):
244
233
  '''
245
234
  @type s string
246
235
  '''
247
- result = False
248
- if self.slice_check():
249
- self.replace_s(self.bra, self.ket, s)
250
- result = True
251
- return result
236
+ assert self.bra >= 0
237
+ assert self.bra <= self.ket
238
+ assert self.ket <= self.limit
239
+ assert self.limit <= len(self.current)
240
+ self.replace_s(self.bra, self.ket, s)
241
+ self.ket = self.bra + len(s)
252
242
 
253
243
  def slice_del(self):
254
244
  return self.slice_from("")
@@ -269,10 +259,11 @@ class BaseStemmer(object):
269
259
  '''
270
260
  Return the slice as a string.
271
261
  '''
272
- result = ''
273
- if self.slice_check():
274
- result = self.current[self.bra:self.ket]
275
- return result
262
+ assert self.bra >= 0
263
+ assert self.bra <= self.ket
264
+ assert self.ket <= self.limit
265
+ assert self.limit <= len(self.current)
266
+ return self.current[self.bra:self.ket]
276
267
 
277
268
  def assign_to(self):
278
269
  '''