nlptoolkit-morphologicalanalysis 1.0.0

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 (65) hide show
  1. package/README.md +144 -0
  2. package/dist/Corpus/DisambiguatedWord.d.ts +20 -0
  3. package/dist/Corpus/DisambiguatedWord.js +38 -0
  4. package/dist/Corpus/DisambiguatedWord.js.map +1 -0
  5. package/dist/Corpus/DisambiguationCorpus.d.ts +4 -0
  6. package/dist/Corpus/DisambiguationCorpus.js +54 -0
  7. package/dist/Corpus/DisambiguationCorpus.js.map +1 -0
  8. package/dist/MorphologicalAnalysis/FiniteStateMachine.d.ts +63 -0
  9. package/dist/MorphologicalAnalysis/FiniteStateMachine.js +178 -0
  10. package/dist/MorphologicalAnalysis/FiniteStateMachine.js.map +1 -0
  11. package/dist/MorphologicalAnalysis/FsmMorphologicalAnalyzer.d.ts +399 -0
  12. package/dist/MorphologicalAnalysis/FsmMorphologicalAnalyzer.js +1255 -0
  13. package/dist/MorphologicalAnalysis/FsmMorphologicalAnalyzer.js.map +1 -0
  14. package/dist/MorphologicalAnalysis/FsmParse.d.ts +290 -0
  15. package/dist/MorphologicalAnalysis/FsmParse.js +684 -0
  16. package/dist/MorphologicalAnalysis/FsmParse.js.map +1 -0
  17. package/dist/MorphologicalAnalysis/FsmParseList.d.ts +96 -0
  18. package/dist/MorphologicalAnalysis/FsmParseList.js +242 -0
  19. package/dist/MorphologicalAnalysis/FsmParseList.js.map +1 -0
  20. package/dist/MorphologicalAnalysis/InflectionalGroup.d.ts +77 -0
  21. package/dist/MorphologicalAnalysis/InflectionalGroup.js +213 -0
  22. package/dist/MorphologicalAnalysis/InflectionalGroup.js.map +1 -0
  23. package/dist/MorphologicalAnalysis/MetamorphicParse.d.ts +63 -0
  24. package/dist/MorphologicalAnalysis/MetamorphicParse.js +592 -0
  25. package/dist/MorphologicalAnalysis/MetamorphicParse.js.map +1 -0
  26. package/dist/MorphologicalAnalysis/MorphologicalParse.d.ts +301 -0
  27. package/dist/MorphologicalAnalysis/MorphologicalParse.js +969 -0
  28. package/dist/MorphologicalAnalysis/MorphologicalParse.js.map +1 -0
  29. package/dist/MorphologicalAnalysis/MorphologicalTag.d.ts +510 -0
  30. package/dist/MorphologicalAnalysis/MorphologicalTag.js +525 -0
  31. package/dist/MorphologicalAnalysis/MorphologicalTag.js.map +1 -0
  32. package/dist/MorphologicalAnalysis/State.d.ts +40 -0
  33. package/dist/MorphologicalAnalysis/State.js +64 -0
  34. package/dist/MorphologicalAnalysis/State.js.map +1 -0
  35. package/dist/MorphologicalAnalysis/Transition.d.ts +159 -0
  36. package/dist/MorphologicalAnalysis/Transition.js +751 -0
  37. package/dist/MorphologicalAnalysis/Transition.js.map +1 -0
  38. package/index.js +12 -0
  39. package/package.json +30 -0
  40. package/penntreebank.txt +208431 -0
  41. package/source/Corpus/DisambiguatedWord.ts +29 -0
  42. package/source/Corpus/DisambiguationCorpus.ts +39 -0
  43. package/source/MorphologicalAnalysis/FiniteStateMachine.ts +165 -0
  44. package/source/MorphologicalAnalysis/FsmMorphologicalAnalyzer.ts +1256 -0
  45. package/source/MorphologicalAnalysis/FsmParse.ts +664 -0
  46. package/source/MorphologicalAnalysis/FsmParseList.ts +238 -0
  47. package/source/MorphologicalAnalysis/InflectionalGroup.ts +210 -0
  48. package/source/MorphologicalAnalysis/MetamorphicParse.ts +589 -0
  49. package/source/MorphologicalAnalysis/MorphologicalParse.ts +995 -0
  50. package/source/MorphologicalAnalysis/MorphologicalTag.ts +510 -0
  51. package/source/MorphologicalAnalysis/State.ts +59 -0
  52. package/source/MorphologicalAnalysis/Transition.ts +733 -0
  53. package/source/tsconfig.json +13 -0
  54. package/tests/DisambiguationCorpusTest.ts +12 -0
  55. package/tests/FiniteStateMachineTest.ts +87 -0
  56. package/tests/FsmMorphologicalAnalyzerTest.ts +204 -0
  57. package/tests/FsmParseListTest.ts +90 -0
  58. package/tests/FsmParseTest.ts +66 -0
  59. package/tests/InflectionalGroupTest.ts +84 -0
  60. package/tests/MorphologicalParseTest.ts +152 -0
  61. package/tests/TransitionTest.ts +174 -0
  62. package/tsconfig.json +15 -0
  63. package/turkish_dictionary.txt +62120 -0
  64. package/turkish_finite_state_machine.xml +1887 -0
  65. package/turkish_misspellings.txt +148932 -0
