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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/README.md +2 -2
- data/Rakefile +10 -3
- data/lib/mittens/version.rb +1 -1
- data/vendor/snowball/.github/workflows/ci.yml +144 -17
- data/vendor/snowball/.github/workflows/coverage.yml +117 -0
- data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
- data/vendor/snowball/.gitignore +33 -7
- data/vendor/snowball/CONTRIBUTING.rst +7 -0
- data/vendor/snowball/COPYING +1 -1
- data/vendor/snowball/GNUmakefile +736 -298
- data/vendor/snowball/NEWS +1394 -7
- data/vendor/snowball/README.rst +8 -8
- data/vendor/snowball/ada/generate/generate.adb +5 -5
- data/vendor/snowball/ada/src/stemmer.adb +177 -188
- data/vendor/snowball/ada/src/stemmer.ads +86 -89
- data/vendor/snowball/ada/src/stemwords.adb +21 -5
- data/vendor/snowball/algorithms/czech.sbl +255 -0
- data/vendor/snowball/algorithms/danish.sbl +22 -10
- data/vendor/snowball/algorithms/english.sbl +7 -4
- data/vendor/snowball/algorithms/esperanto.sbl +6 -7
- data/vendor/snowball/algorithms/estonian.sbl +9 -4
- data/vendor/snowball/algorithms/finnish.sbl +33 -21
- data/vendor/snowball/algorithms/german.sbl +5 -0
- data/vendor/snowball/algorithms/indonesian.sbl +115 -96
- data/vendor/snowball/algorithms/italian.sbl +26 -1
- data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
- data/vendor/snowball/algorithms/norwegian.sbl +17 -6
- data/vendor/snowball/algorithms/persian.sbl +387 -0
- data/vendor/snowball/algorithms/polish.sbl +177 -0
- data/vendor/snowball/algorithms/sesotho.sbl +115 -0
- data/vendor/snowball/algorithms/turkish.sbl +4 -4
- data/vendor/snowball/compiler/analyser.c +2002 -711
- data/vendor/snowball/compiler/driver.c +460 -298
- data/vendor/snowball/compiler/generator.c +439 -1965
- data/vendor/snowball/compiler/generator_ada.c +605 -430
- data/vendor/snowball/compiler/generator_c.c +2227 -0
- data/vendor/snowball/compiler/generator_csharp.c +365 -274
- data/vendor/snowball/compiler/generator_dart.c +1476 -0
- data/vendor/snowball/compiler/generator_go.c +348 -270
- data/vendor/snowball/compiler/generator_java.c +330 -233
- data/vendor/snowball/compiler/generator_js.c +534 -385
- data/vendor/snowball/compiler/generator_pascal.c +361 -320
- data/vendor/snowball/compiler/generator_php.c +1445 -0
- data/vendor/snowball/compiler/generator_python.c +399 -299
- data/vendor/snowball/compiler/generator_rust.c +370 -261
- data/vendor/snowball/compiler/generator_zig.c +1474 -0
- data/vendor/snowball/compiler/header.h +153 -86
- data/vendor/snowball/compiler/space.c +121 -63
- data/vendor/snowball/compiler/tokeniser.c +286 -182
- data/vendor/snowball/compiler/tokens.h +71 -0
- data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
- data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
- data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
- data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
- data/vendor/snowball/cxx/.gitignore +4 -0
- data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
- data/vendor/snowball/cxx/stemmer.h +12 -0
- data/vendor/snowball/cxx/stemwords.cxx +176 -0
- data/vendor/snowball/cxx/utilities.cxx +2 -0
- data/vendor/snowball/dart/.gitignore +4 -0
- data/vendor/snowball/dart/analysis_options.yaml +1 -0
- data/vendor/snowball/dart/example/test_app.dart +65 -0
- data/vendor/snowball/dart/generate_algorithms.pl +21 -0
- data/vendor/snowball/dart/lib/snowball.dart +18 -0
- data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
- data/vendor/snowball/dart/pubspec.yaml +17 -0
- data/vendor/snowball/doc/libstemmer_dart_README +37 -0
- data/vendor/snowball/doc/libstemmer_js_README +2 -2
- data/vendor/snowball/doc/libstemmer_php_README +38 -0
- data/vendor/snowball/doc/libstemmer_python_README +3 -3
- data/vendor/snowball/examples/stemwords.c +18 -3
- data/vendor/snowball/go/README.md +12 -4
- data/vendor/snowball/go/env.go +12 -14
- data/vendor/snowball/go/stemwords/main.go +2 -1
- data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
- data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
- data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
- data/vendor/snowball/javascript/base-stemmer.js +219 -252
- data/vendor/snowball/javascript/stemwords.js +86 -58
- data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
- data/vendor/snowball/libstemmer/modules.txt +4 -0
- data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
- data/vendor/snowball/php/base-stemmer.php +453 -0
- data/vendor/snowball/php/stemwords.php +25 -0
- data/vendor/snowball/python/create_init.py +16 -21
- data/vendor/snowball/python/pyproject.toml +3 -0
- data/vendor/snowball/python/setup.py +5 -7
- data/vendor/snowball/python/snowballstemmer/among.py +1 -2
- data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
- data/vendor/snowball/python/stemwords.py +72 -63
- data/vendor/snowball/runtime/api.c +11 -42
- data/vendor/snowball/runtime/api.h +11 -9
- data/vendor/snowball/runtime/snowball_runtime.h +110 -0
- data/vendor/snowball/runtime/utilities.c +282 -106
- data/vendor/snowball/rust/src/main.rs +2 -2
- data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
- data/vendor/snowball/tests/compilertest +62 -0
- data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
- data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
- data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
- data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
- data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
- data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
- data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
- data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
- data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
- data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
- data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
- data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
- data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
- data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
- data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
- data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
- data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
- data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
- data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
- data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
- data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
- data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
- data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
- data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
- data/vendor/snowball/tests/runtime/among.sbl +19 -0
- data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
- data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
- data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
- data/vendor/snowball/tests/runtime/externals.sbl +22 -0
- data/vendor/snowball/tests/runtime/hop.sbl +12 -0
- data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
- data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
- data/vendor/snowball/tests/runtime/naming.sbl +20 -0
- data/vendor/snowball/tests/runtime/not.sbl +30 -0
- data/vendor/snowball/tests/runtime/or.sbl +17 -0
- data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
- data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
- data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
- data/vendor/snowball/tests/runtime/slice.sbl +27 -0
- data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
- data/vendor/snowball/tests/runtime/strings.sbl +66 -0
- data/vendor/snowball/tests/runtime/test.sbl +19 -0
- data/vendor/snowball/tests/stemtest.c +41 -1
- data/vendor/snowball/tests/syntax/canon.sbl +32 -0
- data/vendor/snowball/tests/syntax/canon.stderr +0 -0
- data/vendor/snowball/tests/syntax/canon.syntax +57 -0
- data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
- data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
- data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
- data/vendor/snowball/tests/syntax/inline.sbl +144 -0
- data/vendor/snowball/tests/syntax/inline.stderr +0 -0
- data/vendor/snowball/tests/syntax/inline.syntax +121 -0
- data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
- data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
- data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
- data/vendor/snowball/tests/syntax/loops.sbl +14 -0
- data/vendor/snowball/tests/syntax/loops.stderr +9 -0
- data/vendor/snowball/tests/syntax/loops.syntax +29 -0
- data/vendor/snowball/tests/syntax/noops.sbl +76 -0
- data/vendor/snowball/tests/syntax/noops.stderr +27 -0
- data/vendor/snowball/tests/syntax/noops.syntax +135 -0
- data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
- data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
- data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
- data/vendor/snowball/tests/syntax/unused.sbl +18 -0
- data/vendor/snowball/tests/syntax/unused.stderr +7 -0
- data/vendor/snowball/tests/syntax/unused.syntax +3 -0
- data/vendor/snowball/zig/env.zig +599 -0
- data/vendor/snowball/zig/generate_algorithms.pl +19 -0
- data/vendor/snowball/zig/stemwords.zig +123 -0
- metadata +102 -4
- data/vendor/snowball/compiler/syswords.h +0 -86
- data/vendor/snowball/runtime/header.h +0 -62
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* -----------------------------------------------------------------------------
|
|
3
|
+
* Persian Stemmer (HPS-like)
|
|
4
|
+
* -----------------------------------------------------------------------------
|
|
5
|
+
* Based on the paper:
|
|
6
|
+
* "HPS: A Hierarchical Persian Stemming Method"
|
|
7
|
+
* Ayshe Rashidi, Mina Zolfy Lighvan (2014)
|
|
8
|
+
* -----------------------------------------------------------------------------
|
|
9
|
+
* Differences from original HPS:
|
|
10
|
+
* 1) No POS-tagger stage:
|
|
11
|
+
* - HPS uses a POS tagger to route tokens to Noun, Adjective, or Verb
|
|
12
|
+
* suffix rules directly.
|
|
13
|
+
* - Here we fall back to the fixed order: Noun or Adjective, then Verb
|
|
14
|
+
* when POS is unknown.
|
|
15
|
+
* Reason: Implementation is tagger-agnostic and lightweight.
|
|
16
|
+
* 2) Lexical "-AN" guard:
|
|
17
|
+
* - Added `Protect_Lexical_AN` to avoid stripping true lexical endings
|
|
18
|
+
* like "...stan", "...ran", and known stems (Iran, Tehran, Ensan).
|
|
19
|
+
* Reason: reduces over-stemming where HPS relied on explicit hash lists.
|
|
20
|
+
* -----------------------------------------------------------------------------
|
|
21
|
+
* Implemented by: https://saeiddrv.com
|
|
22
|
+
* -----------------------------------------------------------------------------
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
stringescapes { }
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Alphabet & special symbols
|
|
29
|
+
// ============================================================================
|
|
30
|
+
stringdef alef '{U+0627}'
|
|
31
|
+
stringdef aa '{U+0622}'
|
|
32
|
+
stringdef be '{U+0628}'
|
|
33
|
+
stringdef pe '{U+067E}'
|
|
34
|
+
stringdef te '{U+062A}'
|
|
35
|
+
stringdef se '{U+062B}'
|
|
36
|
+
stringdef jim '{U+062C}'
|
|
37
|
+
stringdef che '{U+0686}'
|
|
38
|
+
stringdef heh_jimi '{U+062D}'
|
|
39
|
+
stringdef khe '{U+062E}'
|
|
40
|
+
stringdef dal '{U+062F}'
|
|
41
|
+
stringdef zal '{U+0630}'
|
|
42
|
+
stringdef re '{U+0631}'
|
|
43
|
+
stringdef ze '{U+0632}'
|
|
44
|
+
stringdef zhe '{U+0698}'
|
|
45
|
+
stringdef sin '{U+0633}'
|
|
46
|
+
stringdef shin '{U+0634}'
|
|
47
|
+
stringdef sad '{U+0635}'
|
|
48
|
+
stringdef zad '{U+0636}'
|
|
49
|
+
stringdef ta '{U+0637}'
|
|
50
|
+
stringdef za '{U+0638}'
|
|
51
|
+
stringdef ain '{U+0639}'
|
|
52
|
+
stringdef ghain '{U+063A}'
|
|
53
|
+
stringdef fe '{U+0641}'
|
|
54
|
+
stringdef ghaf '{U+0642}'
|
|
55
|
+
stringdef kaf '{U+06A9}'
|
|
56
|
+
stringdef gaf '{U+06AF}'
|
|
57
|
+
stringdef lam '{U+0644}'
|
|
58
|
+
stringdef mim '{U+0645}'
|
|
59
|
+
stringdef nun '{U+0646}'
|
|
60
|
+
stringdef vav '{U+0648}'
|
|
61
|
+
stringdef heh '{U+0647}'
|
|
62
|
+
stringdef ye '{U+06CC}'
|
|
63
|
+
|
|
64
|
+
stringdef space '{U+0020}'
|
|
65
|
+
stringdef zero_width_joiner '{U+200D}'
|
|
66
|
+
stringdef half_space '{U+200C}' // zero-width non-joiner (ZWNJ)
|
|
67
|
+
// Note: U+200C (ZERO WIDTH NON-JOINER) is commonly referred to as "nim-faseleh" ("half-space") in Persian.
|
|
68
|
+
// Although this name is not standard in English Unicode terminology, it reflects its widespread use
|
|
69
|
+
// in Persian as a morpheme separator within words. This character is here to normalize
|
|
70
|
+
|
|
71
|
+
// Arabic variants to normalize to Persian forms
|
|
72
|
+
stringdef ar_kaf '{U+0643}'
|
|
73
|
+
stringdef ar_ye '{U+064A}'
|
|
74
|
+
stringdef ar_ye_with_hamza_above '{U+0626}'
|
|
75
|
+
stringdef ar_heh '{U+06C1}'
|
|
76
|
+
stringdef ar_teh_marbuta '{U+0629}'
|
|
77
|
+
stringdef ar_alef_with_hamza_above '{U+0623}'
|
|
78
|
+
stringdef ar_alef_with_hamza_below '{U+0625}'
|
|
79
|
+
stringdef ar_vav_with_hamza_above '{U+0624}'
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Declarations (routines, flags, and marks)
|
|
83
|
+
// ============================================================================
|
|
84
|
+
routines (
|
|
85
|
+
Normalize_Characters // Forward: Unicode/script normalization
|
|
86
|
+
Prefixes // Forward: handles prefixes
|
|
87
|
+
Delete_ZWNJ // Forward: delete remaining ZWNJs after prefix handling
|
|
88
|
+
Protect_Lexical_AN // Probe: flag lexical -AN endings (per pass)
|
|
89
|
+
|
|
90
|
+
R1 // Test if cursor is in R1
|
|
91
|
+
|
|
92
|
+
// Noun tier (HPS categories)
|
|
93
|
+
AN_Exception
|
|
94
|
+
Irregular_Noun
|
|
95
|
+
Stem_Noun_or_Adjective
|
|
96
|
+
Stem_Verb
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
externals ( stem )
|
|
100
|
+
|
|
101
|
+
// Guard flags:
|
|
102
|
+
// - 'saw_present_prefix':
|
|
103
|
+
// true when a present-tense prefix (mi-/nemi-) is stripped; used as the
|
|
104
|
+
// default for `remove_verb_person_endings` for all suffix removal passes.
|
|
105
|
+
// - 'remove_verb_person_endings':
|
|
106
|
+
// true if a verb cue has been seen; used to enable verb person-ending rules
|
|
107
|
+
// in this pass.
|
|
108
|
+
booleans ( saw_present_prefix remove_verb_person_endings )
|
|
109
|
+
|
|
110
|
+
integers ( p1 )
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// PHASE 1 — NORMALIZATION (forward)
|
|
114
|
+
// - Unify Arabic forms to Persian letters for stable downstream matching.
|
|
115
|
+
// - Delete ZWJ (U+200D) and ASCII spaces so the token is solid.
|
|
116
|
+
// - ZWNJ (U+200C) is preserved here; used as prefix-stem separator in Phase 2,
|
|
117
|
+
// then deleted by Delete_ZWNJ after prefix stripping is complete.
|
|
118
|
+
// ============================================================================
|
|
119
|
+
define Normalize_Characters as (
|
|
120
|
+
repeat (
|
|
121
|
+
[substring] among (
|
|
122
|
+
'{ar_kaf}' (<- '{kaf}')
|
|
123
|
+
'{ar_ye}' '{ar_ye_with_hamza_above}' (<- '{ye}')
|
|
124
|
+
'{ar_teh_marbuta}' '{ar_heh}' (<- '{heh}')
|
|
125
|
+
'{ar_alef_with_hamza_above}' '{ar_alef_with_hamza_below}' (<- '{alef}')
|
|
126
|
+
'{ar_vav_with_hamza_above}' (<- '{vav}')
|
|
127
|
+
'{zero_width_joiner}' ( delete )
|
|
128
|
+
'{space}' ( delete )
|
|
129
|
+
'' ( next )
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// PHASE 2 — PREFIX HANDLING (forward, anchored)
|
|
136
|
+
// - HPS: handle known prefixes before suffix processing.
|
|
137
|
+
// - Requires ZWNJ (U+200C) between prefix and stem to avoid mishandling
|
|
138
|
+
// words that begin with the same syllables but are not prefixed forms.
|
|
139
|
+
// - At least two characters are required after the prefix.
|
|
140
|
+
// - Sets `saw_present_prefix` when matching {mi}/{nemi} to enable person endings later.
|
|
141
|
+
// - Note: negating derivational prefixes na- and bi- are intentionally not
|
|
142
|
+
// stripped as they create words with opposite meanings (e.g. na+dorost =
|
|
143
|
+
// incorrect, bi+khatr = safe) and conflating them with their base forms
|
|
144
|
+
// would harm search precision.
|
|
145
|
+
// - Similarly, nemi- negates the meaning so we don't remove it but do still
|
|
146
|
+
// set `saw_present_prefix` when it is present.
|
|
147
|
+
// ============================================================================
|
|
148
|
+
define Prefixes as (
|
|
149
|
+
[substring] among (
|
|
150
|
+
'{nun}{mim}{ye}{half_space}' // NEMI + ZWNJ: detect but keep
|
|
151
|
+
(hop 2 set saw_present_prefix)
|
|
152
|
+
'{mim}{ye}{half_space}' // MI + ZWNJ
|
|
153
|
+
(hop 2 delete set saw_present_prefix)
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// PHASE 2b — DELETE REMAINING ZWNJs (forward)
|
|
159
|
+
// - After prefix detection, any remaining ZWNJs (e.g. in compound words)
|
|
160
|
+
// are no longer needed and are removed here.
|
|
161
|
+
// ============================================================================
|
|
162
|
+
define Delete_ZWNJ as (
|
|
163
|
+
repeat (
|
|
164
|
+
goto (['{half_space}'] delete)
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
backwardmode (
|
|
169
|
+
|
|
170
|
+
define R1 as $p1 <= cursor
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// PROBE — PROTECT LEXICAL "-AN" (not plural)
|
|
174
|
+
// - HPS strips -AN as a plural; however, many stems end in orthographic "AN":
|
|
175
|
+
// ...stan / ...san / ...ran (place names/lexical stems), and frequent
|
|
176
|
+
// stems such as Iran/Tehran/Ensan/Ostan.
|
|
177
|
+
// - We signal f if a trailing pattern indicates lexical AN.
|
|
178
|
+
// - This is a probe only: it NEVER edits the buffer; it only signals.
|
|
179
|
+
// ============================================================================
|
|
180
|
+
define Protect_Lexical_AN as (
|
|
181
|
+
not AN_Exception
|
|
182
|
+
not among (
|
|
183
|
+
'{sin}{te}{alef}{nun}' // ...stan
|
|
184
|
+
'{sin}{alef}{nun}' // ...san
|
|
185
|
+
'{re}{alef}{nun}' // ...ran
|
|
186
|
+
'{vav}{alef}{nun}' // ...van: onvaan, divaan, javaan, ravaan
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// PHASE 3 — SUFFIX STRIPPING (backward)
|
|
192
|
+
// - All routines in backward mode start matching from the end of the token.
|
|
193
|
+
// - IMPORTANT: no successful "no-op" arms; every success must modify the text.
|
|
194
|
+
// This ensures 'repeat' loops terminate.
|
|
195
|
+
// - Most suffixes are only removed/replaced if they are entirely in R1 to
|
|
196
|
+
// avoid over-stemming short words.
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
// --- Lexical "-AN" suffix list.
|
|
200
|
+
// Small curated set of stems ending in orthographic -AN that are not plural.
|
|
201
|
+
// Used by `Protect_Lexical_AN` to prevent stripping on place names / lexical nouns.
|
|
202
|
+
define AN_Exception as (
|
|
203
|
+
among (
|
|
204
|
+
'{aa}{lam}{mim}{alef}{nun}' // alman
|
|
205
|
+
'{aa}{sin}{mim}{alef}{nun}' // asman
|
|
206
|
+
'{alef}{ye}{mim}{alef}{nun}' // eiman
|
|
207
|
+
'{alef}{ye}{shin}{alef}{nun}' // ishan
|
|
208
|
+
'{alef}{mim}{kaf}{alef}{nun}' // emkan
|
|
209
|
+
'{alef}{sad}{fe}{heh}{alef}{nun}' // esfahan
|
|
210
|
+
'{aa}{zal}{re}{be}{alef}{ye}{jim}{alef}{nun}' // azerbaijan
|
|
211
|
+
'{be}{ye}{alef}{nun}' // bayan
|
|
212
|
+
'{pe}{alef}{ye}{alef}{nun}' // payan
|
|
213
|
+
'{pe}{ye}{mim}{alef}{nun}' // payman
|
|
214
|
+
'{jim}{re}{ye}{alef}{nun}' // jaryan
|
|
215
|
+
'{dal}{re}{mim}{alef}{nun}' // darman
|
|
216
|
+
'{re}{mim}{alef}{nun}' // roman
|
|
217
|
+
'{ze}{nun}{dal}{alef}{nun}' // zendan
|
|
218
|
+
'{sin}{alef}{ze}{mim}{alef}{nun}' // sazman
|
|
219
|
+
'{sin}{lam}{ta}{alef}{nun}' // soltan
|
|
220
|
+
'{gaf}{ye}{lam}{alef}{nun}' // Gilan
|
|
221
|
+
'{ghaf}{heh}{re}{mim}{alef}{nun}' // ghahramaan
|
|
222
|
+
'{kaf}{re}{mim}{alef}{nun}' // Kerman
|
|
223
|
+
'{khe}{alef}{nun}{dal}{alef}{nun}' // khandan
|
|
224
|
+
'{lam}{be}{nun}{alef}{nun}' // lobnan
|
|
225
|
+
'{mim}{ye}{ze}{alef}{nun}' // mizan
|
|
226
|
+
'{mim}{sin}{lam}{mim}{alef}{nun}' // mosalman
|
|
227
|
+
'{nun}{shin}{alef}{nun}' // neshan
|
|
228
|
+
'{heh}{mim}{dal}{alef}{nun}' // hamedan
|
|
229
|
+
'{ye}{vav}{nun}{alef}{nun}' // yunan
|
|
230
|
+
'{kaf}{heh}{kaf}{shin}{alef}{nun}' // kahkeshan (galaxy)
|
|
231
|
+
'{aa}{te}{shin}{fe}{shin}{alef}{nun}' // atashfeshan (volcano)
|
|
232
|
+
'{pe}{re}{ye}{shin}{alef}{nun}' // perishan (confused)
|
|
233
|
+
'{dal}{re}{khe}{shin}{alef}{nun}' // darakhshan (shining)
|
|
234
|
+
'{heh}{mim}{ze}{mim}{alef}{nun}' // hamzaman (simultaneous)
|
|
235
|
+
'{sin}{alef}{khe}{te}{mim}{alef}{nun}' // sakhteman (building)
|
|
236
|
+
'{sin}{lam}{ye}{mim}{alef}{nun}' // soleyman (Solomon)
|
|
237
|
+
( atlimit )
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
// --- Noun irregular rewrites.
|
|
242
|
+
// Only include entries that actually change the buffer.
|
|
243
|
+
define Irregular_Noun as (
|
|
244
|
+
[substring] among (
|
|
245
|
+
'{alef}{khe}{be}{alef}{re}' (<- '{khe}{be}{re}')
|
|
246
|
+
'{alef}{sin}{alef}{te}{ye}{dal}' (<- '{alef}{sin}{te}{alef}{dal}')
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
// --- Noun/Adjective step.
|
|
251
|
+
define Stem_Noun_or_Adjective as (
|
|
252
|
+
Irregular_Noun or
|
|
253
|
+
setlimit tomark p1 for (
|
|
254
|
+
[substring] among (
|
|
255
|
+
// Noun possessive suffixes
|
|
256
|
+
'{alef}{mim}' (delete) // -AM
|
|
257
|
+
'{alef}{shin}' (delete) // -ASH
|
|
258
|
+
|
|
259
|
+
// -MAN/-TAN/-SHAN are intentionally disabled: these 3-char suffixes
|
|
260
|
+
// are ambiguous with plurals of nouns whose root ends in the same letter:
|
|
261
|
+
// ketab+maan (our book) -> strip -maan -> ketab (correct)
|
|
262
|
+
// mardom+an (people pl.) -> strip -an -> mardom (correct, NOT mardom->mard)
|
|
263
|
+
// naqqash+an (painters) -> strip -an -> naqqash (correct, NOT ->naqqaa)
|
|
264
|
+
// derakht+an (trees) -> strip -an -> derakht (correct, NOT ->derakh)
|
|
265
|
+
// Without a lexicon we cannot distinguish the two cases. The -AN
|
|
266
|
+
// rule below handles both at the cost of missing possessive stripping.
|
|
267
|
+
// '{mim}{alef}{nun}' (delete) // -MAN
|
|
268
|
+
// '{te}{alef}{nun}' (delete) // -TAN
|
|
269
|
+
// '{shin}{alef}{nun}' (delete) // -SHAN
|
|
270
|
+
|
|
271
|
+
// Noun plurals.
|
|
272
|
+
// -YAN and -GAN are productive.
|
|
273
|
+
'{ye}{alef}{nun}' (delete) // -YAN
|
|
274
|
+
'{gaf}{alef}{nun}' (delete) // -GAN
|
|
275
|
+
'{heh}{alef}{ye}' (delete) // -HAY
|
|
276
|
+
'{alef}{nun}{ye}' (delete) // -ANI
|
|
277
|
+
'{heh}{alef}' (delete) // -HA
|
|
278
|
+
'{alef}{te}' (delete) // -AT (Arabic sound plural; can also be possessive "your")
|
|
279
|
+
'{alef}{nun}' (delete) // -AN (only if not lexical)
|
|
280
|
+
'{ye}{nun}' (delete) // -IN (also adjective derivation ending)
|
|
281
|
+
|
|
282
|
+
// Other derivational noun endings (conservative set).
|
|
283
|
+
'{gaf}{alef}{heh}' (delete) // -GAH
|
|
284
|
+
'{be}{alef}{nun}' (delete) // -BAN
|
|
285
|
+
'{gaf}{ye}' (delete) // -GI (abstract noun)
|
|
286
|
+
'{ye}{te}' (delete) // -YAT
|
|
287
|
+
'{ye}{ye}' (delete) // -YY (double Y; also adj. relative -Y)
|
|
288
|
+
|
|
289
|
+
// Adjective: comparative/superlative (HPS).
|
|
290
|
+
'{te}{re}{ye}{nun}' (delete) // -TARIN
|
|
291
|
+
'{te}{re}' (not atlimit delete) // -TAR
|
|
292
|
+
|
|
293
|
+
// Adjective derivational endings (HPS list + common variants).
|
|
294
|
+
'{alef}{nun}{heh}' (delete) // -ANE
|
|
295
|
+
'{mim}{nun}{dal}' (delete) // -MAND
|
|
296
|
+
'{vav}{alef}{re}' (delete) // -VAR
|
|
297
|
+
'{nun}{alef}{kaf}' (delete) // -NAK
|
|
298
|
+
'{gaf}{alef}{re}' (delete) // -GAR
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
// --- Verb step.
|
|
304
|
+
define Stem_Verb as (
|
|
305
|
+
(
|
|
306
|
+
// Participle + person/aux clitic tails
|
|
307
|
+
[substring] among (
|
|
308
|
+
'{alef}{ye}{dal}'
|
|
309
|
+
'{alef}{ye}{mim}'
|
|
310
|
+
'{alef}{nun}{dal}'
|
|
311
|
+
'{alef}{sin}{te}'
|
|
312
|
+
'{alef}{sin}'
|
|
313
|
+
'{alef}{ye}'
|
|
314
|
+
'{ye}{dal}'
|
|
315
|
+
'{ye}{mim}'
|
|
316
|
+
(R1 delete)
|
|
317
|
+
)
|
|
318
|
+
) or (
|
|
319
|
+
[substring] among (
|
|
320
|
+
// Generic person and singular/plural endings — only when a verb cue is active
|
|
321
|
+
'{alef}{nun}{dal}' // -AND
|
|
322
|
+
'{ye}{dal}' // -ID
|
|
323
|
+
'{ye}{mim}' // -IM
|
|
324
|
+
'{alef}{mim}' // -AM
|
|
325
|
+
'{dal}' // -D
|
|
326
|
+
'{mim}' // -M
|
|
327
|
+
( remove_verb_person_endings R1 delete )
|
|
328
|
+
|
|
329
|
+
// Specific past-3sg stem fixes
|
|
330
|
+
'{re}{fe}{te}{mim}'
|
|
331
|
+
'{re}{fe}{te}{ye}'
|
|
332
|
+
'{re}{fe}{te}{ye}{mim}'
|
|
333
|
+
'{re}{fe}{te}{ye}{dal}'
|
|
334
|
+
'{re}{fe}{te}{alef}{nun}{dal}'
|
|
335
|
+
(<- '{re}{fe}{te}')
|
|
336
|
+
|
|
337
|
+
// Mood/tense markers (HPS).
|
|
338
|
+
'{nun}{dal}{heh}' // agent noun -NDEH → present root
|
|
339
|
+
'{alef}{nun}' // infinitive -N / -AN
|
|
340
|
+
(R1 delete set remove_verb_person_endings)
|
|
341
|
+
'{dal}{heh}' // past part. -deh
|
|
342
|
+
(not atlimit <-'{dal}' set remove_verb_person_endings)
|
|
343
|
+
'{te}{heh}' // past part. -teh
|
|
344
|
+
(not atlimit <-'{te}' set remove_verb_person_endings)
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
// ============================================================================
|
|
351
|
+
// MAIN (HPS pipeline)
|
|
352
|
+
// ============================================================================
|
|
353
|
+
define stem as (
|
|
354
|
+
unset saw_present_prefix
|
|
355
|
+
|
|
356
|
+
// 1) Normalize script/spacing (ZWNJ preserved for prefix detection)
|
|
357
|
+
do Normalize_Characters
|
|
358
|
+
|
|
359
|
+
// 2) Handle leading prefixes (requires ZWNJ separator)
|
|
360
|
+
do Prefixes
|
|
361
|
+
|
|
362
|
+
// 2b) Delete remaining ZWNJs now that prefix detection is done
|
|
363
|
+
do Delete_ZWNJ
|
|
364
|
+
|
|
365
|
+
// Set p1 to be 3 characters into the string.
|
|
366
|
+
$p1 = limit
|
|
367
|
+
do ( hop 3 setmark p1 )
|
|
368
|
+
|
|
369
|
+
// 3) Remove suffixes. Each pass removes one suffix, trying noun and
|
|
370
|
+
// adjective endings first, then verb endings. So if we remove a noun
|
|
371
|
+
// or adjective ending, we next check for another noun or adjective
|
|
372
|
+
// ending.
|
|
373
|
+
//
|
|
374
|
+
// Note: The loop exits if no suffix is removed and the buffer gets
|
|
375
|
+
// shorter when a suffix is removed so we can't loop forever.
|
|
376
|
+
backwards repeat test (
|
|
377
|
+
// Set remove_verb_person_endings = saw_present_prefix.
|
|
378
|
+
unset remove_verb_person_endings
|
|
379
|
+
try ( saw_present_prefix set remove_verb_person_endings )
|
|
380
|
+
|
|
381
|
+
Protect_Lexical_AN
|
|
382
|
+
// HPS as described in the paper uses a POS tagger. We don't have that
|
|
383
|
+
// information so we use a heuristic and try noun/adjective endings first,
|
|
384
|
+
// then verb endings.
|
|
385
|
+
Stem_Noun_or_Adjective or Stem_Verb
|
|
386
|
+
)
|
|
387
|
+
)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/* Polish stemmer. Author: Dmitry Shachnev */
|
|
2
|
+
|
|
3
|
+
stringescapes {}
|
|
4
|
+
|
|
5
|
+
stringdef ak '{U+0105}' // ą a + ogonek
|
|
6
|
+
stringdef ek '{U+0119}' // ę e + ogonek
|
|
7
|
+
stringdef l/ '{U+0142}' // ł l + stroke
|
|
8
|
+
stringdef c' '{U+0107}' // ć c + acute (kreska)
|
|
9
|
+
stringdef n' '{U+0144}' // ń n + acute (kreska)
|
|
10
|
+
stringdef o' '{U+00f3}' // ó o + acute (kreska)
|
|
11
|
+
stringdef s' '{U+015b}' // ś s + acute (kreska)
|
|
12
|
+
stringdef z' '{U+017a}' // ź z + acute (kreska)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
externals (stem)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
routines (
|
|
19
|
+
mark_regions
|
|
20
|
+
remove_endings
|
|
21
|
+
normalize_consonant
|
|
22
|
+
R1
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
integers ( p1 )
|
|
26
|
+
|
|
27
|
+
groupings ( v )
|
|
28
|
+
|
|
29
|
+
define v 'a{ak}e{ek}io{o'}uy'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
define mark_regions as (
|
|
33
|
+
$p1 = limit
|
|
34
|
+
gopast v
|
|
35
|
+
gopast non-v setmark p1
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
backwardmode (
|
|
39
|
+
define R1 as ($p1 <= cursor)
|
|
40
|
+
|
|
41
|
+
define remove_endings as (
|
|
42
|
+
// Verbs.
|
|
43
|
+
do (
|
|
44
|
+
setlimit tomark p1 for ([substring]) among (
|
|
45
|
+
// conditionals:
|
|
46
|
+
'bym' // 1st person singular (czytał(a)bym)
|
|
47
|
+
'by{s'}' // 2nd person singular (czytał(a)byś)
|
|
48
|
+
'by{s'}my' // 1st person plural (czytalibyśmy)
|
|
49
|
+
'by{s'}cie' // 2nd person plural (czytalibyście)
|
|
50
|
+
'by' // 3rd person singular/plural (czytał(a)by, czytaliby)
|
|
51
|
+
(delete)
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
[substring] among (
|
|
55
|
+
'asz' 'esz' 'isz' // present 2nd person singular (czytasz, piszesz, nosisz)
|
|
56
|
+
'amy' 'emy' 'imy' // present 1st person plural (czytamy, piszemy, nosimy)
|
|
57
|
+
'acie' 'ecie' 'icie' // present 2nd person plural (czytacie, piszecie, nosicie)
|
|
58
|
+
'aj{ak}' // present 3rd person plural (czytają)
|
|
59
|
+
'e{s'}{c'}' // infinitive (przynieść)
|
|
60
|
+
'a{s'}{c'}' // infinitive (popaść)
|
|
61
|
+
'a{c'}' // infinitive (czytać)
|
|
62
|
+
'ie{c'}' // infinitive (lecieć)
|
|
63
|
+
'i{c'}' // infinitive (wozić)
|
|
64
|
+
'{ak}{c'}' // infinitive (marznąć)
|
|
65
|
+
'aj{ak}c' '{ak}c' // contemporary adverbial participle (transgressive) (czytając, lecąc)
|
|
66
|
+
'a{l/}em' 'ia{l/}em' 'i{l/}em' // past 1st person singular masculine (czytałem, leciałem, chodziłem)
|
|
67
|
+
'a{l/}am' 'ia{l/}am' 'i{l/}am' 'am' // past 1st person singular feminine (czytałam, leciałam, chodziłam, marzłam)
|
|
68
|
+
'a{l/}e{s'}' 'ia{l/}e{s'}' 'i{l/}e{s'}' // past 2nd person singular masculine (czytałeś, leciałeś, chodziłeś)
|
|
69
|
+
'a{l/}a{s'}' 'ia{l/}a{s'}' 'i{l/}a{s'}' // past 2nd person singular feminine (czytałaś, leciałaś, chodziłaś)
|
|
70
|
+
'a{l/}' 'ia{l/}' 'i{l/}' // past 3rd person singular masculine (czytał, leciał, chodził)
|
|
71
|
+
'a{l/}a' 'ia{l/}a' 'i{l/}a' // past 3rd person singular feminine (czytała, leciała, chodziła)
|
|
72
|
+
'a{l/}o' 'ia{l/}o' 'i{l/}o' // past 3rd person singular neuter (czytało, leciało, chodziło)
|
|
73
|
+
'ali{s'}my' 'ieli{s'}my' 'ili{s'}my' // past 1st person plural virile (czytaliśmy, lecieliśmy, chodziliśmy)
|
|
74
|
+
'a{l/}y{s'}my' 'ia{l/}y{s'}my' 'i{l/}y{s'}my' // past 1st person plural nonvirile (czytałyśmy, leciałyśmy, chodziłyśmy)
|
|
75
|
+
'ali{s'}cie' 'ieli{s'}cie' 'ili{s'}cie' // past 2nd person plural virile (czytaliście, lecieliście, chodziliście)
|
|
76
|
+
'a{l/}y{s'}cie' 'ia{l/}y{s'}cie' 'i{l/}y{s'}cie' // past 2nd person plural nonvirile (czytałyście, leciałyście, chodziłyście)
|
|
77
|
+
'ali' 'ieli' 'ili' // past 3rd person plural virile (czytali, lecieli, chodzili)
|
|
78
|
+
'a{l/}y' 'ia{l/}y' 'i{l/}y' // past 3rd person plural nonvirile (czytały, leciały, chodziły)
|
|
79
|
+
'aj' // imperative 2nd person singular (czytaj)
|
|
80
|
+
'ajcie' // imperative 2nd person plural (czytajcie)
|
|
81
|
+
'cie' // imperative 2nd person plural (chodźcie)
|
|
82
|
+
'{ek}' // present 1st person singular (lecę)
|
|
83
|
+
(delete)
|
|
84
|
+
'sz{ek}' // present 1st person singular (noszę)
|
|
85
|
+
(<- 's')
|
|
86
|
+
'sz{ak}' // present 3rd person plural (noszą)
|
|
87
|
+
// Also an adjectival form (singular feminine accusative), e.g. lepszą.
|
|
88
|
+
// This heuristic does the right thing in common cases.
|
|
89
|
+
(R1 and delete or <-'s')
|
|
90
|
+
|
|
91
|
+
// There are short verbs whose root consists of only one consonant, e.g. być, żyć.
|
|
92
|
+
// Stemming them to one letter would merge them with these letters used in other
|
|
93
|
+
// contexts, which is undesirable. But let's at least merge all past tense forms
|
|
94
|
+
// together, e.g. byłem, byłam, byłyśmy, etc. to był.
|
|
95
|
+
'{l/}e{s'}'
|
|
96
|
+
'{l/}a{s'}'
|
|
97
|
+
'li{s'}my'
|
|
98
|
+
'{l/}y{s'}my'
|
|
99
|
+
'li{s'}cie'
|
|
100
|
+
'{l/}y{s'}cie'
|
|
101
|
+
(<- '{l/}')
|
|
102
|
+
|
|
103
|
+
// Adjectives (including comparative/superlative forms)
|
|
104
|
+
// as well as participles.
|
|
105
|
+
'y' // singular masculine nominative (nowy)
|
|
106
|
+
'ego' 'iego' // singular masculine genitive (nowego, polskiego)
|
|
107
|
+
'emu' 'iemu' // singular masculine dative (nowemu, polskiemu)
|
|
108
|
+
'ym' 'im' // singular masculine instrumental (nowym, polskim)
|
|
109
|
+
'ej' 'iej' // singular feminine genitive (nowej, polskiej)
|
|
110
|
+
'ych' 'ich' // plural genitive (nowych, polskich)
|
|
111
|
+
'ymi' 'imi' // plural instrumental (nowymi, polskimi)
|
|
112
|
+
(
|
|
113
|
+
delete
|
|
114
|
+
try (
|
|
115
|
+
[substring] among (
|
|
116
|
+
'aj{ak}c' // participle suffix (czytający)
|
|
117
|
+
'{ak}c' // participle suffix (lecący)
|
|
118
|
+
'iejsz' // comparative suffix (piękniejszy)
|
|
119
|
+
'sz' // comparative suffix (lepszy)
|
|
120
|
+
(delete)
|
|
121
|
+
'sz{ak}c' // participle suffix (noszący)
|
|
122
|
+
(<- 's')
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
// We cannot remove endings like -ą and -e unconditionally, because these
|
|
127
|
+
// letters appear in too many contexts. But we can safely remove them if we
|
|
128
|
+
// know that our word is a participle or a comparative/superlative form.
|
|
129
|
+
'aj{ak}ca' '{ak}ca' 'iejsza' 'sza' // singular feminine nominative (czytająca, lecąca, piękniejsza, lepsza)
|
|
130
|
+
'aj{ak}c{ak}' '{ak}c{ak}' 'iejsz{ak}' // singular feminine accusative (czytającą, lecącą, piękniejszą); -szą is handled separately
|
|
131
|
+
'aj{ak}ce' '{ak}ce' 'iejsze' 'sze' // singular neuter nominative (czytające, lecące, piękniejsze, lepsze)
|
|
132
|
+
(delete)
|
|
133
|
+
// Handle participles like nosząca, prosząca.
|
|
134
|
+
'sz{ak}ca'
|
|
135
|
+
'sz{ak}c{ak}'
|
|
136
|
+
'sz{ak}ce'
|
|
137
|
+
(<- 's')
|
|
138
|
+
|
|
139
|
+
// Noun forms (excluding endings that were already handled above).
|
|
140
|
+
'a' R1 'o' R1 // singular nominative (książka, lato)
|
|
141
|
+
'i' R1 'u' R1 'ia' R1 // singular genitive (książki, stołu, słonia)
|
|
142
|
+
'owi' R1 'iowi' R1 // singular dative (stołowi, słoniowi)
|
|
143
|
+
'{ak}' R1 'i{ak}' R1 'em' R1 'iem' R1 // singular instrumental (książką, możliwością, stołem, słoniem)
|
|
144
|
+
'e' R1 'iu' R1 // singular locative (stole, słoniu)
|
|
145
|
+
'ie' R1 // plural nominative (słonie)
|
|
146
|
+
'{o'}w' R1 // plural genitive (stołów)
|
|
147
|
+
'om' R1 'iom' R1 // plural dative (książkom, słoniom)
|
|
148
|
+
'ami' R1 'iami' R1 // plural instrumental (książkami, słoniami)
|
|
149
|
+
'ach' R1 'iach' R1 // plural locative (książkach, słoniach)
|
|
150
|
+
(delete)
|
|
151
|
+
)
|
|
152
|
+
try (['{'}'] delete)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
define normalize_consonant as (
|
|
156
|
+
// Remove kreska mark, because most of oblique cases do not have it.
|
|
157
|
+
// Don't mutate single character inputs.
|
|
158
|
+
[substring] not atlimit among (
|
|
159
|
+
'{c'}' (<- 'c') // e.g. miłość → miłośc
|
|
160
|
+
'{n'}' (<- 'n') // e.g. słoń → słon
|
|
161
|
+
'{s'}' (<- 's') // e.g. gęś → gęs
|
|
162
|
+
'{z'}' (<- 'z') // e.g. miedź → miedz
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
define stem as (
|
|
168
|
+
do mark_regions
|
|
169
|
+
// Make sure we don't produce too short outputs. The "backwards" will
|
|
170
|
+
// set the backwards limit to the current cursor position.
|
|
171
|
+
(
|
|
172
|
+
hop 2
|
|
173
|
+
backwards remove_endings
|
|
174
|
+
) or (
|
|
175
|
+
backwards normalize_consonant
|
|
176
|
+
)
|
|
177
|
+
)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* stringescapes UTF-8 */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Sesotho stemmer for the Snowball project
|
|
5
|
+
----------------------------------------
|
|
6
|
+
Author: Kamohelo Lebjane
|
|
7
|
+
Purpose: To reduce Sesotho words to their morphological stems.
|
|
8
|
+
|
|
9
|
+
Language notes:
|
|
10
|
+
Sesotho (Southern Sotho) is an agglutinative Bantu language.
|
|
11
|
+
Words often contain prefixes for noun classes and suffixes
|
|
12
|
+
for tense, aspect, or derivation.
|
|
13
|
+
|
|
14
|
+
An agglutinative language is a type of language that primarily forms words
|
|
15
|
+
by stringing together morphemes (word parts)—each typically representing a single
|
|
16
|
+
grammatical meaning—without significant modification to their forms (agglutinations).
|
|
17
|
+
In such languages, affixes (prefixes, suffixes, infixes, or circumfixes)
|
|
18
|
+
are added to a root word in a linear and systematic way, creating complex
|
|
19
|
+
words that encode detailed grammatical information.
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
baruti -> rut (root)
|
|
23
|
+
moruti -> rut (root)
|
|
24
|
+
rutile -> rut (root)
|
|
25
|
+
|
|
26
|
+
The rules below remove common noun class prefixes
|
|
27
|
+
and common verb suffixes, keeping the main root form.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/* --- Routine declarations --- */
|
|
31
|
+
routines (
|
|
32
|
+
mark_regions
|
|
33
|
+
remove_noun_prefixes
|
|
34
|
+
remove_verb_suffixes
|
|
35
|
+
remove_nominal_suffixes
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
/* --- External declarations --- */
|
|
39
|
+
externals ( stem )
|
|
40
|
+
|
|
41
|
+
/* --- Groupings --- */
|
|
42
|
+
groupings ( v )
|
|
43
|
+
|
|
44
|
+
/* --- Character sets --- */
|
|
45
|
+
define v 'aeiou'
|
|
46
|
+
|
|
47
|
+
/* --- Integer for tracking position --- */
|
|
48
|
+
integers ( pV )
|
|
49
|
+
|
|
50
|
+
/* --- Mark vowel region --- */
|
|
51
|
+
define mark_regions as (
|
|
52
|
+
// Set pV after the first vowel and at least 2 characters into the string.
|
|
53
|
+
// Signals f if the string doesn't contain a vowel or is shorter than 2
|
|
54
|
+
// characters.
|
|
55
|
+
test (gopast v setmark pV)
|
|
56
|
+
test (hop 2 do ($(cursor > pV) setmark pV))
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
/* --- Remove noun class prefixes --- */
|
|
60
|
+
define remove_noun_prefixes as (
|
|
61
|
+
[substring] among(
|
|
62
|
+
'mo' 'ba' 'me' 'le'
|
|
63
|
+
'ma' 'se' 'boi' 'li'
|
|
64
|
+
)
|
|
65
|
+
/* Require at least two characters remain */
|
|
66
|
+
test (next not atlimit)
|
|
67
|
+
/* Only delete if there's a vowel after the cursor position */
|
|
68
|
+
gopast v delete
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
backwardmode (
|
|
72
|
+
|
|
73
|
+
/* --- Remove verb suffixes (from end of word) --- */
|
|
74
|
+
define remove_verb_suffixes as (
|
|
75
|
+
setlimit tomark pV for (
|
|
76
|
+
[substring] among(
|
|
77
|
+
'ile' /* perfect tense */
|
|
78
|
+
'isa' /* causative */
|
|
79
|
+
'etse' /* applicative */
|
|
80
|
+
'ela' /* applicative */
|
|
81
|
+
'ang' /* plural imperative */
|
|
82
|
+
'ong' /* continuous/derived form */
|
|
83
|
+
'eng'
|
|
84
|
+
'wa' /* passive */
|
|
85
|
+
'a' /* infinitive marker */
|
|
86
|
+
(delete)
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
/* --- Remove nominal suffixes --- */
|
|
92
|
+
define remove_nominal_suffixes as (
|
|
93
|
+
setlimit tomark pV for (
|
|
94
|
+
[substring] among(
|
|
95
|
+
'nyana' /* diminutive form */
|
|
96
|
+
'ana' /* diminutive form */
|
|
97
|
+
'ano'
|
|
98
|
+
'oa'
|
|
99
|
+
'i'
|
|
100
|
+
(delete)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/* --- MAIN STEMMER --- */
|
|
108
|
+
define stem as (
|
|
109
|
+
mark_regions // Signals f if the string is too short to stem.
|
|
110
|
+
backwards (
|
|
111
|
+
do remove_nominal_suffixes
|
|
112
|
+
do remove_verb_suffixes
|
|
113
|
+
)
|
|
114
|
+
do remove_noun_prefixes
|
|
115
|
+
)
|