! ===========================================================================
!
!   RusMCE.h:
!   Системный модуль для русской грамматики
!   (генератор падежей, проверка глаголов, пр.)
!   Source encoding: CP1251
!
!   (c) Gayev D.G. 2003
!
! ---------------------------------------------------------------------------

System_file;

Constant Tlimit = 31;     ! (не больше)
Array  Tbuffer --> 3+TLimit;
Array  Tparse --> Tlimit;

! DL: слегка модифицированная версия для DictionaryLookup
! (из "ParserM.h")

[ DL buf len
  i;

  if (len == 0 || len > Tlimit) return 0;

  Tbuffer-->0 = len;
  for (i = 0: i ~= len: i ++) Tbuffer-->(1+i) = buf-->i;
  Tbuffer-->(1+len) = 0;

  VM_Tokenise(Tbuffer, Tparse);
  return Tparse-->1;
];

! DLx:
! как DL, но для поиска элементов словаря на 'term'

[ DLx buf len term
  i;
  if (len == 0 || len >= Tlimit) return 0;

  Tbuffer-->0 = len+1;
  for (i = 0: i ~= len: i ++) Tbuffer-->(1+i) = buf-->i;
  Tbuffer-->(1+len) = term;
  Tbuffer-->(2+len) = 0;

  VM_Tokenise(Tbuffer, Tparse);
  return Tparse-->1;
];

[ CyrCharToUpperUni ch;
  if (ch >= $0430 && ch <=$044F) {
    return ch - 32;
  }
  if (ch == $0451) {
    return $0401; ! Ё
  }
  return ch;
];

Attribute fem_grammar;      ! (тип склонения женского рода)

Property casegen;     ! (необязательный собственный генератор падежей объекта)

! # падежей (нужно парсеру)
Constant LanguageCases = 6;

! Идентификаторы падежей
Constant csOff =  0;  ! Нет (отключить генератор падежей)

Constant csNom =  1;  ! Именительный падеж  (= номинатив)
Constant csGen =  2;  ! Родительный падеж (= генитив)
Constant csDat =  3;  ! Дательный падеж (= датив)
Constant csAcc =  4;  ! Винительный падеж (= аккузатив)
Constant csIns =  5;  ! Творительный падеж  (= инструментал)
Constant csPre =  6;  ! Предложный падеж  (= препозитив)

! Падеж по умолчанию для вывода ShortName
Global csDflt = csNom;

! Категории объекта
Constant ocSM =   1;  ! Единственное число / Мужской род
Constant ocSF =   2;  ! Единственное число / Женский род
Constant ocSN =   3;  ! Единственное число / Средний род
Constant ocPL =   4;  ! Множественное число

! Категория объекта
! определить категорию
[ objID obj;

  if (obj has pluralname)   return ocPL;
  else if (obj has neuter)  return ocSN;
  else if (obj has female)  return ocSF;
  else if (obj has fem_grammar) return ocSF;
  else        return ocSM;
];

! (Режим отладки генератора падежей)
Constant DEBUG_CASES = false;

! Основная таблица падежных суффиксов

Constant ADJ_TOP = 64;