@@ -0,0 +1,733 @@
1
+ import {State} from "./State";
2
+ import {TxtWord} from "nlptoolkit-dictionary/dist/Dictionary/TxtWord";
3
+ import {TurkishLanguage} from "nlptoolkit-dictionary/dist/Language/TurkishLanguage";
4
+ import {FsmParse} from "./FsmParse";
5
+
6
+ export class Transition {
7
+
8
+ private readonly _toState: State = undefined
9
+ private readonly _with: string = undefined
10
+ private readonly withName: string = undefined
11
+ private formationToCheck: string = undefined
12
+ private readonly _toPos: string = undefined
13
+
14
+ /**
15
+ * Another constructor of {@link Transition} class which takes a {@link State}, and three {@link String}s as input. Then it
16
+ * initializes toState, with, withName and toPos variables with given inputs.
17
+ *
18
+ * @param toState {@link State} input.
19
+ * @param _with String input.
20
+ * @param withName String input.
21
+ * @param toPos String input.
22
+ */
23
+ constructor(_with: string, withName?: string, toState?: State, toPos?: string) {
24
+ this._with = _with
25
+ this.withName = withName
26
+ this._toState = toState
27
+ this._toPos = toPos
28
+ }
29
+
30
+ /**
31
+ * Getter for the toState variable.
32
+ *
33
+ * @return toState variable.
34
+ */
35
+ toState(): State{
36
+ return this._toState
37
+ }
38
+
39
+ /**
40
+ * Getter for the toPos variable.
41
+ *
42
+ * @return toPos variable.
43
+ */
44
+ toPos(): string{
45
+ return this._toPos
46
+ }
47
+
48
+ /**
49
+ * The transitionPossible method takes two {@link String} as inputs; currentSurfaceForm and realSurfaceForm. If the
50
+ * length of the given currentSurfaceForm is greater than the given realSurfaceForm, it directly returns true. If not,
51
+ * it takes a substring from given realSurfaceForm with the size of currentSurfaceForm. Then checks for the characters of
52
+ * with variable.
53
+ * <p>
54
+ * If the character of with that makes transition is C, it returns true if the substring contains c or ç.
55
+ * If the character of with that makes transition is D, it returns true if the substring contains d or t.
56
+ * If the character of with that makes transition is A, it returns true if the substring contains a or e.
57
+ * If the character of with that makes transition is K, it returns true if the substring contains k, g or ğ.
58
+ * If the character of with that makes transition is other than the ones above, it returns true if the substring
59
+ * contains the same character as with.
60
+ *
61
+ * @param currentSurfaceForm {@link String} input.
62
+ * @param realSurfaceForm {@link String} input.
63
+ * @return true when the transition is possible according to Turkish grammar, false otherwise.
64
+ */
65
+ transitionPossible(currentSurfaceForm: string, realSurfaceForm: string): boolean{
66
+ if (currentSurfaceForm.length == 0 || currentSurfaceForm.length >= realSurfaceForm.length) {
67
+ return true;
68
+ }
69
+ let searchString = realSurfaceForm.substring(currentSurfaceForm.length);
70
+ for (let i = 0; i < this._with.length; i++) {
71
+ switch (this._with.charAt(i)) {
72
+ case 'C':
73
+ return searchString.includes("c") || searchString.includes("ç");
74
+ case 'D':
75
+ return searchString.includes("d") || searchString.includes("t");
76
+ case 'c':
77
+ case 'e':
78
+ case 'r':
79
+ case 'p':
80
+ case 'l':
81
+ case 'b':
82
+ case 'g':
83
+ case 'o':
84
+ case 'm':
85
+ case 'v':
86
+ case 'i':
87
+ case 'ü':
88
+ case 'z':
89
+ return searchString.includes(this._with.charAt(i));
90
+ case 'A':
91
+ return searchString.includes("a") || searchString.includes("e");
92
+ case 'k':
93
+ return searchString.includes("k") || searchString.includes("g") || searchString.includes("ğ");
94
+ }
95
+ }
96
+ return true;
97
+ }
98
+
99
+ /**
100
+ * The transitionPossible method takes a {@link FsmParse} currentFsmParse as an input. It then checks some special cases;
101
+ *
102
+ * @param currentFsmParse Parse to be checked
103
+ * @return true if transition is possible false otherwise
104
+ */
105
+ transitionPossibleFromParse(currentFsmParse: FsmParse): boolean{
106
+ if (this._with == "Ar" && currentFsmParse.getSurfaceForm().endsWith("l") &&
107
+ currentFsmParse.getWord().getName() != currentFsmParse.getSurfaceForm()) {
108
+ return false;
109
+ }
110
+ if (currentFsmParse.getVerbAgreement() != null && currentFsmParse.getPossesiveAgreement() != null &&
111
+ this.withName != undefined) {
112
+ if (currentFsmParse.getVerbAgreement() == "A3PL" && this.withName == "^DB+VERB+ZERO+PRES+A1SG") {
113
+ return false;
114
+ }
115
+ if (currentFsmParse.getVerbAgreement() == "A3SG" &&
116
+ (currentFsmParse.getPossesiveAgreement() == "P1SG" || currentFsmParse.getPossesiveAgreement() == "P2SG") &&
117
+ this.withName == "^DB+VERB+ZERO+PRES+A1PL") {
118
+ return false;
119
+ }
120
+ }
121
+ return true;
122
+ }
123
+
124
+ transitionPossibleFromRoot(root: TxtWord, fromState: State){
125
+ if (root.isAdjective() && ((root.isNominal() && !root.isExceptional()) || root.isPronoun()) && this._toState.getName() == "NominalRoot(ADJ)" && this._with == "0") {
126
+ return false;
127
+ }
128
+ if (root.isAdjective() && root.isNominal() && this._with == "^DB+VERB+ZERO+PRES+A3PL" && fromState.getName() == "AdjectiveRoot") {
129
+ return false;
130
+ }
131
+ if (root.isAdjective() && root.isNominal() && this._with == "SH" && fromState.getName() == "AdjectiveRoot") {
132
+ return false;
133
+ }
134
+ if (this._with == "ki") {
135
+ return root.takesRelativeSuffixKi();
136
+ }
137
+ if (this._with == "kü") {
138
+ return root.takesRelativeSuffixKu();
139
+ }
140
+ if (this._with == "dHr") {
141
+ if (this._toState.getName() == "Adverb") {
142
+ return true;
143
+ } else {
144
+ return root.takesSuffixDIRAsFactitive();
145
+ }
146
+ }
147
+ if (this._with == "Hr" && (this._toState.getName() == "AdjectiveRoot(VERB)" ||
148
+ this._toState.getName() == "OtherTense" || this._toState.getName() == "OtherTense2")) {
149
+ return root.takesSuffixIRAsAorist();
150
+ }
151
+ return true;
152
+ }
153
+
154
+ /**
155
+ * The beforeLastVowel method takes a {@link String} stem as an input. It loops through the given stem and returns
156
+ * the second last vowel.
157
+ *
158
+ * @param stem String input.
159
+ * @return Vowel before the last vowel.
160
+ */
161
+ private beforeLastVowel(stem: string): string{
162
+ let before = 1;
163
+ let last = '0';
164
+ for (let i = stem.length - 1; i >= 0; i--) {
165
+ if (TurkishLanguage.isVowel(stem.charAt(i))) {
166
+ if (before == 1) {
167
+ last = stem.charAt(i);
168
+ before--;
169
+ continue;
170
+ }
171
+ return stem.charAt(i);
172
+ }
173
+ }
174
+ return last;
175
+ }
176
+
177
+ /**
178
+ * The lastVowel method takes a {@link String} stem as an input. It loops through the given stem and returns
179
+ * the last vowel.
180
+ *
181
+ * @param stem String input.
182
+ * @return the last vowel.
183
+ */
184
+ private lastVowel(stem: string): string{
185
+ for (let i = stem.length - 1; i >= 0; i--) {
186
+ if (TurkishLanguage.isVowel(stem.charAt(i))) {
187
+ return stem.charAt(i);
188
+ }
189
+ }
190
+ for (let i = stem.length - 1; i >= 0; i--) {
191
+ if (stem.charAt(i) >= '0' && stem.charAt(i) <= '9') {
192
+ return stem.charAt(i);
193
+ }
194
+ }
195
+ return '0';
196
+ }
197
+
198
+ /**
199
+ * The lastPhoneme method takes a {@link String} stem as an input. It then returns the last phoneme of the given stem.
200
+ *
201
+ * @param stem String input.
202
+ * @return the last phoneme.
203
+ */
204
+ private lastPhoneme(stem: string): string{
205
+ if (stem.length == 0) {
206
+ return ' ';
207
+ }
208
+ if (stem.charAt(stem.length - 1) != '\'') {
209
+ return stem.charAt(stem.length - 1);
210
+ } else {
211
+ return stem.charAt(stem.length - 2);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * The withFirstChar method returns the first character of the with variable.
217
+ *
218
+ * @return the first character of the with variable.
219
+ */
220
+ private withFirstChar(): string{
221
+ if (this._with.length == 0) {
222
+ return '$';
223
+ }
224
+ if (this._with.charAt(0) != '\'') {
225
+ return this._with.charAt(0);
226
+ } else {
227
+ if (this._with.length == 1) {
228
+ return this._with.charAt(0);
229
+ } else {
230
+ return this._with.charAt(1);
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ * The startWithVowelorConsonantDrops method checks for some cases. If the first character of with variable is "nsy",
237
+ * and with variable does not equal to one of the Strings; "ylA, ysA, ymHs, yDH, yken", it returns true. If
238
+ * <p>
239
+ * Or, if the first character of with variable is 'A, H: or any other vowels, it returns true.
240
+ *
241
+ * @return true if it starts with vowel or consonant drops, false otherwise.
242
+ */
243
+ private startWithVowelorConsonantDrops(): boolean{
244
+ if (TurkishLanguage.isConsonantDrop(this.withFirstChar()) && this._with != "ylA" && this._with !="ysA" &&
245
+ this._with !="ymHs" && this._with != "yDH" && this._with != "yken") {
246
+ return true;
247
+ }
248
+ if (this.withFirstChar() == 'A' || this.withFirstChar() == 'H' || TurkishLanguage.isVowel(this.withFirstChar())) {
249
+ return true;
250
+ }
251
+ return false;
252
+ }
253
+
254
+ /**
255
+ * The softenDuringSuffixation method takes a {@link TxtWord} root as an input. It checks two cases; first case returns
256
+ * true if the given root is nominal or adjective and has one of the flags "IS_SD, IS_B_SD, IS_SDD" and with variable
257
+ * equals o one of the Strings "Hm, nDAn, ncA, nDA, yA, yHm, yHz, yH, nH, nA, nHn, H, sH, Hn, HnHz, HmHz".
258
+ * <p>
259
+ * And the second case returns true if the given root is verb and has the "F_SD" flag, also with variable starts with
260
+ * "Hyor" or equals one of the Strings "yHs, yAn, yA, yAcAk, yAsH, yHncA, yHp, yAlH, yArAk, yAdur, yHver, yAgel, yAgor,
261
+ * yAbil, yAyaz, yAkal, yAkoy, yAmA, yHcH, HCH, Hr, Hs, Hn, yHn", yHnHz, Ar, Hl").
262
+ *
263
+ * @param root {@link TxtWord} input.
264
+ * @return true if there is softening during suffixation of the given root, false otherwise.
265
+ */
266
+ softenDuringSuffixation(root: TxtWord): boolean{
267
+ if ((root.isNominal() || root.isAdjective()) && root.nounSoftenDuringSuffixation() &&
268
+ (this._with == "Hm" || this._with == "nDAn" || this._with == "ncA" || this._with == "nDA" ||
269
+ this._with == "yA" || this._with == "yHm" || this._with == "yHz" || this._with == "yH" ||
270
+ this._with == "nH" || this._with == "nA" || this._with == "nHn" || this._with == "H" ||
271
+ this._with == "sH" || this._with == "Hn" || this._with == "HnHz" || this._with == "HmHz")) {
272
+ return true;
273
+ }
274
+ if (root.isVerb() && root.verbSoftenDuringSuffixation() &&
275
+ (this._with.startsWith("Hyor") || this._with == "yHs" || this._with == "yAn" || this._with == "yA" ||
276
+ this._with.startsWith("yAcAk") || this._with == "yAsH" || this._with == "yHncA" || this._with == "yHp" ||
277
+ this._with == "yAlH" || this._with == "yArAk" || this._with == "yAdur" || this._with == "yHver" ||
278
+ this._with == "yAgel" || this._with == "yAgor" || this._with == "yAbil" || this._with == "yAyaz" ||
279
+ this._with == "yAkal" || this._with == "yAkoy" || this._with == "yAmA" || this._with == "yHcH" ||
280
+ this._with == "HCH" || this._with.startsWith("Hr") || this._with == "Hs" || this._with == "Hn" ||
281
+ this._with == "yHn" || this._with == "yHnHz" || this._with.startsWith("Ar") || this._with == "Hl")) {
282
+ return true;
283
+ }
284
+ return false;
285
+ }
286
+
287
+ /**
288
+ * The makeTransition method takes a {@link TxtWord} root and s {@link String} stem as inputs. If given root is a verb,
289
+ * it makes transition with given root and stem with the verbal root state. If given root is not verb, it makes transition
290
+ * with given root and stem and the nominal root state.
291
+ *
292
+ * @param root {@link TxtWord} input.
293
+ * @param stem String input.
294
+ * @param startState Start state to make the transition.
295
+ * @return String type output that has the transition.
296
+ */
297
+ makeTransition(root: TxtWord, stem: string, startState?: State): string{
298
+ if (startState == undefined){
299
+ if (root.isVerb()) {
300
+ return this.makeTransition(root, stem, new State("VerbalRoot", true, false));
301
+ } else {
302
+ return this.makeTransition(root, stem, new State("NominalRoot", true, false));
303
+ }
304
+ } else {
305
+ let rootWord = root.getName() == stem || (root.getName() + "'") == stem;
306
+ let formation = stem;
307
+ let i = 0;
308
+ if (this._with == "0") {
309
+ return stem;
310
+ }
311
+ if ((stem== "bu" || stem== "şu" || stem== "o") && rootWord && this._with == "ylA") {
312
+ return stem + "nunla";
313
+ }
314
+ if (this._with == "yA") {
315
+ if (stem== "ben") {
316
+ return "bana";
317
+ }
318
+ if (stem== "sen") {
319
+ return "sana";
320
+ }
321
+ }
322
+ this.formationToCheck = stem;
323
+ //---vowelEChangesToIDuringYSuffixation---
324
+ //de->d(i)yor, ye->y(i)yor
325
+ if (rootWord && this.withFirstChar() == 'y' && root.vowelEChangesToIDuringYSuffixation() &&
326
+ (this._with.charAt(1) != 'H' || root.getName()== "ye")) {
327
+ formation = stem.substring(0, stem.length - 1) + 'i';
328
+ this.formationToCheck = formation;
329
+ } else {
330
+ //---lastIdropsDuringPassiveSuffixation---
331
+ // yoğur->yoğrul, ayır->ayrıl, buyur->buyrul, çağır->çağrıl, çevir->çevril, devir->devril,
332
+ // kavur->kavrul, kayır->kayrıl, kıvır->kıvrıl, savur->savrul, sıyır->sıyrıl, yoğur->yoğrul
333
+ if (rootWord && (this._with == "Hl" || this._with == "Hn") && root.lastIdropsDuringPassiveSuffixation()) {
334
+ formation = stem.substring(0, stem.length - 2) + stem.charAt(stem.length - 1);
335
+ this.formationToCheck = stem;
336
+ } else {
337
+ //---showsSuRegularities---
338
+ //karasu->karasuyu, su->suyu, ağırsu->ağırsuyu, akarsu->akarsuyu, bengisu->bengisuyu
339
+ if (rootWord && root.showsSuRegularities() && this.startWithVowelorConsonantDrops() && !this._with.startsWith("y")) {
340
+ formation = stem + 'y';
341
+ this.formationToCheck = formation;
342
+ } else {
343
+ if (rootWord && root.duplicatesDuringSuffixation() && !startState.getName().startsWith("VerbalRoot") &&
344
+ TurkishLanguage.isConsonantDrop(this._with.charAt(0))) {
345
+ //---duplicatesDuringSuffixation---
346
+ if (this.softenDuringSuffixation(root)) {
347
+ //--extra softenDuringSuffixation
348
+ switch (this.lastPhoneme(stem)) {
349
+ case 'p':
350
+ //tıp->tıbbı
351
+ formation = stem.substring(0, stem.length - 1) + "bb";
352
+ break;
353
+ case 't':
354
+ //cet->ceddi, met->meddi, ret->reddi, serhat->serhaddi, zıt->zıddı, şet->şeddi
355
+ formation = stem.substring(0, stem.length - 1) + "dd";
356
+ break;
357
+ }
358
+ } else {
359
+ //cer->cerri, emrihak->emrihakkı, fek->fekki, fen->fenni, had->haddi, hat->hattı,
360
+ // haz->hazzı, his->hissi
361
+ formation = stem + stem.charAt(stem.length - 1);
362
+ }
363
+ this.formationToCheck = formation;
364
+ } else {
365
+ if (rootWord && root.lastIdropsDuringSuffixation() &&
366
+ !startState.getName().startsWith("VerbalRoot") && !startState.getName().startsWith("ProperRoot") &&
367
+ this.startWithVowelorConsonantDrops()) {
368
+ //---lastIdropsDuringSuffixation---
369
+ if (this.softenDuringSuffixation(root)) {
370
+ //---softenDuringSuffixation---
371
+ switch (this.lastPhoneme(stem)) {
372
+ case 'p':
373
+ //hizip->hizbi, kayıp->kaybı, kayıt->kaydı, kutup->kutbu
374
+ formation = stem.substring(0, stem.length - 2) + 'b';
375
+ break;
376
+ case 't':
377
+ //akit->akdi, ahit->ahdi, lahit->lahdi, nakit->nakdi, vecit->vecdi
378
+ formation = stem.substring(0, stem.length - 2) + 'd';
379
+ break;
380
+ case 'ç':
381
+ //eviç->evci, nesiç->nesci
382
+ formation = stem.substring(0, stem.length - 2) + 'c';
383
+ break;
384
+ }
385
+ } else {
386
+ //sarıağız->sarıağzı, zehir->zehri, zikir->zikri, nutuk->nutku, omuz->omzu, ömür->ömrü
387
+ //lütuf->lütfu, metin->metni, kavim->kavmi, kasıt->kastı
388
+ formation = stem.substring(0, stem.length - 2) + stem.charAt(stem.length - 1);
389
+ }
390
+ this.formationToCheck = stem;
391
+ } else {
392
+ switch (this.lastPhoneme(stem)) {
393
+ //---nounSoftenDuringSuffixation or verbSoftenDuringSuffixation
394
+ case 'p':
395
+ //adap->adabı, amip->amibi, azap->azabı, gazap->gazabı
396
+ if (this.startWithVowelorConsonantDrops() && rootWord && this.softenDuringSuffixation(root)) {
397
+ formation = stem.substring(0, stem.length - 1) + 'b';
398
+ }
399
+ break;
400
+ case 't':
401
+ //adet->adedi, akort->akordu, armut->armudu
402
+ //affet->affedi, yoket->yokedi, sabret->sabredi, rakset->raksedi
403
+ if (this.startWithVowelorConsonantDrops() && rootWord && this.softenDuringSuffixation(root)) {
404
+ formation = stem.substring(0, stem.length - 1) + 'd';
405
+ }
406
+ break;
407
+ case 'ç':
408
+ //ağaç->ağacı, almaç->almacı, akaç->akacı, avuç->avucu
409
+ if (this.startWithVowelorConsonantDrops() && rootWord && this.softenDuringSuffixation(root)) {
410
+ formation = stem.substring(0, stem.length - 1) + 'c';
411
+ }
412
+ break;
413
+ case 'g':
414
+ //arkeolog->arkeoloğu, filolog->filoloğu, minerolog->mineroloğu
415
+ if (this.startWithVowelorConsonantDrops() && rootWord && this.softenDuringSuffixation(root)) {
416
+ formation = stem.substring(0, stem.length - 1) + 'ğ';
417
+ }
418
+ break;
419
+ case 'k':
420
+ //ahenk->ahengi, künk->küngü, renk->rengi, pelesenk->pelesengi
421
+ if (this.startWithVowelorConsonantDrops() && rootWord && root.endingKChangesIntoG() &&
422
+ (!root.isProperNoun() || startState.toString() != "ProperRoot")) {
423
+ formation = stem.substring(0, stem.length - 1) + 'g';
424
+ } else {
425
+ //ablak->ablağı, küllük->küllüğü, kitaplık->kitaplığı, evcilik->evciliği
426
+ if (this.startWithVowelorConsonantDrops() && (!rootWord ||
427
+ (this.softenDuringSuffixation(root) && (!root.isProperNoun() ||
428
+ startState.toString() != "ProperRoot")))) {
429
+ formation = stem.substring(0, stem.length - 1) + 'ğ';
430
+ }
431
+ }
432
+ break;
433
+ }
434
+ this.formationToCheck = formation;
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+ if (TurkishLanguage.isConsonantDrop(this.withFirstChar()) &&
441
+ !TurkishLanguage.isVowel(stem.charAt(stem.length - 1)) &&
442
+ (root.isNumeral() || root.isReal() || root.isFraction() || root.isTime() || root.isDate() ||
443
+ root.isPercent() || root.isRange()) && (root.getName().endsWith("1") || root.getName().endsWith("3") ||
444
+ root.getName().endsWith("4") || root.getName().endsWith("5") || root.getName().endsWith("8") ||
445
+ root.getName().endsWith("9") || root.getName().endsWith("10") || root.getName().endsWith("30") ||
446
+ root.getName().endsWith("40") || root.getName().endsWith("60") || root.getName().endsWith("70") ||
447
+ root.getName().endsWith("80") || root.getName().endsWith("90") || root.getName().endsWith("00"))) {
448
+ if (this._with.charAt(0) == '\'') {
449
+ formation = formation + '\'';
450
+ i = 2;
451
+ } else {
452
+ i = 1;
453
+ }
454
+ } else {
455
+ if ((TurkishLanguage.isConsonantDrop(this.withFirstChar()) && TurkishLanguage.isConsonant(this.lastPhoneme(stem))) ||
456
+ (rootWord && root.consonantSMayInsertedDuringPossesiveSuffixation())) {
457
+ if (this._with.charAt(0) == '\'') {
458
+ formation = formation + '\'';
459
+ if (root.isAbbreviation())
460
+ i = 1;
461
+ else
462
+ i = 2;
463
+ } else {
464
+ i = 1;
465
+ }
466
+ }
467
+ }
468
+ for (; i < this._with.length; i++) {
469
+ switch (this._with.charAt(i)) {
470
+ case 'D':
471
+ formation = this.resolveD(root, formation);
472
+ break;
473
+ case 'A':
474
+ formation = this.resolveA(root, formation, rootWord);
475
+ break;
476
+ case 'H':
477
+ if (this._with.charAt(0) != '\'') {
478
+ formation = this.resolveH(root, formation, i == 0, this._with.startsWith("Hyor"), rootWord);
479
+ } else {
480
+ formation = this.resolveH(root, formation, i == 1, false, rootWord);
481
+ }
482
+ break;
483
+ case 'C':
484
+ formation = this.resolveC(formation);
485
+ break;
486
+ case 'S':
487
+ formation = this.resolveS(formation);
488
+ break;
489
+ case 'Ş':
490
+ formation = this.resolveSh(formation);
491
+ break;
492
+ default:
493
+ if (i == this._with.length - 1 && this._with.charAt(i) == 's') {
494
+ formation += 'ş';
495
+ } else {
496
+ formation += this._with.charAt(i);
497
+ }
498
+ }
499
+ this.formationToCheck = formation;
500
+ }
501
+ return formation;
502
+ }
503
+ }
504
+
505
+ private resolveD(root: TxtWord, formation: string): string{
506
+ if (root.isAbbreviation()) {
507
+ return formation + 'd';
508
+ }
509
+ if (this.lastPhoneme(this.formationToCheck) >= '0' && this.lastPhoneme(this.formationToCheck) <= '9') {
510
+ switch (this.lastPhoneme(this.formationToCheck)) {
511
+ case '3':
512
+ case '4':
513
+ case '5':
514
+ //3->3'tü, 5->5'ti, 4->4'tü
515
+ return formation + 't';
516
+ case '0':
517
+ if (root.getName().endsWith("40") || root.getName().endsWith("60") || root.getName().endsWith("70"))
518
+ //40->40'tı, 60->60'tı, 70->70'ti
519
+ return formation + 't';
520
+ else
521
+ //30->30'du, 50->50'ydi, 80->80'di
522
+ return formation + 'd';
523
+ default:
524
+ return formation + 'd';
525
+ }
526
+ } else {
527
+ if (TurkishLanguage.isSertSessiz(this.lastPhoneme(this.formationToCheck))) {
528
+ //yap+DH->yaptı
529
+ return formation + 't';
530
+ } else {
531
+ //sar+DH->sardı
532
+ return formation + 'd';
533
+ }
534
+ }
535
+ }
536
+
537
+ private resolveA(root: TxtWord, formation: string, rootWord: boolean): string{
538
+ if (root.isAbbreviation()) {
539
+ return formation + 'e';
540
+ }
541
+ if (this.lastVowel(this.formationToCheck) >= '0' && this.lastVowel(this.formationToCheck) <= '9') {
542
+ switch (this.lastVowel(this.formationToCheck)) {
543
+ case '6':
544
+ case '9':
545
+ //6'ya, 9'a
546
+ return formation + 'a';
547
+ case '0':
548
+ if (root.getName().endsWith("10") || root.getName().endsWith("30") || root.getName().endsWith("40") ||
549
+ root.getName().endsWith("60") || root.getName().endsWith("90"))
550
+ //10'a, 30'a, 40'a, 60'a, 90'a
551
+ return formation + 'a';
552
+ else
553
+ //20'ye, 50'ye, 80'e, 70'e
554
+ return formation + 'e';
555
+ default:
556
+ //3'e, 8'e, 4'e, 2'ye
557
+ return formation + 'e';
558
+ }
559
+ }
560
+ if (TurkishLanguage.isBackVowel(this.lastVowel(this.formationToCheck))) {
561
+ if (root.notObeysVowelHarmonyDuringAgglutination() && rootWord) {
562
+ //alkole, anormale
563
+ return formation + 'e';
564
+ } else {
565
+ //sakala, kabala
566
+ return formation + 'a';
567
+ }
568
+ }
569
+ if (TurkishLanguage.isFrontVowel(this.lastVowel(this.formationToCheck))) {
570
+ if (root.notObeysVowelHarmonyDuringAgglutination() && rootWord) {
571
+ //faika, halika
572
+ return formation + 'a';
573
+ } else {
574
+ //kediye, eve
575
+ return formation + 'e';
576
+ }
577
+ }
578
+ if (root.isNumeral() || root.isFraction() || root.isReal()) {
579
+ if (root.getName().endsWith("6") || root.getName().endsWith("9") || root.getName().endsWith("10") ||
580
+ root.getName().endsWith("30") || root.getName().endsWith("40") || root.getName().endsWith("60") ||
581
+ root.getName().endsWith("90")) {
582
+ return formation + 'a';
583
+ } else {
584
+ return formation + 'e';
585
+ }
586
+ }
587
+ return formation;
588
+ }
589
+
590
+ private resolveH(root: TxtWord, formation: string, beginningOfSuffix: boolean,
591
+ specialCaseTenseSuffix: boolean, rootWord: boolean): string{
592
+ if (root.isAbbreviation())
593
+ return formation + 'i';
594
+ if (beginningOfSuffix && TurkishLanguage.isVowel(this.lastPhoneme(this.formationToCheck)) &&
595
+ !specialCaseTenseSuffix) {
596
+ return formation;
597
+ }
598
+ if (specialCaseTenseSuffix) {
599
+ //eğer ek Hyor eki ise,
600
+ if (rootWord) {
601
+ if (root.vowelAChangesToIDuringYSuffixation()) {
602
+ if (TurkishLanguage.isFrontRoundedVowel(this.beforeLastVowel(this.formationToCheck))) {
603
+ //büyülüyor, bölümlüyor, çözümlüyor, döşüyor
604
+ return formation.substring(0, formation.length - 1) + 'ü';
605
+ }
606
+ if (TurkishLanguage.isFrontUnroundedVowel(this.beforeLastVowel(this.formationToCheck))) {
607
+ //adresliyor, alevliyor, ateşliyor, bekliyor
608
+ return formation.substring(0, formation.length - 1) + 'i';
609
+ }
610
+ if (TurkishLanguage.isBackRoundedVowel(this.beforeLastVowel(this.formationToCheck))) {
611
+ //buğuluyor, bulguluyor, çamurluyor, aforozluyor
612
+ return formation.substring(0, formation.length - 1) + 'u';
613
+ }
614
+ if (TurkishLanguage.isBackUnroundedVowel(this.beforeLastVowel(this.formationToCheck))) {
615
+ //açıklıyor, çalkalıyor, gazlıyor, gıcırdıyor
616
+ return formation.substring(0, formation.length - 1) + 'ı';
617
+ }
618
+ }
619
+ }
620
+ if (TurkishLanguage.isVowel(this.lastPhoneme(this.formationToCheck))) {
621
+ if (TurkishLanguage.isFrontRoundedVowel(this.beforeLastVowel(this.formationToCheck))) {
622
+ return formation.substring(0, formation.length - 1) + 'ü';
623
+ }
624
+ if (TurkishLanguage.isFrontUnroundedVowel(this.beforeLastVowel(this.formationToCheck))) {
625
+ return formation.substring(0, formation.length - 1) + 'i';
626
+ }
627
+ if (TurkishLanguage.isBackRoundedVowel(this.beforeLastVowel(this.formationToCheck))) {
628
+ return formation.substring(0, formation.length - 1) + 'u';
629
+ }
630
+ if (TurkishLanguage.isBackUnroundedVowel(this.beforeLastVowel(this.formationToCheck))) {
631
+ return formation.substring(0, formation.length - 1) + 'ı';
632
+ }
633
+ }
634
+ }
635
+ if (TurkishLanguage.isFrontRoundedVowel(this.lastVowel(this.formationToCheck)) ||
636
+ (TurkishLanguage.isBackRoundedVowel(this.lastVowel(this.formationToCheck)) && root.notObeysVowelHarmonyDuringAgglutination())) {
637
+ return formation + 'ü';
638
+ }
639
+ if (TurkishLanguage.isFrontUnroundedVowel(this.lastVowel(this.formationToCheck)) ||
640
+ (this.lastVowel(this.formationToCheck) == 'a' && root.notObeysVowelHarmonyDuringAgglutination())) {
641
+ return formation + 'i';
642
+ }
643
+ if (TurkishLanguage.isBackRoundedVowel(this.lastVowel(this.formationToCheck))) {
644
+ return formation + 'u';
645
+ }
646
+ if (TurkishLanguage.isBackUnroundedVowel(this.lastVowel(this.formationToCheck))) {
647
+ return formation + 'ı';
648
+ }
649
+ if (root.isNumeral() || root.isFraction() || root.isReal()) {
650
+ if (root.getName().endsWith("6") || root.getName().endsWith("40") || root.getName().endsWith("60") ||
651
+ root.getName().endsWith("90")) {
652
+ //6'yı, 40'ı, 60'ı
653
+ return formation + 'ı';
654
+ } else {
655
+ if (root.getName().endsWith("3") || root.getName().endsWith("4") || root.getName().endsWith("00")) {
656
+ //3'ü, 4'ü, 100'ü
657
+ return formation + 'ü';
658
+ } else {
659
+ if (root.getName().endsWith("9") || root.getName().endsWith("10") || root.getName().endsWith("30")) {
660
+ //9'u, 10'u, 30'u
661
+ return formation + 'u';
662
+ } else {
663
+ //2'yi, 5'i, 8'i
664
+ return formation + 'i';
665
+ }
666
+ }
667
+ }
668
+ }
669
+ return formation;
670
+ }
671
+
672
+ /**
673
+ * The resolveC method takes a {@link String} formation as an input. If the last phoneme is on of the "çfhkpsşt", it
674
+ * concatenates given formation with 'ç', if not it concatenates given formation with 'c'.
675
+ *
676
+ * @param formation {@link String} input.
677
+ * @return resolved String.
678
+ */
679
+ private resolveC(formation: string): string{
680
+ if (TurkishLanguage.isSertSessiz(this.lastPhoneme(this.formationToCheck))) {
681
+ return formation + 'ç';
682
+ } else {
683
+ return formation + 'c';
684
+ }
685
+ }
686
+
687
+ /**
688
+ * The resolveS method takes a {@link String} formation as an input. It then concatenates given formation with 's'.
689
+ *
690
+ * @param formation {@link String} input.
691
+ * @return resolved String.
692
+ */
693
+ private resolveS(formation: string): string{
694
+ return formation + 's';
695
+ }
696
+
697
+ /**
698
+ * The resolveSh method takes a {@link String} formation as an input. If the last character is a vowel, it concatenates
699
+ * given formation with ş, if the last character is not a vowel, and not 't' it directly returns given formation, but if it
700
+ * is equal to 't', it transforms it to 'd'.
701
+ *
702
+ * @param formation {@link String} input.
703
+ * @return resolved String.
704
+ */
705
+ private resolveSh(formation: string): string{
706
+ if (TurkishLanguage.isVowel(formation.charAt(formation.length - 1))) {
707
+ return formation + 'ş';
708
+ } else {
709
+ if (formation.charAt(formation.length - 1) != 't')
710
+ return formation;
711
+ else
712
+ return formation.substring(0, formation.length - 1) + 'd';
713
+ }
714
+ }
715
+
716
+ /**
717
+ * An overridden toString method which returns the with variable.
718
+ *
719
+ * @return with variable.
720
+ */
721
+ toString(): string{
722
+ return this._with
723
+ }
724
+
725
+ /**
726
+ * The with method returns the withName variable.
727
+ *
728
+ * @return the withName variable.
729
+ */
730
+ getWith(): string{
731
+ return this.withName
732
+ }
733
+ }