tdk-api-wrapper 1.4.0 → 1.5.1
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.
- package/README.md +43 -14
- package/dist/{chunk-5TYJDVHK.mjs → chunk-NEC5MIQM.mjs} +734 -24
- package/dist/cli.js +872 -29
- package/dist/cli.mjs +185 -6
- package/dist/index.d.mts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +738 -25
- package/dist/index.mjs +9 -3
- package/package.json +2 -2
- package/src/cli.ts +191 -5
- package/src/index.ts +1 -1
- package/src/morphology.ts +124 -5
- package/src/tdk.ts +762 -39
- package/src/types.ts +38 -0
- package/test/grammar.test.js +52 -0
- package/test/morphology.test.js +25 -1
- package/test/proofread.test.js +88 -0
- package/test/tools.test.js +51 -0
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TDK,
|
|
3
|
+
TDKClient,
|
|
3
4
|
TDKError,
|
|
4
5
|
TDKNetworkError,
|
|
5
6
|
TDKValidationError,
|
|
@@ -8,11 +9,14 @@ import {
|
|
|
8
9
|
getStemCandidates,
|
|
9
10
|
isVowel,
|
|
10
11
|
restoreConsonantSoftening,
|
|
12
|
+
restoreGemination,
|
|
11
13
|
restoreInfinitive,
|
|
12
|
-
restoreVowelDrop
|
|
13
|
-
|
|
14
|
+
restoreVowelDrop,
|
|
15
|
+
restoreVowelNarrowing
|
|
16
|
+
} from "./chunk-NEC5MIQM.mjs";
|
|
14
17
|
export {
|
|
15
18
|
TDK,
|
|
19
|
+
TDKClient,
|
|
16
20
|
TDKError,
|
|
17
21
|
TDKNetworkError,
|
|
18
22
|
TDKValidationError,
|
|
@@ -21,6 +25,8 @@ export {
|
|
|
21
25
|
getStemCandidates,
|
|
22
26
|
isVowel,
|
|
23
27
|
restoreConsonantSoftening,
|
|
28
|
+
restoreGemination,
|
|
24
29
|
restoreInfinitive,
|
|
25
|
-
restoreVowelDrop
|
|
30
|
+
restoreVowelDrop,
|
|
31
|
+
restoreVowelNarrowing
|
|
26
32
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tdk-api-wrapper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "TDK (Türk Dil Kurumu) unofficial live data API wrapper for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup src/index.ts src/cli.ts --format cjs,esm --dts --clean --shims",
|
|
20
|
-
"test": "node test/morphology.test.js"
|
|
20
|
+
"test": "node test/morphology.test.js && node test/grammar.test.js && node test/proofread.test.js && node test/tools.test.js"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"tdk",
|
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,17 @@ import { TDK } from "./tdk";
|
|
|
4
4
|
const rawArgs = process.argv.slice(2);
|
|
5
5
|
const jsonMode = rawArgs.includes("--json");
|
|
6
6
|
const args = rawArgs.filter((a) => a !== "--json");
|
|
7
|
+
|
|
8
|
+
const isColor = !jsonMode && Boolean(process.stdout.isTTY);
|
|
9
|
+
const c = {
|
|
10
|
+
bold: (s: string) => (isColor ? `\x1b[1m${s}\x1b[0m` : s),
|
|
11
|
+
dim: (s: string) => (isColor ? `\x1b[2m${s}\x1b[0m` : s),
|
|
12
|
+
green: (s: string) => (isColor ? `\x1b[32m${s}\x1b[0m` : s),
|
|
13
|
+
yellow: (s: string) => (isColor ? `\x1b[33m${s}\x1b[0m` : s),
|
|
14
|
+
cyan: (s: string) => (isColor ? `\x1b[36m${s}\x1b[0m` : s),
|
|
15
|
+
red: (s: string) => (isColor ? `\x1b[31m${s}\x1b[0m` : s),
|
|
16
|
+
};
|
|
17
|
+
|
|
7
18
|
const KNOWN_COMMANDS = new Set([
|
|
8
19
|
"ara",
|
|
9
20
|
"anlam",
|
|
@@ -11,6 +22,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
11
22
|
"ornek",
|
|
12
23
|
"hece",
|
|
13
24
|
"uyum",
|
|
25
|
+
"kucukuyum",
|
|
14
26
|
"yazim",
|
|
15
27
|
"kok",
|
|
16
28
|
"stem",
|
|
@@ -25,6 +37,14 @@ const KNOWN_COMMANDS = new Set([
|
|
|
25
37
|
"karsilastir",
|
|
26
38
|
"analiz",
|
|
27
39
|
"oneri",
|
|
40
|
+
"bulmaca",
|
|
41
|
+
"pattern",
|
|
42
|
+
"anagram",
|
|
43
|
+
"kafiye",
|
|
44
|
+
"rhyme",
|
|
45
|
+
"denetle",
|
|
46
|
+
"proofread",
|
|
47
|
+
"repl",
|
|
28
48
|
"kubbealti",
|
|
29
49
|
"nisanyan",
|
|
30
50
|
"viki",
|
|
@@ -33,7 +53,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
33
53
|
let command = args[0];
|
|
34
54
|
let word = args.slice(1).join(" ");
|
|
35
55
|
|
|
36
|
-
if (command && !KNOWN_COMMANDS.has(command) && command !== "--help" && command !== "-h") {
|
|
56
|
+
if (command && !KNOWN_COMMANDS.has(command) && command !== "--help" && command !== "-h" && command !== "--version" && command !== "-v") {
|
|
37
57
|
word = args.join(" ");
|
|
38
58
|
command = "anlam";
|
|
39
59
|
}
|
|
@@ -50,18 +70,101 @@ function printError(message: string) {
|
|
|
50
70
|
if (jsonMode) {
|
|
51
71
|
console.log(JSON.stringify({ error: message }));
|
|
52
72
|
} else {
|
|
53
|
-
console.log(`Hata: ${message}`);
|
|
73
|
+
console.log(c.red(`Hata: ${message}`));
|
|
54
74
|
}
|
|
55
75
|
}
|
|
56
76
|
|
|
77
|
+
async function startRepl() {
|
|
78
|
+
const readline = await import("node:readline");
|
|
79
|
+
const rl = readline.createInterface({
|
|
80
|
+
input: process.stdin,
|
|
81
|
+
output: process.stdout,
|
|
82
|
+
prompt: c.cyan("tdk> "),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log(c.bold("TDK İnteraktif Sözlük Kabuğu (Çıkmak için 'exit' veya Ctrl+C)"));
|
|
86
|
+
console.log(c.dim("Komutlar: ara <kelime>, hece <kelime>, bulmaca <desen>, denetle <metin> veya doğrudan kelime"));
|
|
87
|
+
rl.prompt();
|
|
88
|
+
|
|
89
|
+
rl.on("line", async (line) => {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (!trimmed) {
|
|
92
|
+
rl.prompt();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (trimmed === "exit" || trimmed === "quit" || trimmed === ".exit") {
|
|
96
|
+
rl.close();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const parts = trimmed.split(/\s+/);
|
|
101
|
+
let subCmd = parts[0].toLowerCase();
|
|
102
|
+
let subArg = parts.slice(1).join(" ");
|
|
103
|
+
if (!KNOWN_COMMANDS.has(subCmd)) {
|
|
104
|
+
subArg = trimmed;
|
|
105
|
+
subCmd = "anlam";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
if (subCmd === "ara" || subCmd === "anlam") {
|
|
110
|
+
const meanings = await TDK.getMeanings(subArg);
|
|
111
|
+
if (meanings.length === 0) console.log(c.dim("Sonuç bulunamadı."));
|
|
112
|
+
else meanings.forEach((m, i) => console.log(`${i + 1}. ${c.green(m)}`));
|
|
113
|
+
} else if (subCmd === "koken") {
|
|
114
|
+
const origin = await TDK.getOrigin(subArg);
|
|
115
|
+
console.log(`Köken: ${c.cyan(origin || "Bilinmiyor")}`);
|
|
116
|
+
} else if (subCmd === "hece") {
|
|
117
|
+
const s = TDK.syllabicate(subArg);
|
|
118
|
+
console.log(`Heceler: ${c.yellow(s.join("-"))}`);
|
|
119
|
+
} else if (subCmd === "uyum") {
|
|
120
|
+
const h = TDK.checkVowelHarmony(subArg);
|
|
121
|
+
console.log(`Büyük Ünlü Uyumu: ${h ? c.green("Uyar") : c.red("Uymaz")}`);
|
|
122
|
+
} else if (subCmd === "kucukuyum") {
|
|
123
|
+
const h = TDK.checkLabialHarmony(subArg);
|
|
124
|
+
console.log(`Küçük Ünlü Uyumu: ${h ? c.green("Uyar") : c.red("Uymaz")}`);
|
|
125
|
+
} else if (subCmd === "bulmaca" || subCmd === "pattern") {
|
|
126
|
+
const matches = await TDK.patternSearch(subArg);
|
|
127
|
+
console.log(matches.slice(0, 15).join(", "));
|
|
128
|
+
} else if (subCmd === "denetle" || subCmd === "proofread") {
|
|
129
|
+
const res = await TDK.proofread(subArg);
|
|
130
|
+
if (res.isCorrect) console.log(c.green("✓ Sorun bulunamadı."));
|
|
131
|
+
else res.issues.forEach((iss) => console.log(`- ${c.yellow(iss.word)}: ${iss.message}${iss.suggestion ? " -> " + c.green(iss.suggestion) : ""}`));
|
|
132
|
+
} else {
|
|
133
|
+
console.log(c.dim("Örnek komutlar: 'ara kalem', 'hece elektrik', 'bulmaca k_l_m', 'denetle Bugün evdeyim'"));
|
|
134
|
+
}
|
|
135
|
+
} catch (e: any) {
|
|
136
|
+
console.log(c.red(`Hata: ${e?.message || e}`));
|
|
137
|
+
}
|
|
138
|
+
rl.prompt();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
57
142
|
async function run() {
|
|
58
|
-
if (!command
|
|
143
|
+
if (!command) {
|
|
144
|
+
if (process.stdin.isTTY) {
|
|
145
|
+
await startRepl();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
59
148
|
console.log("Kullanım: tdk [komut] <kelime> [--json]");
|
|
60
149
|
console.log(
|
|
61
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, kok, deyim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, kubbealti, nisanyan, viki"
|
|
150
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, kucukuyum, yazim, kok, deyim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, bulmaca, anagram, kafiye, denetle, repl, kubbealti, nisanyan, viki"
|
|
62
151
|
);
|
|
63
152
|
console.log("Not: Komut belirtilmezse doğrudan kelime anlamı aranır (örn: tdk selam)");
|
|
64
|
-
process.exit(
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (command === "--version" || command === "-v") {
|
|
157
|
+
console.log("tdk-api-wrapper v1.5.1");
|
|
158
|
+
process.exit(0);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (command === "--help" || command === "-h") {
|
|
162
|
+
console.log("Kullanım: tdk [komut] <kelime> [--json]");
|
|
163
|
+
console.log(
|
|
164
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, kucukuyum, yazim, kok, deyim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, bulmaca, anagram, kafiye, denetle, repl, kubbealti, nisanyan, viki"
|
|
165
|
+
);
|
|
166
|
+
console.log("Not: Komut belirtilmezse doğrudan kelime anlamı aranır (örn: tdk selam)");
|
|
167
|
+
process.exit(0);
|
|
65
168
|
}
|
|
66
169
|
|
|
67
170
|
TDK.enableCache(false);
|
|
@@ -121,6 +224,16 @@ async function run() {
|
|
|
121
224
|
break;
|
|
122
225
|
}
|
|
123
226
|
|
|
227
|
+
case "kucukuyum":
|
|
228
|
+
case "labial": {
|
|
229
|
+
if (!word) throw new Error("Kelime belirtmelisiniz.");
|
|
230
|
+
const isHarmony = TDK.checkLabialHarmony(word);
|
|
231
|
+
printResult({ word, labialHarmony: isHarmony }, () =>
|
|
232
|
+
console.log(`Küçük Ünlü Uyumu: ${isHarmony ? "Uyar" : "Uymaz"}`)
|
|
233
|
+
);
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
124
237
|
case "yazim": {
|
|
125
238
|
if (!word) throw new Error("Kelime belirtmelisiniz.");
|
|
126
239
|
const spellResult = await TDK.checkSpelling(word);
|
|
@@ -299,6 +412,79 @@ async function run() {
|
|
|
299
412
|
break;
|
|
300
413
|
}
|
|
301
414
|
|
|
415
|
+
case "bulmaca":
|
|
416
|
+
case "pattern": {
|
|
417
|
+
if (!word) throw new Error("Desen belirtmelisiniz (örn: k_l_m).");
|
|
418
|
+
const matches = await TDK.patternSearch(word);
|
|
419
|
+
printResult(matches, () => {
|
|
420
|
+
if (matches.length === 0) {
|
|
421
|
+
console.log("Eşleşen kelime bulunamadı.");
|
|
422
|
+
} else {
|
|
423
|
+
console.log(c.bold(`Bulunan Kelimeler (${matches.length}):`));
|
|
424
|
+
matches.forEach((m, i) => console.log(`${i + 1}. ${c.cyan(m)}`));
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
case "anagram": {
|
|
431
|
+
if (!word) throw new Error("Harfler belirtmelisiniz.");
|
|
432
|
+
const anagrams = await TDK.findAnagrams(word);
|
|
433
|
+
printResult(anagrams, () => {
|
|
434
|
+
if (anagrams.length === 0) {
|
|
435
|
+
console.log("Anagram veya bu harflerle türetilebilecek kelime bulunamadı.");
|
|
436
|
+
} else {
|
|
437
|
+
const clean = word.trim().toLocaleLowerCase("tr-TR").replace(/[^a-zçğıöşüâîû]/gi, "");
|
|
438
|
+
const hasExact = anagrams.some((a) => a.length === clean.length);
|
|
439
|
+
const title = hasExact
|
|
440
|
+
? `Anagramlar (${anagrams.length}):`
|
|
441
|
+
: `Birebir anagram bulunamadı. Bu harflerle türetilen kelimeler (${anagrams.length}):`;
|
|
442
|
+
console.log(c.bold(title));
|
|
443
|
+
anagrams.forEach((a, i) => console.log(`${i + 1}. ${c.green(a)} ${c.dim(`(${a.length} harf)`)}`));
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
case "kafiye":
|
|
450
|
+
case "rhyme": {
|
|
451
|
+
if (!word) throw new Error("Kelime belirtmelisiniz.");
|
|
452
|
+
const rhymes = await TDK.findRhymes(word);
|
|
453
|
+
printResult(rhymes, () => {
|
|
454
|
+
if (rhymes.length === 0) {
|
|
455
|
+
console.log("Kafiye bulunamadı.");
|
|
456
|
+
} else {
|
|
457
|
+
console.log(c.bold(`Kafiyeli Kelimeler (${rhymes.length}):`));
|
|
458
|
+
rhymes.forEach((r, i) => console.log(`${i + 1}. ${c.yellow(r)}`));
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
case "denetle":
|
|
465
|
+
case "proofread": {
|
|
466
|
+
if (!word) throw new Error("Metin belirtmelisiniz.");
|
|
467
|
+
const result = await TDK.proofread(word);
|
|
468
|
+
printResult(result, () => {
|
|
469
|
+
if (result.isCorrect) {
|
|
470
|
+
console.log(c.green("✓ Metinde imla veya bağlaç hatası tespit edilmedi."));
|
|
471
|
+
} else {
|
|
472
|
+
console.log(c.bold(c.red(`Metinde ${result.issues.length} olası sorun tespit edildi:`)));
|
|
473
|
+
result.issues.forEach((issue, i) => {
|
|
474
|
+
const label = c.yellow(`[${issue.type}]`);
|
|
475
|
+
const sug = issue.suggestion ? c.green(` -> Öneri: ${issue.suggestion}`) : "";
|
|
476
|
+
console.log(`${i + 1}. ${label} "${c.bold(issue.word)}": ${issue.message}${sug}`);
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case "repl": {
|
|
484
|
+
await startRepl();
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
|
|
302
488
|
case "kubbealti": {
|
|
303
489
|
if (!word) throw new Error("Kelime belirtmelisiniz.");
|
|
304
490
|
const meanings = await TDK.getKubbealtiMeanings(word);
|
package/src/index.ts
CHANGED
package/src/morphology.ts
CHANGED
|
@@ -67,7 +67,8 @@ export const TURKISH_SUFFIXES: readonly string[] = [
|
|
|
67
67
|
"miş", "mış", "muş", "müş", "dim", "dım", "dum", "düm", "tim", "tım", "tum", "tüm",
|
|
68
68
|
"din", "dın", "dun", "dün", "tin", "tın", "tun", "tün", "dik", "dık", "duk", "dük",
|
|
69
69
|
"tik", "tık", "tuk", "tük", "ydi", "ydı", "ydu", "ydü", "yim", "yım", "yum", "yüm",
|
|
70
|
-
"sin", "sın", "sun", "sün", "
|
|
70
|
+
"sin", "sın", "sun", "sün", "sen", "san", "sem", "sam", "sek", "sak",
|
|
71
|
+
"siz", "sız", "suz", "süz", "lik", "lık", "luk", "lük",
|
|
71
72
|
"ici", "ıcı", "ucu", "ücü", "gen", "gan", "ken", "kan",
|
|
72
73
|
"len", "lan", "leş", "laş", "mek", "mak", "yor",
|
|
73
74
|
// 2-letter suffixes
|
|
@@ -126,6 +127,61 @@ export function restoreVowelDrop(stem: string): string[] {
|
|
|
126
127
|
return [];
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Reverses Turkish consonant gemination (ünsüz türemesi / ikizleşmesi):
|
|
132
|
+
* In words of Arabic/foreign origin, when receiving a vowel-initial suffix, the final consonant doubles:
|
|
133
|
+
* e.g. hak->hakkı, his->hissi, sır->sırrı, af->affı, ret->reddi, tıp->tıbbı, zam->zammı, hat->hattı.
|
|
134
|
+
* Restores the single consonant form and checks consonant softening on the result (e.g. redd -> red -> ret).
|
|
135
|
+
*/
|
|
136
|
+
export function restoreGemination(stem: string): string[] {
|
|
137
|
+
if (stem.length < 3) return [];
|
|
138
|
+
const c1 = stem[stem.length - 2];
|
|
139
|
+
const c2 = stem[stem.length - 1];
|
|
140
|
+
if (c1 === c2 && !isVowel(c1)) {
|
|
141
|
+
const single = stem.slice(0, -1);
|
|
142
|
+
const hardened = restoreConsonantSoftening(single);
|
|
143
|
+
return [single, ...hardened];
|
|
144
|
+
}
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Reverses Turkish vowel narrowing (ünlü daralması):
|
|
150
|
+
* Verbs ending in wide vowels 'a' or 'e' narrow to 'ı', 'i', 'u', 'ü' before the continuous tense suffix -yor:
|
|
151
|
+
* e.g. başla-yor -> başlıyor, bekle-yor -> bekliyor, özle-yor -> özlüyor, anla-yor -> anlıyor.
|
|
152
|
+
* Also handles irregular monosyllabic verbs: de-yor -> diyor, ye-yor -> yiyor.
|
|
153
|
+
*/
|
|
154
|
+
export function restoreVowelNarrowing(stem: string): string[] {
|
|
155
|
+
if (stem.length < 2) return [];
|
|
156
|
+
|
|
157
|
+
// Irregular monosyllabic verbs
|
|
158
|
+
if (stem === "di") return ["de"];
|
|
159
|
+
if (stem === "yi") return ["ye"];
|
|
160
|
+
|
|
161
|
+
const lastChar = stem[stem.length - 1];
|
|
162
|
+
const isLastNarrow = "ıiuü".includes(lastChar);
|
|
163
|
+
|
|
164
|
+
// Case 1: stem ends with narrow vowel (e.g. başlı, bekli, özlü, kutlu)
|
|
165
|
+
if (isLastNarrow) {
|
|
166
|
+
const vowelsInBase = stem.slice(0, -1).split("").filter(isVowel);
|
|
167
|
+
const lastVowel = vowelsInBase.length > 0 ? vowelsInBase[vowelsInBase.length - 1] : lastChar;
|
|
168
|
+
const widened = "aıou".includes(lastVowel) ? "a" : "e";
|
|
169
|
+
return [stem.slice(0, -1) + widened];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Case 2: stem ends with consonant (e.g. başlıyor stripped by -ıyor -> stem: başl)
|
|
173
|
+
if (!isVowel(lastChar)) {
|
|
174
|
+
const vowelsInBase = stem.split("").filter(isVowel);
|
|
175
|
+
if (vowelsInBase.length > 0) {
|
|
176
|
+
const lastVowel = vowelsInBase[vowelsInBase.length - 1];
|
|
177
|
+
const widened = "aıou".includes(lastVowel) ? "a" : "e";
|
|
178
|
+
return [stem + widened];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
|
|
129
185
|
/**
|
|
130
186
|
* Restores verb infinitive headword form (-mek / -mak):
|
|
131
187
|
* Since TDK registers verbs in their infinitive form (e.g. okumak, gelmek, yazmak),
|
|
@@ -174,6 +230,15 @@ export function getStemCandidates(
|
|
|
174
230
|
}
|
|
175
231
|
}
|
|
176
232
|
|
|
233
|
+
// Bare verb imperative candidates (e.g. "söyle" -> "söylemek", "oku" -> "okumak")
|
|
234
|
+
const bareInfinitives = restoreInfinitive(normalized);
|
|
235
|
+
for (const inf of bareInfinitives) {
|
|
236
|
+
if (!seen.has(inf) && inf !== normalized) {
|
|
237
|
+
seen.add(inf);
|
|
238
|
+
candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
177
242
|
let frontier = [normalized];
|
|
178
243
|
|
|
179
244
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
@@ -186,11 +251,55 @@ export function getStemCandidates(
|
|
|
186
251
|
|
|
187
252
|
const hardened = restoreConsonantSoftening(stem);
|
|
188
253
|
const vowelDropped = restoreVowelDrop(stem);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
254
|
+
const geminated = restoreGemination(stem);
|
|
255
|
+
|
|
256
|
+
// Vowel narrowing (ünlü daralması) in Turkish strictly occurs with continuous tense (-yor)
|
|
257
|
+
// or with the monosyllabic verbs de-/ye- before buffer 'y' (diye, yiyen).
|
|
258
|
+
// Restricting narrowing to these suffixes prevents false-positive stems on other suffixes.
|
|
259
|
+
const isNarrowingSuffix =
|
|
260
|
+
suffix.startsWith("yor") ||
|
|
261
|
+
suffix.includes("iyor") ||
|
|
262
|
+
suffix.includes("ıyor") ||
|
|
263
|
+
suffix.includes("uyor") ||
|
|
264
|
+
suffix.includes("üyor");
|
|
265
|
+
|
|
266
|
+
const isDeYeBuffer = (stem === "di" || stem === "yi") && suffix.startsWith("y");
|
|
267
|
+
const narrowed = isNarrowingSuffix || isDeYeBuffer ? restoreVowelNarrowing(stem) : [];
|
|
268
|
+
|
|
269
|
+
// Suffix indicator for verbs: -yor, -ecek, -miş, -di, etc.
|
|
270
|
+
const isVerbSuffix =
|
|
271
|
+
isNarrowingSuffix ||
|
|
272
|
+
suffix.includes("ecek") ||
|
|
273
|
+
suffix.includes("acak") ||
|
|
274
|
+
suffix.includes("miş") ||
|
|
275
|
+
suffix.includes("mış") ||
|
|
276
|
+
suffix.includes("müş") ||
|
|
277
|
+
suffix.includes("muş") ||
|
|
278
|
+
suffix.includes("mek") ||
|
|
279
|
+
suffix.includes("mak") ||
|
|
280
|
+
suffix.includes("erek") ||
|
|
281
|
+
suffix.includes("arak") ||
|
|
282
|
+
suffix.includes("dik") ||
|
|
283
|
+
suffix.includes("dık") ||
|
|
284
|
+
suffix.includes("duk") ||
|
|
285
|
+
suffix.includes("dük") ||
|
|
286
|
+
suffix.includes("tik") ||
|
|
287
|
+
suffix.includes("tık") ||
|
|
288
|
+
suffix.includes("tuk") ||
|
|
289
|
+
suffix.includes("tük") ||
|
|
290
|
+
suffix.includes("sen") ||
|
|
291
|
+
suffix.includes("san") ||
|
|
292
|
+
suffix.includes("sem") ||
|
|
293
|
+
suffix.includes("sam") ||
|
|
294
|
+
suffix.includes("sek") ||
|
|
295
|
+
suffix.includes("sak");
|
|
296
|
+
|
|
297
|
+
// Infinitives apply to direct stems, hardened stems, and widened stems (e.g. başlı -> başla -> başlamak)
|
|
298
|
+
const verbalBases = [stem, ...hardened, ...narrowed];
|
|
192
299
|
const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
|
|
193
|
-
|
|
300
|
+
|
|
301
|
+
// Base candidates
|
|
302
|
+
const variants = [stem, ...hardened, ...vowelDropped, ...geminated, ...narrowed];
|
|
194
303
|
|
|
195
304
|
for (const variant of variants) {
|
|
196
305
|
if (!seen.has(variant) && variant !== normalized) {
|
|
@@ -199,6 +308,16 @@ export function getStemCandidates(
|
|
|
199
308
|
candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
|
|
200
309
|
}
|
|
201
310
|
}
|
|
311
|
+
|
|
312
|
+
// Push infinitives with high priority if a verbal suffix matched, preventing noun false-positives
|
|
313
|
+
for (const inf of infinitives) {
|
|
314
|
+
if (!seen.has(inf) && inf !== normalized) {
|
|
315
|
+
seen.add(inf);
|
|
316
|
+
nextFrontier.push(inf);
|
|
317
|
+
const weight = isVerbSuffix ? stem.length + 5 : stem.length;
|
|
318
|
+
candidatesWithWeight.push({ candidate: inf, baseLength: weight });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
202
321
|
}
|
|
203
322
|
}
|
|
204
323
|
}
|