! SM_Req: запрос к таблице падежей (#nreq)
! (ед. число, мужской род)
[ SM_Req csID nreq;

  switch (nreq) {

  ! II склонение, на согласный:
  ! (дом, снег, баран, кнут, мир, парад):
  ! -, -а, -у, -, -ом, -е
  0:  switch (csID) {
  csNom:  return 0;
  csGen:  return 'а//';
  csDat:  return 'у//';
  csAcc:  return 0;
  csIns:  return 'ом';
  csPre:  return 'е//';
  }

  ! II склонение, на -ь:
  ! (снегирь, апрель, пароль, кремень, фонарь, окунь, медведь):
  ! -ь, -я, -ю, -ь, -ем, -е
  1:  switch (csID) {
  csNom:  return 'ь//';
  csGen:  return 'я//';
  csDat:  return 'ю//';
  csAcc:  return 'ь//';
  csIns:  return 'ем';
  csPre:  return 'е//';
  }

  ! II склонение, на -й:
  ! (иней, лакей, зной, май):
  ! -й, -я, -ю, -й, -ем, -е
  2:  switch (csID) {
  csNom:  return 'й//';
  csGen:  return 'я//';
  csDat:  return 'ю//';
  csAcc:  return 'й//';
  csIns:  return 'ем';
  csPre:  return 'е//';
  }

  ! II склонение, на -ий:
  ! (литий, планетарий, крематорий, Евгений, Василий):
  ! -ий, -ия, -ию, -ий, -ием, -ии
  3:  switch (csID) {
  csNom:  return 'ий';
  csGen:  return 'ия';
  csDat:  return 'ию';
  csAcc:  return 'ий';
  csIns:  return 'ием';
  csPre:  return 'ии';
  }

  ! Прилагательные, на -ый:
  ! (красный, белый, сильный, здоровый):
  ! -ый, -ого, -ому, -ый, -ым, -ом
  64:   switch (csID) {
  csNom:  return 'ый';
  csGen:  return 'ого';
  csDat:  return 'ому';
  csAcc:  return 'ый';
  csIns:  return 'ым';
  csPre:  return 'ом';
    }

  ! Прилагательные, на -ой:
  ! (большой, злой, плохой, лихой, нагой):
  ! -ой, -ого, -ому, -ой, -ым, -ом
  65:   switch (csID) {
  csNom:  return 'ой';
  csGen:  return 'ого';
  csDat:  return 'ому';
  csAcc:  return 'ой';
  csIns:  return 'ым';
  csPre:  return 'ом';
  }

  ! Прилагательные, на -ий:
  ! (синий, средний, хороший):
  ! -ий, -его, -ему, -ий, -им, -ем
  66:   switch (csID) {
  csNom:  return 'ий';
  csGen:  return 'его';
  csDat:  return 'ему';
  csAcc:  return 'ий';
  csIns:  return 'им';
  csPre:  return 'ем';
  }

  } ! switch (nreq)

return -1;
];  ! SM_Req

! SF_Req: запрос к таблице падежей (#nreq)
! (ед. число, женский род)
[ SF_Req csID nreq;

  switch (nreq) {

  ! I склонение, на -а:
  ! (вода, стрела, зола, комната):
  ! -а, -ы, -е, -у, -ой, -е
  0:  switch (csID) {
  csNom:  return 'а//';
  csGen:  return 'ы//';
  csDat:  return 'е//';
  csAcc:  return 'у//';
  csIns:  return 'ой';
  csPre:  return 'е//';
  }

  ! I склонение, на -я:
  ! (земля, башня):
  ! -я, -и, -е, -ю, -ей, -е
  1:  switch (csID) {
  csNom:  return 'я//';
  csGen:  return 'и//';
  csDat:  return 'е//';
  csAcc:  return 'ю//';
  csIns:  return 'ей';
  csPre:  return 'е//';
    }

  ! III склонение, на -ь:
  ! (метель, ночь, медь, осень):
  ! -ь, -и, -и, -ь, -ью, -и
  2:  switch (csID) {
  csNom:  return 'ь//';
  csGen:  return 'и//';
  csDat:  return 'и//';
  csAcc:  return 'ь//';
  csIns:  return 'ью';
  csPre:  return 'и//';
    }

  ! I склонение, на -ия:
  ! (сессия, лекция, пародия, агония):
  ! -ия, -ии, -ии, -ию, -ией, -ии
  3:  switch (csID) {
  csNom:  return 'ия';
  csGen:  return 'ии';
  csDat:  return 'ии';
  csAcc:  return 'ию';
  csIns:  return 'ией';
  csPre:  return 'ии';
    }

  ! Прилагательные, на -ая:
  ! (красная, большая, приятная):
  ! -ая, -ой, -ой, -ую, -ой, -ой
  64: switch (csID) {
  csNom:  return 'ая';
  csGen:  return 'ой';
  csDat:  return 'ой';
  csAcc:  return 'ую';
  csIns:  return 'ой';
  csPre:  return 'ой';
    }

  ! Прилагательные, на -яя:
  ! (синяя, средняя):
  ! -яя, -ей, -ей, -юю, -ей, -ей
  65: switch (csID) {
  csNom:  return 'яя';
  csGen:  return 'ей';
  csDat:  return 'ей';
  csAcc:  return 'юю';
  csIns:  return 'ей';
  csPre:  return 'ей';
    }

  } ! switch (nreq)

return -1;
];  ! SF_Req

! SN_Req: запрос к таблице падежей (#nreq)
! (ед. число, средний род)
[ SN_Req csID nreq;

  switch (nreq) {

  ! II склонение, на -о:
  ! (облако, озеро, утро, ведро, зеркало):
  ! -о, -а, -у, -о, -ом, -е
  0:  switch (csID) {
  csNom:  return 'о//';
  csGen:  return 'а//';
  csDat:  return 'у//';
  csAcc:  return 'о//';
  csIns:  return 'ом';
  csPre:  return 'е//';
    }

  ! II склонение, на -е:
  ! (поле, ложе):
  ! -е, -я, -ю, -е, -ем, -е
  1:  switch (csID) {
  csNom:  return 'е//';
  csGen:  return 'я//';
  csDat:  return 'ю//';
  csAcc:  return 'е//';
  csIns:  return 'ем';
  csPre:  return 'е//';
    }

  ! II склонение, на -ие:
  ! (известие, занятие, приветствие, мышление):
  ! -ие, -ия, -ию, -ие, -ием, -ии
  2:  switch (csID) {
  csNom:  return 'ие';
  csGen:  return 'ия';
  csDat:  return 'ию';
  csAcc:  return 'ие';
  csIns:  return 'ием';
  csPre:  return 'ии';
    }

  ! Разносклоняемое, на -я:
  ! (время, племя, имя, знамя):
  ! -я, -ени, -ени, -я, -енем, -ени
  3:  switch (csID) {
  csNom:  return 'я//';
  csGen:  return 'ени';
  csDat:  return 'ени';
  csAcc:  return 'я//';
  csIns:  return 'енем';
  csPre:  return 'ени';
    }

  ! Прилагательные, на -ое:
  ! (красное, малое, мертвое):
  ! -ое, -ого, -ому, -ое, -ым, -ом
  64: switch (csID) {
  csNom:  return 'ое';
  csGen:  return 'ого';
  csDat:  return 'ому';
  csAcc:  return 'ое';
  csIns:  return 'ым';
  csPre:  return 'ом';
    }

  ! Прилагательные, на -ее:
  ! (синее, среднее):
  ! -ее, -его, -ему, -ее, -им, -ем
  65: switch (csID) {
  csNom:  return 'ее';
  csGen:  return 'его';
  csDat:  return 'ему';
  csAcc:  return 'ее';
  csIns:  return 'им';
  csPre:  return 'ем';
    }

  } ! switch (nreq)

return -1;
];  ! SN_Req

! PL_Req: запрос к таблице падежей (#nreq)
! (мн. число)
[ PL_Req csID nreq;

  switch (nreq) {

  ! TMP: нерегулярность Gen Pl

  ! Множественное, на -и:
  ! (овраги, цели, недели):
  ! -и, -ов/-ей/-, -ам/-ям, -и, -ами/-ями, -ах/-ях
  0:  switch (csID) {
  csNom:  return 'и//';
  csGen:  return 'ев';      ! TMP: не всегда! - ей, 
  csDat:  return 'ям';
  csAcc:  return 'и//';
  csIns:  return 'ями';
  csPre:  return 'ях';
    }

  ! Множественное, на -ы:
  ! (работы, солдаты, заботы, закаты):
  ! -ы, -ов/-ей/-, -ам/-ям, -и, -ами/-ями, -ах/-ях
  1:  switch (csID) {
  csNom:  return 'ы//';
  csGen:  return 'ов';      ! TMP: не всегда!
  csDat:  return 'ам';
  csAcc:  return 'ы//';
  csIns:  return 'ами';
  csPre:  return 'ах';
    }

  ! Множественное, на -а:
  ! (облака, дома, номера):
  ! -а, -ов, -ам, -а, -ами, -ах
  2:  switch (csID) {
  csNom:  return 'а//';
  csGen:  return 'ов';
  csDat:  return 'ам';
  csAcc:  return 'а//';
  csIns:  return 'ами';
  csPre:  return 'ах';
    }

  ! Множественное, на -я:
  ! (поля, моря, якоря):
  ! -я, -ей, -ям, -я, -ями, -ях
  3:  switch (csID) {
  csNom:  return 'я//';
  csGen:  return 'ей';
  csDat:  return 'ям';
  csAcc:  return 'я//';
  csIns:  return 'ями';
  csPre:  return 'ях';
    }

  ! Множественное, на -ья:
  ! (листья, деревья, коренья, стулья, перья):
  ! -ья, -ьев, -ьям, -ья, -ьями, -ьях
  4:  switch (csID) {
  csNom:  return 'ья';
  csGen:  return 'ьев';
  csDat:  return 'ьям';
  csAcc:  return 'ья';
  csIns:  return 'ьями';
  csPre:  return 'ьях';
    }

  ! Множественное, на -ия:
  ! (изделия, решения, стремления, понятия):
  ! -ия, -ий, -иям, -ия, -иями, -иях
  5:  switch (csID) {
  csNom:  return 'ия';
  csGen:  return 'ий';
  csDat:  return 'иям';
  csAcc:  return 'ия';
  csIns:  return 'иями';
  csPre:  return 'иях';
    }

  ! Множественное, на -ии:
  ! (станции, реляции, апатии):
  ! -ия, -ий, -иям, -ия, -иями, -иях
  6:  switch (csID) {
  csNom:  return 'ии';
  csGen:  return 'ий';
  csDat:  return 'иям';
  csAcc:  return 'ии';
  csIns:  return 'иями';
  csPre:  return 'иях';
    }

  ! Прилагательные, на -ые:
  ! (красные, опасные, тяжелые):
  ! -ые, -ых, -ым, -ые, -ыми, -ых
  64: switch (csID) {
  csNom:  return 'ые';
  csGen:  return 'ых';
  csDat:  return 'ым';
  csAcc:  return 'ые';
  csIns:  return 'ыми';
  csPre:  return 'ых';
    }

  ! Прилагательные, на -ие:
  ! (синие, легкие, пологие):
  ! -ие, -их, -им, -ие, -ими, -их
  65: switch (csID) {
  csNom:  return 'ие';
  csGen:  return 'их';
  csDat:  return 'им';
  csAcc:  return 'ие';
  csIns:  return 'ими';
  csPre:  return 'их';
  }

  } ! switch (nreq)

return -1;
];  ! PL_Req

! Ending PostProcess (as after 'prch')
[ EndingPost u prch;
  if (u) {
  ! Модификация после 'г'/'к'/'х'/'ж'/'ш'/'ч'/'щ':
  ! сапог -> сапоги, клубок -> клубки, сполох -> сполохи
  if (prch == 'г' or 'к' or 'х' or 'ж' or 'ш' or 'ч' or 'щ')
    switch (u) {
    'ы//':    return 'и//';
    'ый':   return 'ий';
    'ые':   return 'ие';
    'ым':   return 'им';
    'ыми':    return 'ими';
    'ых':   return 'их';
    }

  ! TMP: больше вариантов!

  ! после ц: ов, ом -> ев, ем (если окончание безударное)
  ! после ж, ш: я -> а, ю -> у
  }

  return u;
];

! Ending PreProcess (as after 'prch')
[ EndingPre u prch;
  if (u) {
    if (prch == 'г' or 'к' or 'х' or 'ж' or 'ш' or 'ч' or 'щ')
      switch (u) {
        'и//':    return 'ы//';
        'ий':   return 'ый';
        'ие':   return 'ые';
        'им':   return 'ым';
        'ими':    return 'ыми';
        'их':   return 'ых';
      }
  }

  return u;
  ];

! CCaseEnd:
! перевести падежное окончание (start..end) в соответствующий падеж (csID)
! ocFN - генератор окончаний; disc - дискриминатор; prch - символ перед окончанием
[ CCaseEnd start end csID ocFN disc prch
  i u v;

  v = EndingPre (DL (start, (end-start)/4), prch);

  ! Выполнить поиск по таблицам...
  for (i = 0: : ++ i) {
    u = indirect (ocFN, csNom, i);

    if (u == -1) { 
      if (i >= ADJ_TOP) ! (больше нет вариантов)
        { print "?"; return; }
      else      ! (прилагательные)
       { i = ADJ_TOP - 1; }
    } else if (u == v) {

      if (disc) { -- disc; continue; }

      if (csID ~= csNom) u = EndingPost (indirect (ocFN, csID, i), prch);
      else u = EndingPost (u, prch);

      if (u) print (address) u;
      return;
    }
  }
];

! Специфичная версия для 'LanguageRefers'

[ EndingLookup addr len csID
  v u ocFN i;

  if (csID == 0) rtrue;     !! (любой падеж допустим)

  if (len ~= 0) {
    v = DL (addr, len);
    if (v == 0) rfalse;
  }
  else v = 0;

  ocFN = SM_Req;

  for (::) {
    for (i = 0: : ++ i) {
      u = indirect (ocFN, csID, i);

      if (u == -1) { 
        if (i >= ADJ_TOP) break;  ! (больше нет вариантов)
        else i = ADJ_TOP - 1;   ! (прилагательные)
      }
      else if (u == v) rtrue;
    }

    switch (ocFN) {
      SM_Req: ocFN = SF_Req;
      SF_Req: ocFN = SN_Req;
      SN_Req: ocFN = PL_Req;
      PL_Req: rfalse;     ! (больше нет вариантов)
    }
  }

  rfalse;
];

!
! Проверить корректность глагольного суффикса
!
[ IsVerbSuffix start len;

  ! ("-ся|-сь": убрать)
  if (len >= 2 && start-->(len-2) == 'с' && start-->(len-1) == 'я' or 'ь')
    len = len - 2;

  if (len == 1 && start-->0 == 'и' or 'ь') rtrue;

  ! "[аеиоуыюя]([ийь]|ть)"
  if (start-->0 == 'а' or 'е' or 'и' or 'о' or 'у' or 'ы' or 'ю' or 'я') {
    start = start + 4;
    len--;

    if (len == 1 && start-->0 == 'й') rtrue;
  }

  ! "ти|ть"
  if (len == 2 && start-->0 == 'т' && start-->1 == 'ь' or 'и') rtrue;

  ! "нь|ни|нуть"
  if (start-->0 == 'н' &&
     ((len == 2 && start-->1 == 'и' or 'ь') ||
      (len == 4 && start-->1 == 'у' && start-->2 == 'т' && start-->3 == 'ь')))
    rtrue;

  rfalse;
];

!
! Проверить корректность глагольного префикса
!
[ IsVerbPrefix start len
  w;

  w = DL (start, len);
  if (w == 0) return false;

  return w ==
  'в//' or 'вз' or 'во' or 'вы' or
  'до' or
  'за' or
  'из' or 'ис' or
  'на' or
  'о//' or 'об' or 'обо' or 'от' or 'ото' or
  'по' or 'под' or 'подо' or 'пре' or 'при' or 'про' or 'пере' or
  'раз' or 'рас' or
  'с//' or 'со' or 'съ' or 'у//';
];

!
! Проверить корректность глагола (#wnum)
!
[ LanguageIsVerb buffer parse wnum
  adr beg len end w;

  adr = buffer + (parse-->(wnum*3))*4; 
  len = parse-->(wnum*3-1);

  w = DLx (adr, len, '!');
  if (w) return w;
  
  for (end = len: end > 0: -- end) {
    if (end == len || IsVerbSuffix (adr + end * 4, len - end))
      for (beg = 0: beg < end: ++ beg)
        if (beg == 0 || IsVerbPrefix (adr, beg)) {
          w = DL (adr + beg * 4, end - beg);
          if (w ~= 0 && (w->#dict_par1 & 1) ~= 0)
            return w;       ! (verb entry found)
        }
  }

  return 0;
];

! Расшифровка глаголов (LanguageVerb):
! просмотреть объекты в VerbDepot

[ LanguageVerb word
  obj;

  objectloop (obj in VerbDepot) {
  if (WordInProperty (word, obj, name))
    { print (object) obj; rtrue; }
  }

  rfalse;
];

! Падеж по умолчанию для вывода LanguageRefers
Global csLR = 0;
Global csLRU = 0;

! Обработчик соответствующего символа парсера
[ c_token idtok csID
  retval;

  csLR = csID;
  csLRU = csID; ! последний падеж, который вызывался

  retval = ParseToken (ELEMENTARY_TT, idtok);

  csLR = 0;

  return retval;
];

! LanguageRefers
! Запрос от парсера:
! может ли слово #wnum в данном контексте обращаться к объекту obj?

[ LanguageRefers obj wnum
  adr len end w csID;

  adr = WordAddress(wnum); len = WordLength(wnum);

  ! Для компасных направлений -- упрощенная обработка
  if (parent (obj) == Compass) {
    w = DL (adr, len);
    if (w ~= 0 && WordInProperty (w, obj, name)) rtrue;
    rfalse;
  }

  ! Для мужских одушевленных предметов Acc -> Gen
  csID = csLR;
  if (csID == csAcc && obj has animate && obj has male && obj hasnt fem_grammar)
    csID = csGen;

  for (end = len: end ~= 0: -- end) {
    w = DL (adr, end);
    if (w ~= 0 && WordInProperty (w, obj, name) && EndingLookup (adr + end * 4, len - end, csID))
      rtrue;
  }

  rfalse;
];

Constant ScrLen = 200;
Array Scratch --> ScrLen;

! CCase:
! перевести имя объекта (obj) в соответствующий падеж (csID)

[ CCase obj csID ucase as_obj
  i dlm limit ocFN;

  #iftrue DEBUG_CASES;

  ! (отладочный вывод)
  csLabel (csID);
  print " (", (object) obj, ")";

  #ifnot;
  
  if (as_obj == false && obj == player) {
    if (ucase) {
      switch (csID) {
        csNom: print "Ты";
        csGen: print "Себя";
        csDat: print "Себе";
        csAcc: print "Себя";
        csIns: print "Собой";
        csPre: print "Себе";
      }
    } else {
      switch (csID) {
        csNom: print "ты";
        csGen: print "себя";
        csDat: print "себе";
        csAcc: print "себя";
        csIns: print "собой";
        csPre: print "себе";
      }
    }
    return;
  }

  switch (objID (obj)) {
    ocSM: ocFN = SM_Req;
    ocSF: ocFN = SF_Req;
    ocSN: ocFN = SN_Req;
    ocPL: ocFN = PL_Req;
    default:  return;
  }

  ! Для мужских одушевленных предметов Acc -> Gen
  if (csID == csAcc && obj has animate && 
      obj has male && obj hasnt fem_grammar)
    csID = csGen;

  if (csID ~= 0) {
    Scratch-->0 = VM_PrintToBuffer(Scratch, ScrLen, obj);
    
    if (ucase) {
      Scratch-->1 = CyrCharToUpperUni(Scratch-->1);
    }

    dlm = 0;
    limit = Scratch-->0;
    for (i = 1: i <= limit: ++ i) {
      if (Scratch-->i == '/') {
        if (dlm == 0) { 
          dlm = Scratch + i * 4; 
        } else {  
          CCaseF (obj, ocFN, csID, dlm + 4, Scratch + i * 4);
          dlm = 0;
        }
      } else {  
        if (dlm ~= 0 && Scratch-->i == ' ') {
          CCaseF (obj, ocFN, csID, dlm + 4, Scratch + i * 4);
          dlm = 0;
        }

        if (dlm == 0) print (char) (Scratch-->i);
      }
    }
!
    if (dlm ~= 0) { 
      CCaseF (obj, ocFN, csID, dlm + 4, Scratch + i * 4); 
    }
  } else {
    print (object) obj;
  }

  #endif;
];

[ CCaseF obj ocFN csID beg end disc;

  ! (discriminator present?)
  while (end-->(-1) == '!') { -- end; ++ disc; }

  if (obj provides casegen && obj.casegen (beg, end, csID));
  else CCaseEnd (beg, end, csID, ocFN, disc, beg-->(-2));

];

! Вывод краткого имени объекта (через CCase)
[ LanguagePrintShortName obj
  sn;

  sn = short_name;

  if (obj provides sn && PrintOrRun(obj, sn, 1) ~= 0) rtrue;

  CCase (obj, csDflt, false);

  rtrue;
];

! Вывод списка в падеже csID
[ WriteListFromCase obj flag csID
  rval csSV;

  csSV = csDflt; csDflt = csID;

  rval = WriteListFrom (obj, flag);
  
  csDflt = csSV;
  return rval;
];

! Подходящее местоимение для 'obj'
[ Pronoun obj;
  print (string) (
    IIF (obj == player, "Ты", IIF (obj has pluralname, "Они",
    IIF (obj has female, "Она", IIF (obj has neuter, "Оно", "Он")))));
];

[ PronounS obj;
  print (string) (
    IIF (obj == player, "ты", IIF (obj has pluralname, "они",
    IIF (obj has female, "она", IIF (obj has neuter, "оно", "он")))));
];

! Окончание краткой формы прилагательных/причастий, согласованных с 'obj'
! ("открыт[а|о|ы]", "пуст[а|о|ы]")
[ SAEnd obj;

  switch (objID (obj)) {
    ocSM: ;
    ocSF: print (address) 'а//';
    ocSN: print (address) 'о//';
    ocPL: print (address) 'ы//';
  }
];

! Окончание глаголов, согласованных с 'obj':
! (f1 ? 1-ое : 2-ое) спряжение;
!  f2 ? 'ют'/'ят' : 'ут'/'ат'
[ VEnd obj f1 f2;

  print (address) IIF (obj has pluralname,
    IIF (f1,
      IIF (f2, 'ют', 'ут'),
      IIF (f2, 'ят', 'ат')),
    IIF (f1, 'ет', 'ит'));
];

! Окончание глаголов: '-ет'/'-ут'
[ V1aEnd x; VEnd (x,  true,  false); ];

! Окончание глаголов: '-ет'/'-ют'
[ V1bEnd x; VEnd (x,  true, true); ];

! Окончание глаголов: '-ит'/'-ат'
[ V2aEnd x; VEnd (x, false,  false); ];

! Окончание глаголов: '-ит'/'-ят'
[ V2bEnd x; VEnd (x, false, true); ];

! Окончание глаголов в прошедшем времени
[ VPEnd noun;
  if (noun has pluralname)  {print "и"; rtrue;}
  else if (noun has neuter) {print "о"; rtrue;}
  else if (noun has female) {print "а"; rtrue;}
  else        {print ""; rtrue;}
];

!
! Обработчик беглых гласных
! (возвращает true если обработана)
!
[ ICVowel csID beg end ch0 ch1;

  if ((beg == end && ch0 == 0) || (beg + 4 == end && beg-->0 == ch0)) {
    if (csID == csNom || csID == csAcc) { 
      if (ch0) print (char) ch0;
    }
    else { 
      if (ch1) print (char) ch1;
    }
    rtrue;
  }

  rfalse;
];

[ AEnd noun;
  if (noun has pluralname)  {print "ые"; rtrue;}
  else if (noun has neuter) {print "ое"; rtrue;}
  else if (noun has female) {print "ая"; rtrue;}
  else        {print "ый"; rtrue;}
  ]; 
[ AEnd2 noun;
  if (noun has pluralname)  {print "ие"; rtrue;}
  else if (noun has neuter) {print "ое"; rtrue;}
  else if (noun has female) {print "ая"; rtrue;}
  else        {print "ий"; rtrue;}
  ]; 
[ AEnd3 noun;
  if (noun has pluralname)  {print "ие"; rtrue;}
  else if (noun has neuter) {print "ое"; rtrue;}
  else if (noun has female) {print "ая"; rtrue;}
  else        {print "ой"; rtrue;}
  ]; 
[ PEnding1 n;
  if (n has pluralname) {print "ими"; rtrue;}
  if (n has female) {print "ой"; rtrue;}
  print "им"; rtrue;
];
[ PEnding2 n;
  if (n has pluralname) {print "ыми"; rtrue;}
  if (n has female) {print "ой"; rtrue;}
  print "ым"; rtrue;
];
[ GenIt n;
  if (n has female) {print "её"; rtrue;}
  if (n has pluralname) {print "их"; rtrue;}
  print "его"; rtrue;
];
[ GenIt2 n;
  print "н";
  if (n has female) {print "её"; rtrue;}
  if (n has pluralname) {print "их"; rtrue;}
  print "его"; rtrue;
];
[ DatIt n;
  if (n has female) {print "ей"; rtrue;}
  if (n has pluralname) {print "им"; rtrue;}
  print "ему"; rtrue;
]; 
[ DatIt2 n;
  print "н";
  if (n has female) {print "ей"; rtrue;}
  if (n has pluralname) {print "им"; rtrue;}
  print "ему"; rtrue;
]; 
[ InsIt n;
  if (n has female) {print "ей"; rtrue;}
  if (n has pluralname) {print "ими"; rtrue;}
  print "им"; rtrue;
]; 
[ InsIt2 n;
  print "н";
  if (n has female) {print "ей"; rtrue;}
  if (n has pluralname) {print "ими"; rtrue;}
  print "им"; rtrue;
]; 
