├── .eslintrc.js ├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── index.js ├── kefir ├── case.js ├── functional.js ├── phonology.js ├── predication.js ├── pronoun.js ├── subject.js └── suffix.js ├── package-lock.json ├── package.json └── test └── test.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'unu', 3 | rules: { 4 | 'no-console': 'off' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | node_modules 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Run AVA test", 11 | "program": "${workspaceRoot}/node_modules/ava/profile.js", 12 | "args": [ 13 | "${workspaceFolder}/test/test.js" 14 | ], 15 | "skipFiles": [ 16 | "/**/*.js" 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yogurt-cultures 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | kefir 3 | 4 | # Kefir 5 | 6 | ![Logo](https://avatars1.githubusercontent.com/u/42190640?s=200&v=4) 7 | 8 | Yogurt is a free software community that establised in `Mustafa Akgul Ozgul Yazılım Kampı` in 2018. 9 | 10 | Kefir is a natural language processing kit for Turkic languages, and maybe Finnish and Hungarian in phonology. 11 | 12 | # Credits 13 | 14 | - Berk Buzcu (8-bit artwork) 15 | - Serdar Açıkyol (Illustration) 16 | - Fatih Erikli (Phonological processes, Predicate Logic) 17 | - Armagan Amcalar (JavaScript port) 18 | 19 | # Contribution guide 20 | - Don't load a fixture, code is our data. 21 | - Don't leave a comment! Docstrings are only for the context and test. 22 | - Be nice 🦄 23 | 24 | # How to use 25 | 26 | There are two competing notions of the predicate in theories of grammar. 27 | The competition between these two concepts has generated confusion concerning 28 | the use of the term predicate in theories of grammar. 29 | 30 | Kefir is designed to construct sentences by predicate-logic. 31 | https://www.wikiwand.com/en/Predicate_(grammar) 32 | 33 | ```python 34 | >>> ayni = subject('aynı') 35 | >>> havuc = subject('havuç') 36 | >>> gel = predicate('gel', 'third', 'perfective') 37 | >>> yap = predicate('yap', 'third', 'perfective') 38 | >>> dal = predicate('dal', 'third', 'progressive') 39 | >>> dal = predicate(dal, 'third', 'perfective') 40 | 41 | >>> birisi = subject('yakup') 42 | >>> [sentence(birisi, eylem) for eylem in (yap, dal,)] 43 | ['yakup yaptı', 'yakup dalmaktaydı'] 44 | 45 | >>> [sentence(havuc, eylem) for eylem in (gel, yap, dal)] 46 | ['havuç geldi', 'havuç yaptı', 'havuç dalmaktaydı'] 47 | 48 | >>> sebze = predicate(locative('marul'), 'first', 'perfective', True) 49 | >>> dal = predicate(locative('dal'), 'first', 'perfective', True) 50 | >>> [sentence(ayni, eylem) for eylem in (sebze, dal)] 51 | ['aynı maruldaydık', 'aynı daldaydık'] 52 | 53 | ``` 54 | 55 | ### cases 56 | 57 | - nominative 58 | - genitive 59 | - dative 60 | - accusative 61 | - ablative 62 | - locative 63 | 64 | ### copulas 65 | 66 | - negative 67 | - zero 68 | - tobe 69 | - personal 70 | - perfective 71 | - imperfective 72 | - progressive 73 | - necessitative 74 | - future 75 | - impotential 76 | - conditional 77 | 78 |
79 |
80 | kefir.case 81 | 82 | # Grammatical Cases 83 | 84 | Implemented only six grammatical cases. 85 | 86 | - Nominative 87 | - Genitive 88 | - Dative 89 | - Accusative 90 | - Ablative 91 | - Locative 92 | 93 | Turkish has 9 more cases. 94 | 95 | - Essive 96 | - Instrumental 97 | - Inclusive 98 | - Abessive 99 | - Likeness 100 | - Coverage 101 | - Qualitative 102 | - Conditional 103 | - Possesive 104 | 105 | Detailed explaination: 106 | https://en.wikibooks.org/wiki/Turkish/Cases 107 | 108 | TODO: Enum'lardaki rakamlar yerine auto() kullanılmalı. 109 | 110 | ## nominative case (yalın in turkish) 111 | the simplest grammatical case, there's no suffix to 112 | affix in that case. 113 | 114 | nominative comes from latin cāsus nominātīvus 115 | means case for naming. 116 | 117 | 118 | ## ablative case (ayrılma in turkish) 119 | a grammatical case for nouns, pronouns and adjectives in 120 | the grammar of various languages; it is sometimes used to 121 | express motion away from something, among other uses. 122 | 123 | ✎︎ examples 124 | ``` 125 | adalar[dan] geldim 126 | merkez[den] geçtim 127 | teyit[ten] geçtim 128 | açlık[tan] öldüm 129 | ``` 130 | 131 | 132 | ## accusative (ilgi in turkish) 133 | The accusative case (abbreviated acc) of a noun is the 134 | grammatical case used to mark the direct object of a 135 | transitive verb. The same case is used in many 136 | languages for the objects of (some or all) prepositions. 137 | 138 | ✎︎ examples 139 | ``` 140 | aday[ı] yedim 141 | evim[i] yaptım 142 | üzüm[ü] pişirdim 143 | ``` 144 | 145 | 146 | ## genitive case (genitifler in turkish) 147 | In grammar, the genitive is the grammatical case 148 | that marks a word, usually a noun, as modifying 149 | another word, also usually a noun. 150 | 151 | ✎︎ examples 152 | ``` 153 | hanımelinin çiçeği (flower of a plant called hanımeli) 154 | kadının ayakkabısı (shoes of the woman) 155 | باب بيت bābu baytin (the door of a house) 156 | mari[i] nie ma w domu (maria is not at home) 157 | ``` 158 | 159 | 160 | ## dative case (yönelme in turkish) 161 | In some languages, the dative is used to mark the 162 | indirect object of a sentence. 163 | 164 | ✎︎ examples 165 | ``` 166 | marya yakup'a bir drink verdi (maria gave jacob a drink) 167 | maria jacobī potum dedit (maria gave jacob a drink) 168 | ``` 169 | 170 | 171 | ## locative case (bulunma in turkish) 172 | Locative is a grammatical case which indicates a location. 173 | It corresponds vaguely to the English prepositions "in", 174 | "on", "at", and "by". 175 | 176 | ✎︎ examples 177 | ``` 178 | bahçe[de] hanımeli var. 179 | yorum[da] iyi beatler var. 180 | kalem[de] güzel uç var. 181 | ``` 182 | 183 |
184 |
185 | kefir.phonology 186 | 187 | # Turkish phonology 188 | 189 | In Hungarian, Finnish, and Turkic languages 190 | vowel sounds are organized in a concept called 191 | vowel harmony. Vowels may be classified as Back 192 | or Front vowels, based on the placement of the 193 | sound in the mouth. 194 | 195 | ``` 196 | Front Vowels 197 | +----------------+ 198 | Unrounded ⟨e⟩ ⟨i⟩ 199 | Rounded ⟨ü⟩ ⟨ö⟩ 200 | 201 | Back Vowels 202 | +----------------+ 203 | Unrounded ⟨a⟩ ⟨ı⟩ 204 | Rounded ⟨u⟩ ⟨o⟩ 205 | ``` 206 | 207 | TODO: Document consonant harmony. 208 | 209 | #### swap_front_and_back 210 | Swaps front sounds to back, and vice versa 211 | 212 | ```python 213 | >>> swap_front_and_back('acak') 214 | 'ecek' 215 | 216 | >>> swap_front_and_back('ocok') 217 | 'öcök' 218 | 219 | >>> swap_front_and_back('öcök') 220 | 'ocok' 221 | 222 | >>> swap_front_and_back('acak') 223 | 'ecek' 224 | 225 | ``` 226 | 227 | 228 | ## Voicing or sonorization (yumuşama in turkish) 229 | to make pronouncation easier, nouns ending 230 | with these sounds. 231 | 232 | ``` 233 | ⟨p⟩ ⟨ç⟩ ⟨t⟩ ⟨k⟩ 234 | ``` 235 | 236 | may be softened by replacing them in order: 237 | 238 | ``` 239 | ⟨b⟩ ⟨c⟩ ⟨d⟩ ⟨ğ⟩ 240 | ``` 241 | 242 | ✎︎ examples 243 | ``` 244 | ço⟨p⟩un → ço⟨b⟩un 245 | ağa⟨ç⟩ın → ağa⟨c⟩n 246 | kağı⟨t⟩ın → kağı⟨d⟩ın 247 | ren⟨k⟩in → ren⟨g⟩in 248 | ``` 249 | 250 | ✎︎ examples in other languages 251 | ``` 252 | li⟨f⟩e → li⟨v⟩e 253 | stri⟨f⟩e → stri⟨v⟩e 254 | proo⟨f⟩ → pro⟨v⟩e 255 | ``` 256 | 257 | 258 | ## Devoicing or desonorization (sertleşme in turkish) 259 | to make pronouncation easier, nouns ending with 260 | these sounds: 261 | ``` 262 | ⟨p⟩ ⟨ç⟩ ⟨t⟩ ⟨k⟩ 263 | ``` 264 | 265 | may be hardened by replacing them in order: 266 | ``` 267 | ⟨b⟩ ⟨c⟩ ⟨d⟩ ⟨ğ⟩ 268 | ``` 269 | 270 | ✎︎ examples 271 | ``` 272 | ço⟨p⟩un → ço⟨b⟩un 273 | ağa⟨ç⟩ın → ağa⟨c⟩n 274 | kağı⟨t⟩ın → kağı⟨d⟩ın 275 | ren⟨k⟩in → ren⟨g⟩in 276 | ``` 277 | 278 | ✎︎ examples in other languages 279 | ``` 280 | dogs → dogs ([ɡz]) 281 | missed → missed ([st]) 282 | whizzed → whizzed ([zd]) 283 | prośba → prɔʑba 284 | просьба → prozʲbə 285 | ``` 286 | 287 |
288 |
289 | kefir.predication 290 | 291 | # Turkish Predication and Copula 292 | 293 | turkish language copulas, which are called as ek-eylem which 294 | literally means 'suffix-verb' are one of the most distinct 295 | features of turkish grammar. 296 | 297 | TODO: Remove unused imports. 298 | 299 | #### zero copula 300 | is the rule for third person, as in hungarian 301 | and russian. that means two nouns, or a noun and an 302 | adjective can be juxtaposed to make a sentence without 303 | using any copula. third person plural might be indicated 304 | with the use of plural suffix "-lar/-ler". 305 | 306 | ✎︎ examples 307 | ``` 308 | yogurt kültür (yogurt [is-a] culture) 309 | abbas yolcu (abbas [is-a] traveller) 310 | evlerinin önü yonca (the front of their home [is-a] plant called yonca) 311 | ``` 312 | 313 | ✎︎ tests 314 | ```python 315 | >>> zero('yolcu') 316 | 'yolcu' 317 | 318 | ``` 319 | 320 | 321 | #### negative 322 | negation is indicated by the negative copula değil. 323 | değil is never used as a suffix, but it takes suffixes 324 | according to context. 325 | 326 | ✎︎ examples 327 | ``` 328 | yogurt kültür değildir (yogurt [is-not-a] culture) 329 | abbas yolcu değildir (abbas [is-not-a] traveller) 330 | evlerinin önü yonca değildir (the front of their home [is-not-a] yonca) 331 | ``` 332 | 333 | ✎︎ tests 334 | ```python 335 | >>> negative('yolcu') 336 | 'yolcu değil' 337 | 338 | ``` 339 | 340 | 341 | ### tobe 342 | turkish "to be" as regular/auxiliary verb (olmak). 343 | 344 | ✎︎ examples 345 | ``` 346 | yogurt kültürdür (yogurt [is] culture) 347 | abbas yolcudur (abbas [is] traveller) 348 | evlerinin önü yoncadır (the front of their home [is] plant called yonca) 349 | ``` 350 | 351 | ✎︎ tests 352 | ```python 353 | >>> tobe('yolcu') 354 | 'yolcudur' 355 | >>> tobe('üzüm') 356 | 'üzümdür' 357 | >>> tobe('yonca') 358 | 'yoncadır' 359 | 360 | ``` 361 | 362 | 363 | ### personification copula 364 | 365 | ✎︎ examples 366 | ``` 367 | ben buralıyım (i'm from here) 368 | sen oralısın (you're from over there) 369 | aynı gezegenliyiz (we're from same planet) 370 | ``` 371 | 372 | ✎︎ tests 373 | ```python 374 | >>> personal('uçak', Person.FIRST, is_plural=False) 375 | 'uçağım' 376 | 377 | >>> personal('oralı', Person.SECOND, is_plural=False) 378 | 'oralısın' 379 | 380 | >>> personal('gezegenli', Person.FIRST, is_plural=True) 381 | 'gezegenliyiz' 382 | 383 | ``` 384 | 385 | 386 | ### inferential mood (-miş in turkish) 387 | it is used to convey information about events 388 | which were not directly observed or were inferred by the speaker. 389 | 390 | ✎︎ examples 391 | ``` 392 | elmaymışım (i was an apple as i've heard) 393 | üzülmüşsün (you were sad as i've heard) 394 | doktormuş (he/she/it was a doctor as i've heard) 395 | üzümmüşsün (you were a grape as i've heard) 396 | ``` 397 | 398 | ✎︎ tests 399 | ```python 400 | >>> inferential('öğretmen', Person.SECOND, is_plural=False) 401 | 'öğretmenmişsin' 402 | 403 | >>> inferential('üzül', Person.SECOND, is_plural=False) 404 | 'üzülmüşsün' 405 | 406 | >>> inferential('robot', Person.FIRST, is_plural=False) 407 | 'robotmuşum' 408 | 409 | >>> inferential('robot', Person.THIRD, is_plural=False) 410 | 'robotmuş' 411 | 412 | >>> inferential('ada', Person.THIRD, is_plural=False) 413 | 'adaymış' 414 | 415 | ``` 416 | 417 | 418 | ### inferential mood (-isem in turkish) 419 | It is a grammatical mood used to express a proposition whose 420 | validity is dependent on some condition, possibly counterfactual. 421 | 422 | ✎︎ examples 423 | ``` 424 | elmaysam (if i am an apple) 425 | üzümsen (if you are a grape) 426 | bıçaklarsa (if they are a knife) 427 | ``` 428 | 429 | ✎︎ tests 430 | ```python 431 | >>> conditional('elma', Person.FIRST, is_plural=False) 432 | 'elmaysam' 433 | >>> conditional('üzüm', Person.SECOND, is_plural=False) 434 | 'üzümsen' 435 | >>> conditional('bıçak', Person.THIRD, is_plural=True) 436 | 'bıçaklarsa' 437 | 438 | ``` 439 | 440 | 441 | ### alethic modality (-idi in turkish) 442 | linguistic modality that indicates modalities of truth, 443 | in particular the modalities of logical necessity, 444 | possibility or impossibility. 445 | 446 | ✎︎ examples 447 | ``` 448 | elmaydım (i was an apple) 449 | üzümdün (you were a grape) 450 | doktordu (he/she/it was a doctor) 451 | ``` 452 | 453 | ✎︎ tests 454 | ```python 455 | >>> perfective('açık', Person.FIRST, is_plural=False) 456 | 'açıktım' 457 | 458 | >>> perfective('oralı', Person.SECOND, is_plural=False) 459 | 'oralıydın' 460 | 461 | >>> perfective('dalda', Person.FIRST, is_plural=False) 462 | 'daldaydım' 463 | 464 | >>> perfective('dalda', Person.THIRD, is_plural=False) 465 | 'daldaydı' 466 | 467 | >>> perfective('dalda', Person.FIRST, is_plural=True) 468 | 'daldaydık' 469 | 470 | >>> perfective('dalda', Person.SECOND, is_plural=True) 471 | 'daldaydınız' 472 | 473 | >>> perfective('dalda', Person.THIRD, is_plural=True) 474 | 'daldaydılar' 475 | 476 | >>> perfective('gezegende', Person.THIRD, is_plural=True) 477 | 'gezegendeydiler' 478 | 479 | ``` 480 | 481 | 482 | ### the imperfective (-iyor in turkish) 483 | grammatical aspect used to describe a situation viewed with interior composition. 484 | describes ongoing, habitual, repeated, or similar semantic roles, 485 | whether that situation occurs in the past, present, or future. 486 | 487 | ✎︎ examples 488 | ``` 489 | gidiyorum (i'm going) 490 | kayıyor (he's skating) 491 | üzümlüyor (he's graping) 492 | ``` 493 | 494 | ✎︎ tests 495 | ```python 496 | >>> imperfective('açı', Person.FIRST, is_plural=False) 497 | 'açıyorum' 498 | 499 | >>> imperfective('açık', Person.FIRST, is_plural=False) 500 | 'açıkıyorum' 501 | 502 | >>> imperfective('oralı', Person.SECOND, is_plural=False) 503 | 'oralıyorsun' 504 | 505 | >>> imperfective('dal', Person.THIRD, is_plural=False) 506 | 'dalıyor' 507 | 508 | >>> imperfective('dal', Person.FIRST, is_plural=True) 509 | 'dalıyoruz' 510 | 511 | >>> imperfective('dal', Person.FIRST, is_plural=True) 512 | 'dalıyoruz' 513 | 514 | >>> imperfective('dal', Person.SECOND, is_plural=True) 515 | 'dalıyorsunuz' 516 | 517 | >>> imperfective('dal', Person.THIRD, is_plural=True) 518 | 'dalıyorlar' 519 | 520 | ``` 521 | 522 | 523 | ### the future tense (-iyor in turkish) 524 | is a verb form that generally marks the event described by the verb as not 525 | having happened yet, but expected to happen in the future. 526 | 527 | ✎︎ examples 528 | ``` 529 | gidecek (he'll go) 530 | ölecek (he'll die) 531 | can alacak (he'll kill someone) 532 | ``` 533 | 534 | ✎︎ tests 535 | ```python 536 | >>> future('gel', Person.FIRST, is_plural=False) 537 | 'geleceğim' 538 | 539 | >>> future('açık', Person.FIRST, is_plural=False) 540 | 'açıkacağım' 541 | 542 | >>> future('gel', Person.FIRST, is_plural=True) 543 | 'geleceğiz' 544 | 545 | ``` 546 | 547 | 548 | ### progressive tense 549 | 550 | ✎︎ examples 551 | gülmekteyim (i am in the process of laughing) 552 | ölmekteler (they are in the process of dying 👾) 553 | 554 | ✎︎ tests 555 | ```python 556 | >>> progressive('gel', Person.FIRST, is_plural=False) 557 | 'gelmekteyim' 558 | 559 | >>> progressive('açık', Person.FIRST, is_plural=False) 560 | 'açıkmaktayım' 561 | 562 | >>> progressive('gel', Person.FIRST, is_plural=True) 563 | 'gelmekteyiz' 564 | 565 | ``` 566 | 567 | 568 | ### necessitative copula 569 | 570 | ✎︎ examples 571 | ``` 572 | gitmeliyim (i must go) 573 | kaçmalıyım (i must run away) 574 | ``` 575 | 576 | ✎︎ tests 577 | ```python 578 | >>> necessitative('git', Person.FIRST, is_plural=False) 579 | 'gitmeliyim' 580 | 581 | >>> necessitative('açık', Person.FIRST, is_plural=False) 582 | 'açıkmalıyım' 583 | 584 | >>> necessitative('uza', Person.FIRST, is_plural=True) 585 | 'uzamalıyız' 586 | 587 | ``` 588 | 589 | 590 | ### impotential copula 591 | 592 | ✎︎ examples 593 | ``` 594 | gidemem (i cannot come) 595 | kaçamayız (we cannot run away) 596 | ``` 597 | 598 | ✎︎ tests 599 | ```python 600 | >>> impotential('git', Person.FIRST, is_plural=False) 601 | 'gidemem' 602 | 603 | >>> impotential('git', Person.SECOND, is_plural=False) 604 | 'gidemezsin' 605 | 606 | >>> impotential('git', Person.THIRD, is_plural=False) 607 | 'gidemez' 608 | 609 | >>> impotential('git', Person.FIRST, is_plural=True) 610 | 'gidemeyiz' 611 | 612 | >>> impotential('git', Person.FIRST, is_plural=True) 613 | 'gidemeyiz' 614 | 615 | >>> impotential('git', Person.SECOND, is_plural=True) 616 | 'gidemezsiniz' 617 | 618 | >>> impotential('git', Person.THIRD, is_plural=True) 619 | 'gidemezler' 620 | 621 | >>> impotential('al', Person.THIRD, is_plural=True) 622 | 'alamazlar' 623 | 624 | ``` 625 | 626 |
627 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const subject = require('./kefir/subject') 2 | const { predicate, Copula } = require('./kefir/predication') 3 | const { locative, genitive } = require('./kefir/case') 4 | const { enumValues } = require('./kefir/functional') 5 | 6 | const sentence = (subject, predicate, delimiter = ' ') => [subject, predicate].join(delimiter) 7 | 8 | module.exports = { 9 | sentence, 10 | subject, 11 | predicate, 12 | Copula, 13 | locative, 14 | genitive, 15 | enumValues 16 | } 17 | -------------------------------------------------------------------------------- /kefir/case.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Grammatical Cases 3 | 4 | Implemented only six grammatical cases. 5 | 6 | - Nominative 7 | - Genitive 8 | - Dative 9 | - Accusative 10 | - Ablative 11 | - Locative 12 | 13 | Turkish has 9 more cases. 14 | 15 | - Essive 16 | - Instrumental 17 | - Inclusive 18 | - Abessive 19 | - Likeness 20 | - Coverage 21 | - Qualitative 22 | - Conditional 23 | - Possesive 24 | 25 | Detailed explaination: 26 | https://en.wikibooks.org/wiki/Turkish/Cases 27 | 28 | TODO: Enum'lardaki rakamlar yerine auto() kullanılmalı. 29 | */ 30 | 31 | const { 32 | skipFalsyAndJoin, NOTHING 33 | } = require('./functional') 34 | const Suffix = require('./suffix') 35 | const { 36 | getLastVowel, 37 | getVowelSymbol, 38 | Front, 39 | Back, 40 | isBack, 41 | voice, 42 | isFront, 43 | endsWithConsonant, 44 | endsWithVoiceless, 45 | SOFTENING_SOUNDS, 46 | // VOICELESS_CONSONANTS, 47 | UNROUNDED_BACK_VOWELS, 48 | ROUNDED_BACK_VOWELS, 49 | UNROUNDED_FRONT_VOWELS, 50 | ROUNDED_FRONT_VOWELS 51 | } = require('./phonology') 52 | 53 | const GrammaticalCase = { 54 | NOMINATIVE: 1, 55 | GENITIVE: 2, 56 | DATIVE: 3, 57 | ACCUSATIVE: 4, 58 | ABLATIVE: 5, 59 | LOCATIVE: 6 60 | } 61 | 62 | const getCaseProcessor = caseValue => { 63 | const GrammaticalCaseProcessors = { 64 | [GrammaticalCase.NOMINATIVE]: nominative, 65 | [GrammaticalCase.ABLATIVE]: ablative, 66 | [GrammaticalCase.ACCUSATIVE]: accusative, 67 | [GrammaticalCase.GENITIVE]: genitive, 68 | [GrammaticalCase.DATIVE]: dative, 69 | [GrammaticalCase.LOCATIVE]: locative 70 | } 71 | 72 | return GrammaticalCaseProcessors[caseValue] 73 | } 74 | 75 | /* 76 | ## nominative case (yalın in turkish) 77 | the simplest grammatical case, there's no suffix to 78 | affix in that case. 79 | 80 | nominative comes from latin cāsus nominātīvus 81 | means case for naming. 82 | */ 83 | const nominative = text => text 84 | 85 | /* 86 | ## ablative case (ayrılma in turkish) 87 | a grammatical case for nouns, pronouns and adjectives in 88 | the grammar of various languages; it is sometimes used to 89 | express motion away from something, among other uses. 90 | 91 | ✎︎ examples 92 | ``` 93 | adalar[dan] geldim 94 | merkez[den] geçtim 95 | teyit[ten] geçtim 96 | açlık[tan] öldüm 97 | ``` 98 | */ 99 | const ablative = text => { 100 | let suffix = '' 101 | 102 | if (endsWithVoiceless(text)) { 103 | suffix = isBack(text) ? Suffix.TAN : Suffix.TEN 104 | } else { 105 | suffix = isBack(text) ? Suffix.DAN : Suffix.DEN 106 | } 107 | 108 | return text + suffix 109 | } 110 | 111 | /* 112 | ## accusative(ilgi in turkish) 113 | The accusative case (abbreviated acc) of a noun is the 114 | grammatical case used to mark the direct object of a 115 | transitive verb.The same case is used in many 116 | languages for the objects of(some or all) prepositions. 117 | 118 | ✎︎ examples 119 | ``` 120 | aday[ı] yedim 121 | evim[i] yaptım 122 | üzüm[ü] pişirdim 123 | ``` 124 | */ 125 | const accusative = (text, voicer = voice) => { 126 | const lastVowel = getLastVowel(text) 127 | const sound = getVowelSymbol(lastVowel) 128 | 129 | const map = [ 130 | [UNROUNDED_BACK_VOWELS, Back.I], 131 | [UNROUNDED_FRONT_VOWELS, Front.I], 132 | [ROUNDED_BACK_VOWELS, Back.U], 133 | [ROUNDED_FRONT_VOWELS, Front.U] 134 | ] 135 | 136 | for (let [vowels, affix] of map) { 137 | if (vowels.includes(sound)) { 138 | return skipFalsyAndJoin( 139 | voicer(text), 140 | 141 | // if ends with a vowel, echo the genitive 142 | // sound ⟨n⟩ right before the voiced suffix 143 | // !ends_with_consonant(text) ? Suffix.Y : NOTHING, 144 | affix 145 | ) 146 | } 147 | } 148 | } 149 | 150 | /* 151 | ## genitive case (genitifler in turkish) 152 | In grammar, the genitive is the grammatical case 153 | that marks a word, usually a noun, as modifying 154 | another word, also usually a noun. 155 | 156 | ✎︎ examples 157 | ``` 158 | hanımelinin çiçeği (flower of a plant called hanımeli) 159 | kadının ayakkabısı (shoes of the woman) 160 | باب بيت bābu baytin (the door of a house) 161 | mari[i] nie ma w domu (maria is not at home) 162 | ``` 163 | */ 164 | const genitive = text => { 165 | const lastVowel = getLastVowel(text) 166 | const sound = getVowelSymbol(lastVowel) 167 | 168 | const map = [ 169 | [UNROUNDED_BACK_VOWELS, Back.I], 170 | [UNROUNDED_FRONT_VOWELS, Front.I], 171 | [ROUNDED_BACK_VOWELS, Back.U], 172 | [ROUNDED_FRONT_VOWELS, Front.U] 173 | ] 174 | 175 | for (let [vowels, affix] of map) { 176 | if (vowels.includes(sound)) { 177 | return skipFalsyAndJoin( 178 | // nominative form 179 | voice(text), 180 | 181 | // if ends with a vowel, echo the genitive 182 | // sound ⟨n⟩ right before the voiced suffix 183 | !endsWithConsonant(text) ? Suffix.N : NOTHING, 184 | 185 | // ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 186 | affix, 187 | 188 | // genitive sound ⟨n⟩ 189 | Suffix.N 190 | ) 191 | } 192 | } 193 | } 194 | 195 | /* 196 | ## dative case (yönelme in turkish) 197 | In some languages, the dative is used to mark the 198 | indirect object of a sentence. 199 | 200 | ✎︎ examples 201 | ``` 202 | marya yakup'a bir drink verdi (maria gave jacob a drink) 203 | maria jacobī potum dedit (maria gave jacob a drink) 204 | ``` 205 | */ 206 | const dative = text => { 207 | const lastVowel = getLastVowel(text) 208 | // const sound = getVowelSymbol(lastVowel) 209 | const symbol = isFront(lastVowel) ? Front.E : Back.A 210 | 211 | return skipFalsyAndJoin( 212 | // nominative form 213 | voice(text), 214 | 215 | // if ends with a vowel, echo the genitive 216 | // sound ⟨n⟩ right before the voiced suffix 217 | !endsWithConsonant(text) ? Suffix.Y : NOTHING, 218 | 219 | // ⟨e⟩ ⟨a⟩ 220 | symbol, 221 | ) 222 | } 223 | 224 | /* 225 | ## locative case (bulunma in turkish) 226 | Locative is a grammatical case which indicates a location. 227 | It corresponds vaguely to the English prepositions "in", 228 | "on", "at", and "by". 229 | 230 | ✎︎ examples 231 | ``` 232 | bahçe[de] hanımeli var. 233 | yorum[da] iyi beatler var. 234 | kalem[de] güzel uç var. 235 | ``` 236 | */ 237 | const locative = text => { 238 | const lastVowel = getLastVowel(text) 239 | const symbol = isFront(lastVowel) ? Front.E : Back.A 240 | 241 | return skipFalsyAndJoin( 242 | text, 243 | 244 | // ⟨d⟩ or ⟨t⟩ 245 | (text.slice(-1) in SOFTENING_SOUNDS) ? Suffix.T : Suffix.D, 246 | 247 | // ⟨e⟩ or ⟨a⟩ 248 | symbol, 249 | ) 250 | } 251 | 252 | module.exports = { 253 | GrammaticalCase, 254 | getCaseProcessor, 255 | locative 256 | } 257 | -------------------------------------------------------------------------------- /kefir/functional.js: -------------------------------------------------------------------------------- 1 | // from functools import reduce 2 | 3 | const identity = x => x 4 | const isTruthy = x => !!x 5 | const NOTHING = '' 6 | const toDict = namedTuple => namedTuple 7 | const enumValues = x => Object.values(x) 8 | const getEnumMember = (x, val) => x.find(y => y == val) 9 | const join = (...x) => x.join(NOTHING) 10 | const curry = (prior, ...additional) => (...args) => prior(...[...args, ...additional]) 11 | const skipFalsyAndJoin = (...x) => x.filter(isTruthy).join(NOTHING) 12 | 13 | module.exports = { 14 | identity, 15 | isTruthy, 16 | NOTHING, 17 | toDict, 18 | enumValues, 19 | getEnumMember, 20 | join, 21 | curry, 22 | skipFalsyAndJoin 23 | } 24 | -------------------------------------------------------------------------------- /kefir/phonology.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Turkish phonology 3 | 4 | In Hungarian, Finnish, and Turkic languages 5 | vowel sounds are organized in a concept called 6 | vowel harmony.Vowels may be classified as Back 7 | or Front vowels, based on the placement of the 8 | sound in the mouth. 9 | 10 | ``` 11 | Front Vowels 12 | +----------------+ 13 | Unrounded ⟨e⟩ ⟨i⟩ 14 | Rounded ⟨ü⟩ ⟨ö⟩ 15 | 16 | Back Vowels 17 | +----------------+ 18 | Unrounded ⟨a⟩ ⟨ı⟩ 19 | Rounded ⟨u⟩ ⟨o⟩ 20 | ``` 21 | 22 | TODO: Document consonant harmony. 23 | */ 24 | 25 | const { enumValues, join, NOTHING } = require('./functional') 26 | 27 | const Front = { 28 | E: 'e', 29 | I: 'i', 30 | U: 'ü', 31 | O: 'ö' 32 | } 33 | 34 | const Back = { 35 | A: 'a', 36 | I: 'ı', 37 | U: 'u', 38 | O: 'o' 39 | } 40 | 41 | const ROUNDED_FRONT_VOWELS = [ 42 | Front.U, Front.O 43 | ] 44 | 45 | const UNROUNDED_FRONT_VOWELS = [ 46 | Front.E, Front.I 47 | ] 48 | 49 | const ROUNDED_BACK_VOWELS = [ 50 | Back.U, Back.O 51 | ] 52 | 53 | const UNROUNDED_BACK_VOWELS = [ 54 | Back.A, Back.I 55 | ] 56 | 57 | const ROUNDED_VOWELS = [...ROUNDED_BACK_VOWELS, ...ROUNDED_FRONT_VOWELS] 58 | 59 | const CONTINUANT_VOICED = [ 60 | 'ğ', 'j', 'l', 'm', 61 | 'n', 'r', 'v', 'y', 62 | 'z' 63 | ] 64 | 65 | const NON_CONTINUANT_VOICED = [ 66 | 'b', 'c', 'd', 'g' 67 | ] 68 | 69 | const VOICELESS_CONTINUANT = [ 70 | 'p', 'ç', 't', 'k' 71 | ] 72 | 73 | const VOICELESS_NON_CONTINUANT = [ 74 | 'f', 'h', 's', 'ş' 75 | ] 76 | 77 | // FISTIKÇI ŞAHAP 👳 78 | const VOICELESS_CONSONANTS = [...VOICELESS_CONTINUANT, ...VOICELESS_NON_CONTINUANT] 79 | 80 | const VOICED_CONSONANTS = [...CONTINUANT_VOICED, ...NON_CONTINUANT_VOICED] 81 | 82 | const CONSONANTS = [...VOICED_CONSONANTS, ...VOICELESS_CONSONANTS] 83 | 84 | const SOFTENING_SOUNDS = { 85 | p: 'b', 86 | ç: 'c', 87 | t: 'd', 88 | k: 'ğ' 89 | } 90 | 91 | const HARDENING_SOUNDS = { 92 | p: 'b', 93 | ç: 'c', 94 | t: 'd', 95 | k: 'ğ' 96 | } 97 | 98 | class MissingVowelSound extends Error { } 99 | 100 | const endsWithConsonant = text => CONSONANTS.includes(text.slice(-1)) 101 | 102 | const endsWithVoiceless = text => VOICELESS_CONSONANTS.includes(text.slice(-1)) 103 | 104 | const getVowelSymbol = vowel => { 105 | if (enumValues(Front).includes(vowel)) return vowel 106 | if (enumValues(Back).includes(vowel)) return vowel 107 | } 108 | 109 | const getLastVowel = text => { 110 | for (let symbol of text.split(NOTHING).reverse()) { 111 | if (enumValues(Front).includes(symbol)) return symbol 112 | if (enumValues(Back).includes(symbol)) return symbol 113 | } 114 | 115 | throw MissingVowelSound 116 | } 117 | 118 | const determineVowelHarmony = text => { 119 | for (let sound of text.split(NOTHING).reverse()) { 120 | if (enumValues(Front).includes(sound)) return Front 121 | if (enumValues(Back).includes(sound)) return Back 122 | } 123 | 124 | throw MissingVowelSound 125 | } 126 | 127 | const isFront = text => determineVowelHarmony(text) == Front 128 | const isBack = text => determineVowelHarmony(text) == Back 129 | const isRounded = text => ROUNDED_VOWELS.includes(getVowelSymbol(getLastVowel(text))) 130 | const harmony = sound => { 131 | const map = [ 132 | [UNROUNDED_BACK_VOWELS, Back.I], 133 | [UNROUNDED_FRONT_VOWELS, Front.I], 134 | [ROUNDED_BACK_VOWELS, Back.U], 135 | [ROUNDED_FRONT_VOWELS, Front.U] 136 | ] 137 | 138 | for (let [vowels, affix] of map) { 139 | if (vowels.includes(sound)) { return affix } 140 | } 141 | } 142 | 143 | /* 144 | #### swap_front_and_back 145 | Swaps front sounds to back, and vice versa 146 | ```python 147 | >>> swap_front_and_back('acak') 148 | 'ecek' 149 | >>> swap_front_and_back('ocok') 150 | 'öcök' 151 | >>> swap_front_and_back('öcök') 152 | 'ocok' 153 | >>> swap_front_and_back('acak') 154 | 'ecek' 155 | ``` 156 | */ 157 | const swapFrontAndBack = text => { 158 | let vowelSet = [] 159 | 160 | if (isFront(text)) { 161 | vowelSet = [Front, Back] 162 | } else { 163 | vowelSet = [Back, Front] 164 | } 165 | 166 | const symbols = text.split('').map(symbol => { 167 | try { 168 | determineVowelHarmony(symbol) 169 | } catch (e) { 170 | return symbol 171 | } 172 | 173 | const index = Object.values(vowelSet[0]).indexOf(symbol) 174 | return Object.values(vowelSet[1])[index] 175 | }) 176 | 177 | return symbols.join('') 178 | } 179 | 180 | /* 181 | ## Voicing or sonorization(yumuşama in turkish) 182 | to make pronouncation easier, nouns ending 183 | with these sounds. 184 | 185 | ``` 186 | ⟨p⟩ ⟨ç⟩ ⟨t⟩ ⟨k⟩ 187 | ``` 188 | 189 | may be softened by replacing them in order: 190 | 191 | ``` 192 | ⟨b⟩ ⟨c⟩ ⟨d⟩ ⟨ğ⟩ 193 | ``` 194 | 195 | ✎︎ examples 196 | ``` 197 | ço⟨p⟩un → ço⟨b⟩un 198 | ağa⟨ç⟩ın → ağa⟨c⟩n 199 | kağı⟨t⟩ın → kağı⟨d⟩ın 200 | ren⟨k⟩in → ren⟨g⟩in 201 | ``` 202 | 203 | ✎︎ examples in other languages 204 | ``` 205 | li⟨f⟩e → li⟨v⟩e 206 | stri⟨f⟩e → stri⟨v⟩e 207 | proo⟨f⟩ → pro⟨v⟩e 208 | ``` 209 | */ 210 | const voice = text => { 211 | for (let sound in SOFTENING_SOUNDS) { 212 | if (text.endsWith(sound)) { 213 | return join(text.slice(0, -1), SOFTENING_SOUNDS[sound]) 214 | } 215 | } 216 | 217 | return text 218 | } 219 | 220 | /* 221 | ## Devoicing or desonorization(sertleşme in turkish) 222 | to make pronouncation easier, nouns ending with 223 | these sounds: 224 | ``` 225 | ⟨p⟩ ⟨ç⟩ ⟨t⟩ ⟨k⟩ 226 | ``` 227 | 228 | may be hardened by replacing them in order: 229 | ``` 230 | ⟨b⟩ ⟨c⟩ ⟨d⟩ ⟨ğ⟩ 231 | ``` 232 | 233 | ✎︎ examples 234 | ``` 235 | ço⟨p⟩un → ço⟨b⟩un 236 | ağa⟨ç⟩ın → ağa⟨c⟩n 237 | kağı⟨t⟩ın → kağı⟨d⟩ın 238 | ren⟨k⟩in → ren⟨g⟩in 239 | ``` 240 | 241 | ✎︎ examples in other languages 242 | ``` 243 | dogs → dogs ([ɡz]) 244 | missed → missed ([st]) 245 | whizzed → whizzed ([zd]) 246 | prośba → prɔʑba 247 | просьба → prozʲbə 248 | ``` 249 | */ 250 | const devoice = text => { 251 | for (let sound in SOFTENING_SOUNDS) { 252 | if (text.endsWith(sound)) { 253 | return join(text.slice(0, -1), SOFTENING_SOUNDS[sound]) 254 | } 255 | } 256 | 257 | return text 258 | } 259 | 260 | module.exports = { 261 | getLastVowel, 262 | getVowelSymbol, 263 | Front, 264 | Back, 265 | isBack, 266 | voice, 267 | isFront, 268 | isRounded, 269 | harmony, 270 | devoice, 271 | swapFrontAndBack, 272 | endsWithConsonant, 273 | endsWithVoiceless, 274 | SOFTENING_SOUNDS, 275 | HARDENING_SOUNDS, 276 | VOICELESS_CONSONANTS, 277 | UNROUNDED_BACK_VOWELS, 278 | ROUNDED_BACK_VOWELS, 279 | UNROUNDED_FRONT_VOWELS, 280 | ROUNDED_FRONT_VOWELS 281 | } 282 | -------------------------------------------------------------------------------- /kefir/predication.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Turkish Predication and Copula 3 | 4 | turkish language copulas, which are called as ek-eylem which 5 | literally means 'suffix-verb' are one of the most distinct 6 | features of turkish grammar. 7 | 8 | TODO: Remove unused imports. 9 | */ 10 | 11 | const { 12 | join, 13 | skipFalsyAndJoin, 14 | NOTHING 15 | } = require('./functional') 16 | 17 | const Suffix = require('./suffix') 18 | const { 19 | getLastVowel, 20 | getVowelSymbol, 21 | Back, 22 | Front, 23 | isFront, 24 | voice, 25 | endsWithConsonant, 26 | endsWithVoiceless, 27 | UNROUNDED_BACK_VOWELS, 28 | ROUNDED_BACK_VOWELS, 29 | UNROUNDED_FRONT_VOWELS, 30 | ROUNDED_FRONT_VOWELS, 31 | harmony, 32 | swapFrontAndBack 33 | } = require('./phonology') 34 | 35 | const Person = { 36 | FIRST: 'first', 37 | SECOND: 'second', 38 | THIRD: 'third' 39 | } 40 | 41 | const Copula = { 42 | NEGATIVE: 'negative', 43 | ZERO: 'zero', 44 | TOBE: 'tobe', 45 | PERSONAL: 'personal', 46 | PERFECTIVE: 'perfective', 47 | IMPERFECTIVE: 'imperfective', 48 | PROGRESSIVE: 'progressive', 49 | NECESSITATIVE: 'necessitative', 50 | FUTURE: 'future', 51 | IMPOTENTIAL: 'impotential', 52 | CONDITIONAL: 'conditional' 53 | } 54 | 55 | const getCopulaProcessor = copula => { 56 | const CopulaProcessors = { 57 | [Copula.NEGATIVE]: negative, 58 | [Copula.ZERO]: zero, 59 | [Copula.TOBE]: tobe, 60 | [Copula.PERSONAL]: personal, 61 | [Copula.PERFECTIVE]: perfective, 62 | [Copula.IMPERFECTIVE]: imperfective, 63 | [Copula.PROGRESSIVE]: progressive, 64 | [Copula.NECESSITATIVE]: necessitative, 65 | [Copula.FUTURE]: future, 66 | [Copula.IMPOTENTIAL]: impotential, 67 | [Copula.CONDITIONAL]: conditional 68 | } 69 | 70 | return CopulaProcessors[copula] 71 | } 72 | 73 | /* 74 | #### zero copula 75 | is the rule for third person, as in hungarian 76 | and russian. that means two nouns, or a noun and an 77 | adjective can be juxtaposed to make a sentence without 78 | using any copula. third person plural might be indicated 79 | with the use of plural suffix "-lar/-ler". 80 | 81 | ✎︎ examples 82 | ``` 83 | yogurt kültür (yogurt [is-a] culture) 84 | abbas yolcu (abbas [is-a] traveller) 85 | evlerinin önü yonca (the front of their home [is-a] plant called yonca) 86 | ``` 87 | 88 | ✎︎ tests 89 | ```python 90 | >>> zero('yolcu') 91 | 'yolcu' 92 | 93 | ``` 94 | */ 95 | const zero = (predicate, person = Person.THIRD, isPlural = false) => predicate 96 | 97 | /* 98 | ''' 99 | #### negative 100 | negation is indicated by the negative copula değil. 101 | değil is never used as a suffix, but it takes suffixes 102 | according to context. 103 | 104 | ✎︎ examples 105 | ``` 106 | yogurt kültür değildir (yogurt [is-not-a] culture) 107 | abbas yolcu değildir (abbas [is-not-a] traveller) 108 | evlerinin önü yonca değildir (the front of their home [is-not-a] yonca) 109 | ``` 110 | 111 | ✎︎ tests 112 | ```python 113 | >>> negative('yolcu') 114 | 'yolcu değil' 115 | 116 | ``` 117 | ''' 118 | */ 119 | const negative = (predicate, person = Person.THIRD, isPlural = false, delimiter = Suffix.DELIMITER) => join(predicate, delimiter, Suffix.NEGATIVE) 120 | 121 | /* 122 | ### tobe 123 | turkish "to be" as regular/auxiliary verb (olmak). 124 | 125 | ✎︎ examples 126 | ``` 127 | yogurt kültürdür (yogurt [is] culture) 128 | abbas yolcudur (abbas [is] traveller) 129 | evlerinin önü yoncadır (the front of their home [is] plant called yonca) 130 | ``` 131 | 132 | ✎︎ tests 133 | ```python 134 | >>> tobe('yolcu') 135 | 'yolcudur' 136 | >>> tobe('üzüm') 137 | 'üzümdür' 138 | >>> tobe('yonca') 139 | 'yoncadır' 140 | 141 | ``` 142 | */ 143 | const tobe = (predicate, person = Person.THIRD, isPlural = false) => { 144 | const lastVowel = getLastVowel(predicate) 145 | const sound = getVowelSymbol(lastVowel) 146 | 147 | const map = [ 148 | [UNROUNDED_BACK_VOWELS, Back.I], 149 | [UNROUNDED_FRONT_VOWELS, Front.I], 150 | [ROUNDED_BACK_VOWELS, Back.U], 151 | [ROUNDED_FRONT_VOWELS, Front.U] 152 | ] 153 | 154 | for (let [vowels, affix] of map) { 155 | if (vowels.includes(sound)) { 156 | return skipFalsyAndJoin( 157 | predicate, 158 | Suffix.D, 159 | affix, 160 | Suffix.R 161 | ) 162 | } 163 | } 164 | } 165 | 166 | /* 167 | ### personification copula 168 | 169 | ✎︎ examples 170 | ``` 171 | ben buralıyım (i'm from here) 172 | sen oralısın (you're from over there) 173 | aynı gezegenliyiz (we're from same planet) 174 | ``` 175 | 176 | ✎︎ tests 177 | ```python 178 | >>> personal('uçak', Person.FIRST, is_plural=False) 179 | 'uçağım' 180 | 181 | >>> personal('oralı', Person.SECOND, is_plural=False) 182 | 'oralısın' 183 | 184 | >>> personal('gezegenli', Person.FIRST, is_plural=True) 185 | 'gezegenliyiz' 186 | 187 | ``` 188 | */ 189 | const personal = (predicate, whom = Person.THIRD, isPlural = false) => impersonate(predicate, whom, isPlural, false) 190 | 191 | /* 192 | ### inferential mood (-miş in turkish) 193 | it is used to convey information about events 194 | which were not directly observed or were inferred by the speaker. 195 | 196 | ✎︎ examples 197 | ``` 198 | elmaymışım (i was an apple as i've heard) 199 | üzülmüşsün (you were sad as i've heard) 200 | doktormuş (he/she/it was a doctor as i've heard) 201 | üzümmüşsün (you were a grape as i've heard) 202 | ``` 203 | 204 | ✎︎ tests 205 | ```python 206 | >>> inferential('öğretmen', Person.SECOND, is_plural=False) 207 | 'öğretmenmişsin' 208 | 209 | >>> inferential('üzül', Person.SECOND, is_plural=False) 210 | 'üzülmüşsün' 211 | 212 | >>> inferential('robot', Person.FIRST, is_plural=False) 213 | 'robotmuşum' 214 | 215 | >>> inferential('robot', Person.THIRD, is_plural=False) 216 | 'robotmuş' 217 | 218 | >>> inferential('ada', Person.THIRD, is_plural=False) 219 | 'adaymış' 220 | 221 | ``` 222 | */ 223 | const inferential = (predicate, whom = Person.THIRD, isPlural = false) => { 224 | const lastVowel = getLastVowel(predicate) 225 | const sound = getVowelSymbol(lastVowel) 226 | 227 | const inferenceSuffix = join('m', harmony(sound), 'ş') 228 | 229 | return skipFalsyAndJoin( 230 | predicate, 231 | 232 | // combinative consontant ⟨y⟩ 233 | !endsWithConsonant(predicate) ? Suffix.Y : NOTHING, 234 | 235 | impersonate(inferenceSuffix, whom, isPlural) 236 | ) 237 | } 238 | 239 | /* 240 | ### inferential mood (-isem in turkish) 241 | It is a grammatical mood used to express a proposition whose 242 | validity is dependent on some condition, possibly counterfactual. 243 | 244 | ✎︎ examples 245 | ``` 246 | elmaysam (if i am an apple) 247 | üzümsen (if you are a grape) 248 | bıçaklarsa (if they are a knife) 249 | ``` 250 | 251 | ✎︎ tests 252 | ```python 253 | >>> conditional('elma', Person.FIRST, is_plural=False) 254 | 'elmaysam' 255 | >>> conditional('üzüm', Person.SECOND, is_plural=False) 256 | 'üzümsen' 257 | >>> conditional('bıçak', Person.THIRD, is_plural=True) 258 | 'bıçaklarsa' 259 | 260 | ``` 261 | */ 262 | const conditional = (predicate, whom = Person.THIRD, isPlural = false) => { 263 | const conditionSuffix = isFront(predicate) ? Suffix.SE : Suffix.SA 264 | 265 | const map = [ 266 | [Person.FIRST, false, Suffix.M], 267 | [Person.SECOND, false, Suffix.N], 268 | [Person.THIRD, false, NOTHING], 269 | [Person.FIRST, true, Suffix.K], 270 | [Person.SECOND, true, Suffix.NIZ], 271 | [Person.THIRD, true, NOTHING] 272 | ] 273 | 274 | for (let [toWhom, plurality, personification] of map) { 275 | if (toWhom == whom && plurality == isPlural) { 276 | return skipFalsyAndJoin( 277 | predicate, 278 | 279 | // plural suffix for 3rd person 280 | (whom == Person.THIRD && isPlural) ? (isFront(predicate) ? Suffix.LER : Suffix.LAR) : NOTHING, 281 | 282 | // combinative consontant ⟨y⟩ 283 | !endsWithConsonant(predicate) ? Suffix.Y : NOTHING, 284 | 285 | conditionSuffix, 286 | personification 287 | ) 288 | } 289 | } 290 | } 291 | 292 | 293 | /* 294 | ### alethic modality (-idi in turkish) 295 | linguistic modality that indicates modalities of truth, 296 | in particular the modalities of logical necessity, 297 | possibility or impossibility. 298 | 299 | ✎︎ examples 300 | ``` 301 | elmaydım (i was an apple) 302 | üzümdün (you were a grape) 303 | doktordu (he/she/it was a doctor) 304 | ``` 305 | 306 | ✎︎ tests 307 | ```python 308 | >>> perfective('açık', Person.FIRST, is_plural=False) 309 | 'açıktım' 310 | 311 | >>> perfective('oralı', Person.SECOND, is_plural=False) 312 | 'oralıydın' 313 | 314 | >>> perfective('dalda', Person.FIRST, is_plural=False) 315 | 'daldaydım' 316 | 317 | >>> perfective('dalda', Person.THIRD, is_plural=False) 318 | 'daldaydı' 319 | 320 | >>> perfective('dalda', Person.FIRST, is_plural=True) 321 | 'daldaydık' 322 | 323 | >>> perfective('dalda', Person.SECOND, is_plural=True) 324 | 'daldaydınız' 325 | 326 | >>> perfective('dalda', Person.THIRD, is_plural=True) 327 | 'daldaydılar' 328 | 329 | >>> perfective('gezegende', Person.THIRD, is_plural=True) 330 | 'gezegendeydiler' 331 | 332 | ``` 333 | */ 334 | const perfective = (predicate, whom = Person.THIRD, isPlural = false) => impersonate(predicate, whom, isPlural, true) 335 | 336 | /* 337 | ### the imperfective (-iyor in turkish) 338 | grammatical aspect used to describe a situation viewed with interior composition. 339 | describes ongoing, habitual, repeated, or similar semantic roles, 340 | whether that situation occurs in the past, present, or future. 341 | 342 | ✎︎ examples 343 | ``` 344 | gidiyorum (i'm going) 345 | kayıyor (he's skating) 346 | üzümlüyor (he's graping) 347 | ``` 348 | 349 | ✎︎ tests 350 | ```python 351 | >>> imperfective('açı', Person.FIRST, is_plural=False) 352 | 'açıyorum' 353 | 354 | >>> imperfective('açık', Person.FIRST, is_plural=False) 355 | 'açıkıyorum' 356 | 357 | >>> imperfective('oralı', Person.SECOND, is_plural=False) 358 | 'oralıyorsun' 359 | 360 | >>> imperfective('dal', Person.THIRD, is_plural=False) 361 | 'dalıyor' 362 | 363 | >>> imperfective('dal', Person.FIRST, is_plural=True) 364 | 'dalıyoruz' 365 | 366 | >>> imperfective('dal', Person.FIRST, is_plural=True) 367 | 'dalıyoruz' 368 | 369 | >>> imperfective('dal', Person.SECOND, is_plural=True) 370 | 'dalıyorsunuz' 371 | 372 | >>> imperfective('dal', Person.THIRD, is_plural=True) 373 | 'dalıyorlar' 374 | 375 | ``` 376 | */ 377 | const imperfective = (predicate, whom = Person.THIRD, isPlural = false) => { 378 | const lastVowel = getLastVowel(predicate) 379 | const sound = getVowelSymbol(lastVowel) 380 | 381 | const imperfectCopula = skipFalsyAndJoin( 382 | endsWithConsonant(predicate) ? harmony(sound) : NOTHING, 383 | Suffix.IMPERFECT 384 | ) 385 | 386 | return join(predicate, impersonate(imperfectCopula, whom, isPlural, false)) 387 | } 388 | 389 | /* 390 | ''' 391 | ### the future tense (-iyor in turkish) 392 | is a verb form that generally marks the event described by the verb as not 393 | having happened yet, but expected to happen in the future. 394 | 395 | ✎︎ examples 396 | ``` 397 | gidecek (he'll go) 398 | ölecek (he'll die) 399 | can alacak (he'll kill someone) 400 | ``` 401 | 402 | ✎︎ tests 403 | ```python 404 | >>> future('gel', Person.FIRST, is_plural=False) 405 | 'geleceğim' 406 | 407 | >>> future('açık', Person.FIRST, is_plural=False) 408 | 'açıkacağım' 409 | 410 | >>> future('gel', Person.FIRST, is_plural=True) 411 | 'geleceğiz' 412 | 413 | ``` 414 | ''' 415 | */ 416 | const future = (predicate, whom = Person.THIRD, isPlural = false) => { 417 | const futureCopula = join( 418 | predicate, 419 | isFront(predicate) ? Suffix.FUTURE : swapFrontAndBack(Suffix.FUTURE), 420 | ) 421 | 422 | return impersonate(futureCopula, whom, isPlural, false) 423 | } 424 | 425 | /* 426 | ''' 427 | ### progressive tense 428 | 429 | ✎︎ examples 430 | gülmekteyim (i am in the process of laughing) 431 | ölmekteler (they are in the process of dying 👾) 432 | 433 | ✎︎ tests 434 | ```python 435 | >>> progressive('gel', Person.FIRST, is_plural=False) 436 | 'gelmekteyim' 437 | 438 | >>> progressive('açık', Person.FIRST, is_plural=False) 439 | 'açıkmaktayım' 440 | 441 | >>> progressive('gel', Person.FIRST, is_plural=True) 442 | 'gelmekteyiz' 443 | 444 | ``` 445 | ''' 446 | */ 447 | const progressive = (predicate, whom = Person.THIRD, isPlural = false) => { 448 | const progressiveCopula = join( 449 | predicate, 450 | isFront(predicate) ? Suffix.PROGRESSIVE : swapFrontAndBack(Suffix.PROGRESSIVE) 451 | ) 452 | 453 | return impersonate(progressiveCopula, whom, isPlural, false) 454 | } 455 | 456 | /* 457 | ### necessitative copula 458 | 459 | ✎︎ examples 460 | ``` 461 | gitmeliyim (i must go) 462 | kaçmalıyım (i must run away) 463 | ``` 464 | 465 | ✎︎ tests 466 | ```python 467 | >>> necessitative('git', Person.FIRST, is_plural=False) 468 | 'gitmeliyim' 469 | 470 | >>> necessitative('açık', Person.FIRST, is_plural=False) 471 | 'açıkmalıyım' 472 | 473 | >>> necessitative('uza', Person.FIRST, is_plural=True) 474 | 'uzamalıyız' 475 | 476 | ``` 477 | */ 478 | const necessitative = (predicate, whom = Person.THIRD, isPlural = false) => { 479 | const necessitativeCopula = join( 480 | predicate, 481 | isFront(predicate) ? Suffix.NECESSITY : swapFrontAndBack(Suffix.NECESSITY) 482 | ) 483 | 484 | return impersonate(necessitativeCopula, whom, isPlural, false) 485 | } 486 | 487 | /* 488 | ### impotential copula 489 | 490 | ✎︎ examples 491 | ``` 492 | gidemem (i cannot come) 493 | kaçamayız (we cannot run away) 494 | ``` 495 | 496 | ✎︎ tests 497 | ```python 498 | >>> impotential('git', Person.FIRST, is_plural=False) 499 | 'gidemem' 500 | 501 | >>> impotential('git', Person.SECOND, is_plural=False) 502 | 'gidemezsin' 503 | 504 | >>> impotential('git', Person.THIRD, is_plural=False) 505 | 'gidemez' 506 | 507 | >>> impotential('git', Person.FIRST, is_plural=True) 508 | 'gidemeyiz' 509 | 510 | >>> impotential('git', Person.FIRST, is_plural=True) 511 | 'gidemeyiz' 512 | 513 | >>> impotential('git', Person.SECOND, is_plural=True) 514 | 'gidemezsiniz' 515 | 516 | >>> impotential('git', Person.THIRD, is_plural=True) 517 | 'gidemezler' 518 | 519 | >>> impotential('al', Person.THIRD, is_plural=True) 520 | 'alamazlar' 521 | 522 | ``` 523 | */ 524 | const impotential = (predicate, whom = Person.THIRD, isPlural = false) => { 525 | let impotentialCopula = isFront(predicate) ? Suffix.IMPOTENTIAL : swapFrontAndBack(Suffix.IMPOTENTIAL) 526 | let plurality = isFront(predicate) ? Suffix.LER : Suffix.LAR 527 | 528 | const map = [ 529 | [Person.FIRST, false, Suffix.M], 530 | [Person.SECOND, false, Suffix.Z + Suffix.SIN], 531 | [Person.THIRD, false, Suffix.Z], 532 | [Person.FIRST, true, Suffix.Y + Suffix.IZ], 533 | [Person.SECOND, true, Suffix.Z + Suffix.SIN + Suffix.IZ], 534 | [Person.THIRD, true, Suffix.Z + plurality] 535 | ] 536 | 537 | for (let [toWhom, plurality, personification] of map) { 538 | if (toWhom == whom && plurality == isPlural) { 539 | return skipFalsyAndJoin( 540 | voice(predicate), 541 | 542 | // combinative consontant ⟨y⟩ 543 | !endsWithConsonant(predicate) ? Suffix.Y : NOTHING, 544 | 545 | impotentialCopula, 546 | personification 547 | ) 548 | } 549 | } 550 | } 551 | 552 | const firstPersonSingular = (text, inPast = false) => { 553 | const lastVowel = getLastVowel(text) 554 | const sound = getVowelSymbol(lastVowel) 555 | 556 | return skipFalsyAndJoin( 557 | // last vowel should not be voiced in alethic modality 558 | inPast ? text : voice(text), 559 | 560 | // combinative consontant ⟨y⟩ 561 | !endsWithConsonant(text) ? Suffix.Y : NOTHING, 562 | 563 | // ⟨d⟩ or ⟨t⟩ 564 | inPast ? (endsWithVoiceless(text) ? Suffix.T : Suffix.D) : NOTHING, 565 | 566 | // ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 567 | harmony(sound), 568 | 569 | Suffix.M 570 | ) 571 | } 572 | 573 | const secondPersonSingular = (text, inPast = false) => { 574 | const lastVowel = getLastVowel(text) 575 | const sound = getVowelSymbol(lastVowel) 576 | 577 | return skipFalsyAndJoin( 578 | text, 579 | 580 | // combinative consontant ⟨y⟩ 581 | inPast ? !endsWithConsonant(text) ? Suffix.Y : NOTHING : NOTHING, 582 | 583 | // ⟨d⟩ or ⟨t⟩ 584 | inPast ? (endsWithVoiceless(text) ? Suffix.T : Suffix.D) : NOTHING, 585 | 586 | // sound ⟨s⟩ in present time 587 | !inPast ? Suffix.S : NOTHING, 588 | 589 | // # ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 590 | harmony(sound), 591 | 592 | Suffix.N 593 | ) 594 | } 595 | 596 | const thirdPersonSingular = (text, inPast = false) => { 597 | const lastVowel = getLastVowel(text) 598 | const sound = getVowelSymbol(lastVowel) 599 | 600 | return skipFalsyAndJoin( 601 | text, 602 | 603 | // combinative consontant ⟨y⟩ 604 | !endsWithConsonant(text) ? Suffix.Y : NOTHING, 605 | 606 | // add ⟨t⟩ or ⟨d⟩ for alethic modality 607 | inPast ? (endsWithVoiceless(text) ? Suffix.T : Suffix.D) : NOTHING, 608 | 609 | // # ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 610 | inPast ? harmony(sound) : NOTHING 611 | ) 612 | } 613 | 614 | const firstPersonPlural = (text, inPast = false) => { 615 | const lastVowel = getLastVowel(text) 616 | const sound = getVowelSymbol(lastVowel) 617 | 618 | return skipFalsyAndJoin( 619 | // last vowel should not be voiced in alethic modality 620 | inPast ? text : voice(text), 621 | 622 | // combinative consontant ⟨y⟩ 623 | !endsWithConsonant(text) ? Suffix.Y : NOTHING, 624 | 625 | // ⟨d⟩ or ⟨t⟩ 626 | inPast ? (endsWithVoiceless(text) ? Suffix.T : Suffix.D) : NOTHING, 627 | 628 | // # ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 629 | harmony(sound), 630 | 631 | inPast ? Suffix.K : Suffix.Z 632 | ) 633 | } 634 | 635 | const secondPersonPlural = (text, inPast = false) => { 636 | const lastVowel = getLastVowel(text) 637 | const sound = getVowelSymbol(lastVowel) 638 | 639 | return skipFalsyAndJoin( 640 | secondPersonSingular(text, inPast), 641 | 642 | // # ⟨a⟩ ⟨i⟩ ⟨u⟩ ⟨ü⟩ 643 | harmony(sound), 644 | 645 | Suffix.Z 646 | ) 647 | } 648 | 649 | const thirdPersonPlural = (text, inPast = false) => skipFalsyAndJoin( 650 | thirdPersonSingular(text, inPast), 651 | 652 | // -lar or -ler, plural affix 653 | isFront(text) ? Suffix.LER : Suffix.LAR 654 | ) 655 | 656 | const impersonate = (text, toWhom, isPlural, inPast = false) => { 657 | const map = [ 658 | [Person.FIRST, false, firstPersonSingular], 659 | [Person.SECOND, false, secondPersonSingular], 660 | [Person.THIRD, false, thirdPersonSingular], 661 | [Person.FIRST, true, firstPersonPlural], 662 | [Person.SECOND, true, secondPersonPlural], 663 | [Person.THIRD, true, thirdPersonPlural] 664 | ] 665 | 666 | for (let [person, plurality, processor] of map) { 667 | if (person == toWhom && isPlural == plurality) { 668 | return processor(text, inPast) 669 | } 670 | } 671 | } 672 | 673 | const predicate = (text, person = Person.THIRD, copula = Copula.ZERO, isPlural = false) => { 674 | try { 675 | let processor = getCopulaProcessor(copula) 676 | return processor(text, person, isPlural) 677 | } catch (e) { 678 | throw new Error(`invalid copula. options: ${JSON.values(Copula).join(', ')}`) 679 | } 680 | } 681 | 682 | module.exports = { 683 | Person, 684 | zero, 685 | negative, 686 | tobe, 687 | personal, 688 | inferential, 689 | conditional, 690 | perfective, 691 | imperfective, 692 | future, 693 | progressive, 694 | necessitative, 695 | predicate, 696 | Copula, 697 | impotential 698 | } 699 | -------------------------------------------------------------------------------- /kefir/pronoun.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Turkish Grammar / Pronouns 3 | singular plural 4 | 1st 2nd 3rd 1st 2nd 3rd 5 | absolute ben sen o biz siz onlar 6 | accusative beni seni onu bizi sizi onları 7 | dative bana sana ona bize size onlara 8 | locative bende sende onda bizde sizde onlarda 9 | ablative benden senden ondan bizden sizden onlardan 10 | genitive benim senin onun bizim sizin onların 11 | TODO: Implement pronouns. 12 | */ 13 | -------------------------------------------------------------------------------- /kefir/subject.js: -------------------------------------------------------------------------------- 1 | const { GrammaticalCase, getCaseProcessor } = require('./case') 2 | const { isFront } = require('./phonology') 3 | const { join, NOTHING } = require('./functional') 4 | const Suffix = require('./suffix') 5 | 6 | const subject = (stem, isPlural = false, caseValue = GrammaticalCase.NOMINATIVE) => { 7 | let suffix = NOTHING 8 | 9 | if (isPlural) { 10 | suffix = isFront(stem) ? Suffix.LER : Suffix.LAR 11 | } 12 | 13 | const processor = getCaseProcessor(caseValue) 14 | 15 | return processor(join(stem, suffix)) 16 | } 17 | 18 | module.exports = subject 19 | -------------------------------------------------------------------------------- /kefir/suffix.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NEGATIVE: 'değil', 3 | IMPERFECT: 'yor', 4 | FUTURE: 'ecek', 5 | PROGRESSIVE: 'mekte', 6 | NECESSITY: 'meli', 7 | IMPOTENTIAL: 'eme', 8 | 9 | LAR: 'lar', 10 | LER: 'ler', 11 | DEN: 'den', 12 | DAN: 'dan', 13 | TAN: 'tan', 14 | TEN: 'ten', 15 | NIZ: 'niz', 16 | SIN: 'sin', 17 | MAK: 'mak', 18 | MEK: 'mek', 19 | 20 | SE: 'se', 21 | SA: 'sa', 22 | IZ: 'iz', 23 | 24 | N: 'n', 25 | M: 'm', 26 | Y: 'y', 27 | D: 'd', 28 | T: 't', 29 | R: 'r', 30 | S: 's', 31 | Z: 'z', 32 | K: 'k', 33 | 34 | DELIMITER: ' ' 35 | 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kefir", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "ava --verbose", 8 | "lint": "eslint ." 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/yogurt-cultures/kefir-js.git" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/yogurt-cultures/kefir-js/issues" 18 | }, 19 | "homepage": "https://github.com/yogurt-cultures/kefir-js#readme", 20 | "devDependencies": { 21 | "ava": "^0.25.0", 22 | "eslint": "^5.3.0", 23 | "eslint-config-unu": "^1.2.4" 24 | }, 25 | "eslintConfig": { 26 | "extends": "unu" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | 3 | import { 4 | Person, 5 | zero, 6 | negative, 7 | tobe, 8 | personal, 9 | inferential, 10 | conditional, 11 | perfective, 12 | imperfective, 13 | future, 14 | progressive, 15 | necessitative, 16 | impotential, 17 | predicate 18 | } from '../kefir/predication' 19 | 20 | import { 21 | swapFrontAndBack 22 | } from '../kefir/phonology' 23 | 24 | import { 25 | sentence, 26 | subject, 27 | locative 28 | } from '..' 29 | 30 | test('predication > zero', t => { 31 | t.is(zero('yolcu'), 'yolcu') 32 | }) 33 | 34 | test('predication > negative', t => { 35 | t.is(negative('yolcu'), 'yolcu değil') 36 | }) 37 | 38 | test('predication > tobe', t => { 39 | t.is(tobe('yolcu'), 'yolcudur') 40 | t.is(tobe('üzüm'), 'üzümdür') 41 | t.is(tobe('yonca'), 'yoncadır') 42 | }) 43 | 44 | test('predication > personal', t => { 45 | t.is(personal('uçak', Person.FIRST, false), 'uçağım') 46 | t.is(personal('oralı', Person.SECOND, false), 'oralısın') 47 | t.is(personal('gezegenli', Person.FIRST, true), 'gezegenliyiz') 48 | }) 49 | 50 | test('predication > inferential', t => { 51 | t.is(inferential('öğretmen', Person.SECOND, false), 'öğretmenmişsin') 52 | t.is(inferential('üzül', Person.SECOND, false), 'üzülmüşsün') 53 | t.is(inferential('robot', Person.FIRST, false), 'robotmuşum') 54 | t.is(inferential('robot', Person.THIRD, false), 'robotmuş') 55 | t.is(inferential('ada', Person.THIRD, false), 'adaymış') 56 | }) 57 | 58 | test('predication > conditional', t => { 59 | t.is(conditional('elma', Person.FIRST, false), 'elmaysam') 60 | t.is(conditional('üzüm', Person.SECOND, false), 'üzümsen') 61 | t.is(conditional('bıçak', Person.THIRD, true), 'bıçaklarsa') 62 | }) 63 | 64 | test('predication > perfective', t => { 65 | t.is(perfective('açık', Person.FIRST, false), 'açıktım') 66 | t.is(perfective('oralı', Person.SECOND, false), 'oralıydın') 67 | t.is(perfective('dalda', Person.FIRST, false), 'daldaydım') 68 | t.is(perfective('dalda', Person.THIRD, false), 'daldaydı') 69 | t.is(perfective('dalda', Person.FIRST, true), 'daldaydık') 70 | t.is(perfective('dalda', Person.SECOND, true), 'daldaydınız') 71 | t.is(perfective('dalda', Person.THIRD, true), 'daldaydılar') 72 | t.is(perfective('gezegende', Person.THIRD, true), 'gezegendeydiler') 73 | }) 74 | 75 | test('predication > imperfective', t => { 76 | t.is(imperfective('açı', Person.FIRST, false), 'açıyorum') 77 | t.is(imperfective('açık', Person.FIRST, false), 'açıkıyorum') 78 | t.is(imperfective('oralı', Person.SECOND, false), 'oralıyorsun') 79 | t.is(imperfective('dal', Person.THIRD, false), 'dalıyor') 80 | t.is(imperfective('dal', Person.FIRST, true), 'dalıyoruz') 81 | t.is(imperfective('dal', Person.FIRST, true), 'dalıyoruz') 82 | t.is(imperfective('dal', Person.SECOND, true), 'dalıyorsunuz') 83 | t.is(imperfective('dal', Person.THIRD, true), 'dalıyorlar') 84 | }) 85 | 86 | test('predication > future', t => { 87 | t.is(future('gel', Person.FIRST, false), 'geleceğim') 88 | t.is(future('açık', Person.FIRST, false), 'açıkacağım') 89 | t.is(future('gel', Person.FIRST, true), 'geleceğiz') 90 | }) 91 | 92 | test('predication > progressive', t => { 93 | t.is(progressive('gel', Person.FIRST, false), 'gelmekteyim') 94 | t.is(progressive('açık', Person.FIRST, false), 'açıkmaktayım') 95 | t.is(progressive('gel', Person.FIRST, true), 'gelmekteyiz') 96 | }) 97 | 98 | test('predication > necessitative', t => { 99 | t.is(necessitative('git', Person.FIRST, false), 'gitmeliyim') 100 | t.is(necessitative('açık', Person.FIRST, false), 'açıkmalıyım') 101 | t.is(necessitative('uza', Person.FIRST, true), 'uzamalıyız') 102 | }) 103 | 104 | test('predication > impotential', t => { 105 | t.is(impotential('git', Person.FIRST, false), 'gidemem') 106 | t.is(impotential('git', Person.SECOND, false), 'gidemezsin') 107 | t.is(impotential('git', Person.THIRD, false), 'gidemez') 108 | t.is(impotential('git', Person.FIRST, true), 'gidemeyiz') 109 | t.is(impotential('git', Person.FIRST, true), 'gidemeyiz') 110 | t.is(impotential('git', Person.SECOND, true), 'gidemezsiniz') 111 | t.is(impotential('git', Person.THIRD, true), 'gidemezler') 112 | t.is(impotential('al', Person.THIRD, true), 'alamazlar') 113 | }) 114 | 115 | test('phonology > swapFrontAndBack', t => { 116 | t.is(swapFrontAndBack('acak'), 'ecek') 117 | t.is(swapFrontAndBack('ocok'), 'öcök') 118 | t.is(swapFrontAndBack('öcök'), 'ocok') 119 | t.is(swapFrontAndBack('acak'), 'ecek') 120 | }) 121 | 122 | test('index module', t => { 123 | const ayni = subject('aynı') 124 | const havuc = subject('havuç') 125 | const gel = predicate('gel', 'third', 'perfective') 126 | const yap = predicate('yap', 'third', 'perfective') 127 | let dal = predicate('dal', 'third', 'progressive') 128 | dal = predicate(dal, 'third', 'perfective') 129 | 130 | const birisi = subject('yakup') 131 | t.is(sentence(birisi, yap), 'yakup yaptı') 132 | t.is(sentence(birisi, dal), 'yakup dalmaktaydı') 133 | 134 | t.is(sentence(havuc, gel), 'havuç geldi') 135 | t.is(sentence(havuc, yap), 'havuç yaptı') 136 | t.is(sentence(havuc, dal), 'havuç dalmaktaydı') 137 | 138 | const sebze = predicate(locative('marul'), 'first', 'perfective', true) 139 | dal = predicate(locative('dal'), 'first', 'perfective', true) 140 | 141 | t.is(sentence(ayni, sebze), 'aynı maruldaydık') 142 | t.is(sentence(ayni, dal), 'aynı daldaydık') 143 | }) 144 | --------------------------------------------------------------------------------