verb-master

Practice different formality levels, tenses, and grammar forms for Korean verbs
git clone git@knot.brookjeynes.dev:did:plc:qr52v73pdmgppmvnpjwa5viw
Log | Files | Refs | README | LICENSE

conjugator.js (19488B)


      1 import { Geulja, findVowelToAppend, getLead, getPadchim, getVowel, join } from "./hangul.js";
      2 import { isDIrregular, isHIrregular, isLEuIrregular, isLIrregular, isPIrregular, isSIrregular } from "./irregulars.js";
      3 import { merge_rules } from "./merge_rules.js";
      4 export class Conjugator {
      5     constructor() {
      6         this.tense_rules = [];
      7         this.reasons = [];
      8         this.tense_rules = [
      9             { name: "base", rule: (infinitive, regular) => this.base(infinitive, regular) },
     10             { name: "base2", rule: (infinitive, regular) => this.base2(infinitive, regular) },
     11             { name: "base3", rule: (infinitive, regular) => this.base3(infinitive, regular) },
     12             { name: "declarative present informal low", rule: (infinitive, regular) => this.declarativePresentInformalLow(infinitive, regular) },
     13             { name: "declarative present informal high", rule: (infinitive, regular) => this.declarativePresentInformalHigh(infinitive, regular) },
     14             { name: "declarative present formal low", rule: (infinitive, regular) => this.declarativePresentFormalLow(infinitive, regular) },
     15             { name: "declarative present formal high", rule: (infinitive, regular) => this.declarativePresentFormalHigh(infinitive, regular) },
     16             { name: "past base", rule: (infinitive, regular) => this.pastBase(infinitive, regular) },
     17             { name: "declarative past informal low", rule: (infinitive, regular) => this.declarativePastInformalLow(infinitive, regular) },
     18             { name: "declarative past informal high", rule: (infinitive, regular) => this.declarativePastInformalHigh(infinitive, regular) },
     19             { name: "declarative past formal low", rule: (infinitive, regular) => this.declarativePastFormalLow(infinitive, regular) },
     20             { name: "declarative past formal high", rule: (infinitive, regular) => this.declarativePastFormalHigh(infinitive, regular) },
     21             { name: "future base", rule: (infinitive, regular) => this.futureBase(infinitive, regular) },
     22             { name: "declarative future informal low", rule: (infinitive, regular) => this.declarativeFutureInformalLow(infinitive, regular) },
     23             { name: "declarative future informal high", rule: (infinitive, regular) => this.declarativeFutureInformalHigh(infinitive, regular) },
     24             { name: "declarative future formal low", rule: (infinitive, regular) => this.declarativeFutureFormalLow(infinitive, regular) },
     25             { name: "declarative future formal high", rule: (infinitive, regular) => this.declarativeFutureFormalHigh(infinitive, regular) },
     26             { name: "declarative future conditional informal low", rule: (infinitive, regular) => this.declarativeFutureConditionalInformalLow(infinitive, regular) },
     27             { name: "declarative future conditional informal high", rule: (infinitive, regular) => this.declarativeFutureConditionalInformalHigh(infinitive, regular) },
     28             { name: "declarative future conditional formal low", rule: (infinitive, regular) => this.declarativeFutureConditionalFormalLow(infinitive, regular) },
     29             { name: "declarative future conditional formal high", rule: (infinitive, regular) => this.declarativeFutureConditionalFormalHigh(infinitive, regular) },
     30             { name: "inquisitive present informal low", rule: (infinitive, regular) => this.inquisitivePresentInformalLow(infinitive, regular) },
     31             { name: "inquisitive present informal high", rule: (infinitive, regular) => this.inquisitivePresentInformalHigh(infinitive, regular) },
     32             { name: "inquisitive present formal low", rule: (infinitive, regular) => this.inquisitivePresentFormalLow(infinitive, regular) },
     33             { name: "inquisitive present formal high", rule: (infinitive, regular) => this.inquisitivePresentFormalHigh(infinitive, regular) },
     34             { name: "inquisitive past informal low", rule: (infinitive, regular) => this.inquisitivePastInformalLow(infinitive, regular) },
     35             { name: "inquisitive past informal high", rule: (infinitive, regular) => this.inquisitivePastInformalHigh(infinitive, regular) },
     36             { name: "inquisitive past formal low", rule: (infinitive, regular) => this.inquisitivePastFormalLow(infinitive, regular) },
     37             { name: "inquisitive past formal high", rule: (infinitive, regular) => this.inquisitivePastFormalHigh(infinitive, regular) },
     38             { name: "imperative present informal low", rule: (infinitive, regular) => this.imperativePresentInformalLow(infinitive, regular) },
     39             { name: "imperative present informal high", rule: (infinitive, regular) => this.imperativePresentInformalHigh(infinitive, regular) },
     40             { name: "imperative present formal low", rule: (infinitive, regular) => this.imperativePresentFormalLow(infinitive, regular) },
     41             { name: "imperative present formal high", rule: (infinitive, regular) => this.imperativePresentFormalHigh(infinitive, regular) },
     42             { name: "propositive present informal low", rule: (infinitive, regular) => this.propositivePresentInformalLow(infinitive, regular) },
     43             { name: "propositive present informal high", rule: (infinitive, regular) => this.propositivePresentInformalHigh(infinitive, regular) },
     44             { name: "propositive present formal low", rule: (infinitive, regular) => this.propositivePresentFormalLow(infinitive, regular) },
     45             { name: "propositive present formal high", rule: (infinitive, regular) => this.propositivePresentFormalHigh(infinitive, regular) },
     46             { name: "connective if", rule: (infinitive, regular) => this.connectiveIf(infinitive, regular) },
     47             { name: "connective and", rule: (infinitive, regular) => this.connectiveAnd(infinitive, regular) },
     48             { name: "nominal ing", rule: (infinitive, regular) => this.nominalIng(infinitive, regular) },
     49         ];
     50     }
     51     perform(infinitive, regular = false) {
     52         const results = [];
     53         for (const tense of this.tense_rules) {
     54             this.reasons = [];
     55             const conjugation = tense.rule(infinitive, regular);
     56             results.push({
     57                 tense: tense.name,
     58                 conjugation: conjugation.toString(),
     59                 reasons: this.reasons
     60             });
     61         }
     62         return results;
     63     }
     64     dropL(x, y) {
     65         if (getPadchim(x[x.length - 1]) === "ᆯ") {
     66             this.reasons.push("drop ㄹ");
     67             return x.substring(0, x.length - 1) + join(getLead(x[x.length - 1]), getVowel(x[x.length - 1])) + y;
     68         }
     69         throw new Error("Not an L padchim");
     70     }
     71     dropLAndBorrowPadchim(x, y) {
     72         if (getPadchim(x[x.length - 1]) === "ᆯ") {
     73             this.reasons.push(`drop ${getPadchim(x[x.length - 1])} borrow padchim`);
     74             return x.substring(0, x.length - 1) + join(getLead(x[x.length - 1]), getVowel(x[x.length - 1]), getPadchim(y[0])) + y.substring(1);
     75         }
     76         throw new Error("Not an L padchim");
     77     }
     78     merge(x, y) {
     79         for (let i = 0; i < merge_rules.length; i++) {
     80             const rule = merge_rules[i];
     81             const output = rule(x, y);
     82             if (output) {
     83                 this.reasons.push(`${output[0] && output[0] || ""} (${x} + ${y} -> ${output[1]})`);
     84                 return output[1];
     85             }
     86         }
     87         throw new Error("Unable to match on rule");
     88     }
     89     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     90     base(infinitive, _regular = false) {
     91         if (infinitive.endsWith("다")) {
     92             return infinitive.substring(0, infinitive.split("").length - 1);
     93         }
     94         return infinitive;
     95     }
     96     base2(infinitive, regular = false) {
     97         infinitive = this.base(infinitive, regular);
     98         if (infinitive === "아니") {
     99             const geulja = new Geulja("아니");
    100             geulja.hidden_padchim = true;
    101             return geulja;
    102         }
    103         if (infinitive === "뵙")
    104             return "뵈";
    105         if (infinitive === "푸")
    106             return "퍼";
    107         let new_infinitive = infinitive;
    108         if (isHIrregular(infinitive, regular)) {
    109             new_infinitive = this.merge(infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1])), "이");
    110             this.reasons.push(`ㅎ irregular (${infinitive} -> ${new_infinitive})`);
    111         }
    112         else if (isPIrregular(infinitive, regular)) {
    113             let new_vowel = "";
    114             // only some verbs get ㅗ (highly irregular)
    115             if (["묻잡"].includes(infinitive.toString()) || ["돕", "곱"].includes(infinitive[infinitive.length - 1])) {
    116                 new_vowel = "ㅗ";
    117             }
    118             else {
    119                 new_vowel = "ㅜ";
    120             }
    121             new_infinitive = this.merge(infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1])), join("ᄋ", new_vowel));
    122             this.reasons.push(`ㅂ irregular (${infinitive} -> ${new_infinitive})`);
    123         }
    124         else if (isDIrregular(infinitive, regular)) {
    125             new_infinitive = new Geulja(infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1]), "ᆯ"));
    126             new_infinitive.original_padchim = "ᆮ";
    127             this.reasons.push(`ㄷ irregular (${infinitive} -> ${new_infinitive})`);
    128         }
    129         else if (isSIrregular(infinitive, regular)) {
    130             new_infinitive = new Geulja(infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1])));
    131             new_infinitive.hidden_padchim = true;
    132             this.reasons.push(`ㅅ irregular (${infinitive} -> ${new_infinitive} [hidden padchim])`);
    133         }
    134         return new_infinitive;
    135     }
    136     base3(infinitive, regular = false) {
    137         infinitive = this.base(infinitive, regular);
    138         if (infinitive === "아니")
    139             return "아니";
    140         if (infinitive == "푸")
    141             return "푸";
    142         if (infinitive == "뵙")
    143             return "뵈";
    144         if (isHIrregular(infinitive, regular)) {
    145             return infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1]));
    146         }
    147         else if (isPIrregular(infinitive, regular)) {
    148             return infinitive.substring(0, infinitive.length - 1) + join(getLead(infinitive[infinitive.length - 1]), getVowel(infinitive[infinitive.length - 1])) + "우";
    149         }
    150         else {
    151             return this.base2(infinitive, regular);
    152         }
    153     }
    154     declarativePresentInformalLow(infinitive, regular = false, further_use = false) {
    155         infinitive = this.base2(infinitive, regular);
    156         if (!further_use && ((infinitive[infinitive.length - 1] === "이" && !(infinitive instanceof Geulja)) || infinitive === "아니")) {
    157             this.reasons.push("야 irregular");
    158             return infinitive + "야";
    159         }
    160         // 르 irregular
    161         if (regular && infinitive === "이르") {
    162             return "일러";
    163         }
    164         if (isLEuIrregular(infinitive, regular)) {
    165             let new_base = infinitive.substring(0, infinitive.length - 2) + join(getLead(infinitive[infinitive.length - 2]), getVowel(infinitive[infinitive.length - 2]), "ᆯ");
    166             if (["푸르", "이르"].includes(infinitive.substring(infinitive.length - 2))) {
    167                 new_base = new_base + join("ᄅ", getVowel(findVowelToAppend(new_base)));
    168                 this.reasons.push(`irregular stem + ${infinitive} -> ${new_base}`);
    169                 return infinitive + "러";
    170             }
    171             else if (findVowelToAppend(infinitive.substring(0, infinitive.length - 1)) === "아") {
    172                 new_base += "라";
    173                 this.reasons.push(`르 irregular stem change [${infinitive} -> ${new_base}]`);
    174                 return new_base;
    175             }
    176             else {
    177                 new_base += "러";
    178                 this.reasons.push(`르 irregular stem change [${infinitive} -> ${new_base}]`);
    179                 return new_base;
    180             }
    181         }
    182         else if (infinitive[infinitive.length - 1] === "하") {
    183             return this.merge(infinitive, "여");
    184         }
    185         else if (isHIrregular(infinitive, regular)) {
    186             return this.merge(infinitive, "이");
    187         }
    188         return this.merge(infinitive, findVowelToAppend(infinitive));
    189     }
    190     declarativePresentInformalHigh(infinitive, regular = false) {
    191         infinitive = this.base2(infinitive, regular);
    192         if ((infinitive[infinitive.length - 1] === "이" && !(infinitive instanceof Geulja)) || infinitive === "아니") {
    193             this.reasons.push("에요 irregular");
    194             return infinitive + "에요";
    195         }
    196         return this.merge(this.declarativePresentInformalLow(infinitive, regular, true), "요");
    197     }
    198     declarativePresentFormalLow(infinitive, regular = false) {
    199         if (isLIrregular(this.base(infinitive), regular)) {
    200             return this.dropLAndBorrowPadchim(this.base(infinitive, regular), "는다");
    201         }
    202         return this.merge(this.base(infinitive, regular), "는다");
    203     }
    204     declarativePresentFormalHigh(infinitive, regular = false) {
    205         if (isLIrregular(this.base(infinitive), regular)) {
    206             return this.dropLAndBorrowPadchim(this.base(infinitive, regular), "습니다");
    207         }
    208         return this.merge(this.base(infinitive, regular), "습니다");
    209     }
    210     pastBase(infinitive, regular = false) {
    211         const ps = this.declarativePresentInformalLow(infinitive, regular, true);
    212         if (findVowelToAppend(ps) == "아") {
    213             return this.merge(ps, "았");
    214         }
    215         return this.merge(ps, "었");
    216     }
    217     declarativePastInformalLow(infinitive, regular = false) {
    218         return this.merge(this.pastBase(infinitive, regular), "어");
    219     }
    220     declarativePastInformalHigh(infinitive, regular = false) {
    221         return this.merge(this.declarativePastInformalLow(infinitive, regular), "요");
    222     }
    223     declarativePastFormalLow(infinitive, regular = false) {
    224         return this.merge(this.pastBase(infinitive, regular), "다");
    225     }
    226     declarativePastFormalHigh(infinitive, regular = false) {
    227         return this.merge(this.pastBase(infinitive, regular), "습니다");
    228     }
    229     futureBase(infinitive, regular = false) {
    230         if (isLIrregular(this.base(infinitive, regular))) {
    231             return this.dropLAndBorrowPadchim(this.base3(infinitive, regular), "을");
    232         }
    233         return this.merge(this.base3(infinitive, regular), "을");
    234     }
    235     declarativeFutureInformalLow(infinitive, regular = false) {
    236         return this.merge(this.futureBase(infinitive, regular), " 거야");
    237     }
    238     declarativeFutureInformalHigh(infinitive, regular = false) {
    239         return this.merge(this.futureBase(infinitive, regular), " 거예요");
    240     }
    241     declarativeFutureFormalLow(infinitive, regular = false) {
    242         return this.merge(this.futureBase(infinitive, regular), " 거다");
    243     }
    244     declarativeFutureFormalHigh(infinitive, regular = false) {
    245         return this.merge(this.futureBase(infinitive, regular), " 겁니다");
    246     }
    247     declarativeFutureConditionalInformalLow(infinitive, regular = false) {
    248         return this.merge(this.base(infinitive, regular), "겠어");
    249     }
    250     declarativeFutureConditionalInformalHigh(infinitive, regular = false) {
    251         return this.merge(this.base(infinitive, regular), "겠어요");
    252     }
    253     declarativeFutureConditionalFormalLow(infinitive, regular = false) {
    254         return this.merge(this.base(infinitive, regular), "겠다");
    255     }
    256     declarativeFutureConditionalFormalHigh(infinitive, regular = false) {
    257         return this.merge(this.base(infinitive, regular), "겠습니다");
    258     }
    259     inquisitivePresentInformalLow(infinitive, regular = false) {
    260         return this.merge(this.declarativePresentInformalLow(infinitive, regular), "?");
    261     }
    262     inquisitivePresentInformalHigh(infinitive, regular = false) {
    263         return this.merge(this.declarativePresentInformalHigh(infinitive, regular), "?");
    264     }
    265     inquisitivePresentFormalLow(infinitive, regular = false) {
    266         infinitive = this.base(infinitive, regular);
    267         if (isLIrregular(infinitive, regular)) {
    268             return this.dropL(infinitive, "니?");
    269         }
    270         return this.merge(infinitive, "니?");
    271     }
    272     inquisitivePresentFormalHigh(infinitive, regular = false) {
    273         infinitive = this.base(infinitive, regular);
    274         if (isLIrregular(infinitive, regular)) {
    275             return this.dropLAndBorrowPadchim(infinitive, "습니까?");
    276         }
    277         return this.merge(infinitive, "습니까?");
    278     }
    279     inquisitivePastInformalLow(infinitive, regular = false) {
    280         return this.declarativePastInformalLow(infinitive, regular) + "?";
    281     }
    282     inquisitivePastInformalHigh(infinitive, regular = false) {
    283         return this.merge(this.declarativePastInformalHigh(infinitive, regular), "?");
    284     }
    285     inquisitivePastFormalLow(infinitive, regular = false) {
    286         return this.merge(this.pastBase(infinitive, regular), "니?");
    287     }
    288     inquisitivePastFormalHigh(infinitive, regular = false) {
    289         return this.merge(this.pastBase(infinitive, regular), "습니까?");
    290     }
    291     imperativePresentInformalLow(infinitive, regular = false) {
    292         return this.declarativePresentInformalLow(infinitive, regular);
    293     }
    294     imperativePresentInformalHigh(infinitive, regular = false) {
    295         if (isLIrregular(this.base(infinitive, regular))) {
    296             return this.dropL(this.base3(infinitive, regular), "세요");
    297         }
    298         return this.merge(this.base3(infinitive, regular), "세요");
    299     }
    300     imperativePresentFormalLow(infinitive, regular = false) {
    301         return this.merge(this.imperativePresentInformalLow(infinitive, regular), "라");
    302     }
    303     imperativePresentFormalHigh(infinitive, regular = false) {
    304         if (isLIrregular(this.base(infinitive, regular))) {
    305             return this.dropL(this.base3(infinitive, regular), "십시오");
    306         }
    307         return this.merge(this.base3(infinitive, regular), "십시오");
    308     }
    309     propositivePresentInformalLow(infinitive, regular = false) {
    310         return this.declarativePresentInformalLow(infinitive, regular);
    311     }
    312     propositivePresentInformalHigh(infinitive, regular = false) {
    313         return this.declarativePresentInformalHigh(infinitive, regular);
    314     }
    315     propositivePresentFormalLow(infinitive, regular = false) {
    316         return this.merge(this.base(infinitive, regular), "자");
    317     }
    318     propositivePresentFormalHigh(infinitive, regular = false) {
    319         infinitive = this.base(infinitive);
    320         if (isLIrregular(infinitive, regular)) {
    321             return this.dropLAndBorrowPadchim(this.base3(infinitive, regular), "읍시다");
    322         }
    323         return this.merge(this.base3(infinitive, regular), "읍시다");
    324     }
    325     connectiveIf(infinitive, regular = false) {
    326         return this.merge(this.base3(infinitive, regular), "면");
    327     }
    328     connectiveAnd(infinitive, regular = false) {
    329         infinitive = this.base(infinitive, regular);
    330         return this.merge(this.base(infinitive, regular), "고");
    331     }
    332     nominalIng(infinitive, regular = false) {
    333         return this.merge(this.base3(infinitive, regular), "음");
    334     }
    335 }