Blob Blame History Raw
# Suomi-malaga, suomen kielen muoto-opin kuvaus.
#
# Tekijänoikeus © 2006-2011 Hannu Väisänen (Etunimi.Sukunimi@uef.fi)
#
# Tämä ohjelma on vapaa; tätä ohjelmaa on sallittu levittää
# edelleen ja muuttaa GNU yleisen lisenssin (GPL lisenssin)
# ehtojen mukaan sellaisina kuin Free Software Foundation
# on ne julkaissut; joko Lisenssin version 2, tai (valinnan
# mukaan) minkä tahansa myöhemmän version mukaisesti.
#
# Tätä ohjelmaa levitetään siinä toivossa, että se olisi
# hyödyllinen, mutta ilman mitään takuuta; ilman edes
# hiljaista takuuta kaupallisesti hyväksyttävästä laadusta tai
# soveltuvuudesta tiettyyn tarkoitukseen. Katso GPL
# lisenssistä lisää yksityiskohtia.
#
# Tämän ohjelman mukana pitäisi tulla kopio GPL
# lisenssistä; jos näin ei ole, kirjoita osoitteeseen Free
# Software Foundation Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Tämän ohjeman linkittäminen staattisesti tai dynaamisesti
# muihin moduuleihin on ohjelmaan perustuvan teoksen
# tekemistä, joka on siis GPL lisenssin ehtojen alainen.
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Linking this program statically or dynamically with other modules is
# making a combined work based on this program.  Thus, the terms and
# conditions of the GNU General Public License cover the whole
# combination.

initial <>, rules # laatusana,
                  nimi_laatusana,
                  lukusana,
                  sanan_alku;

include "suomi.inc";
###include "sanat/luvut.mor";

define @teonsana := <teonsana, kieltosana>;

define @ehtotapa := <ehtotapa, ehtotapa_ttA, ehtotapa_tA>;

define @mahtotapa := <mahtotapa_le, mahtotapa_ne, mahtotapa_re, mahtotapa_se,
                      mahtotapa_le_kielto, mahtotapa_ne_kielto,
                      mahtotapa_re_kielto, mahtotapa_se_kielto,
                      mahtotapa_ttA, mahtotapa_tA>;

define @käskytapa := <käskytapa, käskytapa_kielto, käskytapa_ttA,
                      käskytapa_tA>;

define @nimitapa_1 := <nimitapa_1_A,  nimitapa_1_dA, nimitapa_1_lA,
                       nimitapa_1_nA, nimitapa_1_rA, nimitapa_1_tA>;
define @nimitapa_2 := <nimitapa_2, nimitapa_2_ttA, nimitapa_2_tA>;
define @nimitapa_3 := <nimitapa_3, nimitapa_3_saama, nimitapa_3_ttA, nimitapa_3_tA>;

define @voittoaste := <voittoaste>;
define @yliaste    := <yliaste>;


define @tekijämuodot := @kestämän_tekijäpääte + @tositavan_tekijäpääte_4 + @kertoman_tekijäpääte
                        + @ehtotapa + @mahtotapa + @käskytapa;

# Neljäs nimitapa katsotaan teonsanasta johdetuksi nimisanaksi.
define @nimitavat := @nimitapa_1 + @nimitapa_2 + @nimitapa_3 + <nimitapa_5>;
#define @nimitavat := @nimitapa_1 + @nimitapa_2 + @nimitapa_3 + <nimitapa_4, nimitapa_5>;

define @laatutavat := @laatutapa_1 + @laatutapa_2;



# Sääntö sanan_alku on muokattu Harri Pitkäsen Voikko-versiosta.
#
combi_rule sanan_alku ($vasen, $oikea, $sana):
####define $profile := transmit ("sanan_alku ($vasen, $oikea, $sana):");
#  define $a := transmit ($vasen);
#  define $b := transmit ($oikea);
#  define $c := transmit ($sana);

#if ($oikea.luokka in <lyhenne, huudahdussana, sidesana, etuliite, teonsana, kieltosana,
#                      asemosana, seikkasana, suhdesana, nimisana> + @erisnimi) then
#  define $b := transmit ("§§§ " + value_string($oikea.luokka));
#end;

  define $res := <[alku: $sana] + $oikea>;

  if $oikea.luokka = nimisana then
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (yhdyssana in $oikea.jatko) and
        (($oikea.tiedot = nil) or not ((ei_ys in $oikea.tiedot) or (ei_ysa in $oikea.tiedot))) then
      result $res, rules yhdyssana;
    end;
    if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
      result $res, rules nimisanasta_johdettu_laatusana;
    end;
    if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
      result $res, rules nimisanasta_johdettu_seikkasana;
    end;
    if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
      result $res, rules nimi_laatusanan_johdin;
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules sijapääte;

  elseif ($oikea.luokka = laatusana) then
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    # Arka+mainen, halpa+mainen, oiva+llinen, täyde+llinen (= täysi + johdin_llinen), yms.
    if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
      result $res, rules nimi_laatusanan_johdin;
    end;
    if (@voittoaste + @yliaste * $oikea.jatko /= <>) then
      result $res, rules voitto_yliaste;
    end;
    if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
      result $res, rules laatusanasta_johdettu_nimisana;
    end;
    if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
      result $res, rules laatusanasta_johdettu_laatusana;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
#    if (yhdyssana in $oikea.jatko) then
    if (yhdyssana in $oikea.jatko) and
        (($oikea.tiedot = nil) or not ((ei_ys in $oikea.tiedot) or (ei_ysa in $oikea.tiedot))) then
      result $res, rules yhdyssana;
#      result $res, rules inen_päätteinen_laatusana;
    end;
    result $res, rules sijapääte;

  elseif ($oikea.luokka = teonsana) then
#define $a := transmit ($oikea);
    if (liitesana in $oikea.jatko) then
      if (($oikea.tapaluokka = käskytapa) and
          ($oikea.tekijä = 2) and
          ($oikea.luku = yksikkö)) then
        result $res, rules liitesana_ei_kO;
      else
        result $res, rules liitesana;
      end;
    end;
    if (loppu in  $oikea.jatko) then
      result $res, accept;
    end;
    if (tavuviiva in  $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (@teonsanasta_johdettu_teonsana * $oikea.jatko /= <>) then
      result $res, rules teonsanan_johdos_teonsana;
    end;
#    if (@tekijämuodot * $oikea.jatko /= <>) then
#      result $res, rules tekijämuodot;
#    end;
#    if (@nimitavat * $oikea.jatko /= <>) then
#      result $res, rules nimitavat;
#    end;
#    if (@laatutavat * $oikea.jatko /= <>) then
#      result $res, rules laatutavat;
#    end;
#    result $res, rules teonsanan_johdos_nimisana, teonsanan_johdos_laatusana;
# Tämä on nopeampi kuin kaikki erikseen!
    result $res, rules tekijämuodot, nimitavat, laatutavat,
                       teonsanan_johdos_nimisana, teonsanan_johdos_laatusana;
  elseif $oikea.luokka = lyhenne then
    if (kaksoispiste in $oikea.jatko) then
      result $res, rules kaksoispiste;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
#    result $res, rules kaksoispiste, tavuviiva, loppu;
  elseif $oikea.luokka in @erisnimi then
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if ((johdin_lAinen in $oikea.jatko or johdin_mAinen in $oikea.jatko)
        and ($oikea.luokka in <paikannimi, sukunimi>)) then
      result $res, rules paikannimen_ja_sukunimen_lainen_johdin;
    end;
    result $res, rules erisnimen_sijapääte;
  elseif $oikea.luokka in <huudahdussana, sidesana> then
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  elseif $oikea.luokka = etuliite then
    if (kieltosana in $oikea.jatko) then
      result $res, rules kieltosana;
    end;
    if (nimisana in $oikea.jatko) then
      result $res, rules nimisana, teonsanasta_johdettu_nimisana,
                         paikannimen_ja_sukunimen_lainen_johdos;
      if (not (teonsana in $oikea.jatko)) then
        result $res, rules teonsana_teonsanasta_johdettu_nimisana;
      end;
    end;
    if (laatusana in $oikea.jatko) then
      result $res, rules laatusana, teonsanasta_johdettu_laatusana;
      if (not (teonsana in $oikea.jatko)) then
        result $res, rules teonsana_teonsanasta_johdettu_laatusana;
      end;
    end;
    if (nimi_laatusana in $oikea.jatko) then
      result $res, rules nimi_laatusana;
      if (not (nimisana in $oikea.jatko)) then
        result $res, rules nimisana, teonsanasta_johdettu_nimisana;
      end;
      if (not (laatusana in $oikea.jatko)) then
        result $res, rules laatusana, teonsanasta_johdettu_laatusana;
      end;
    end;
    if (teonsana in $oikea.jatko) then
      result $res, rules teonsana;
    end;
    # Tavuviiva ja etuliite ovat aina jatkossa eli niitä ei tarvitse testata.
    result $res, rules tavuviiva, etuliite;
  elseif $oikea.luokka = kieltosana then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana, liitesana_kä, tavuviiva;
  elseif $oikea.luokka = asemosana then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules asemosanan_sijapääte, liitesana, tavuviiva, voitto_yliaste;
  elseif $oikea.luokka in <seikkasana, suhdesana> then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    result $res, rules sijapääte;
  elseif $oikea.luokka in @erisnimi then
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if ((johdin_lAinen in $oikea.jatko) and ($oikea.luokka in <paikannimi, sukunimi>)) then
      result $res, rules paikannimen_ja_sukunimen_lainen_johdin;
    end;
    result $res, rules erisnimen_sijapääte;
  end;
#define $b := transmit (<"c"> + <$oikea>);
#define $c := transmit (<"d"> + <$sana>);
  stop;
end;


combi_rule etuliite ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("etuliite ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = etuliite;

#define $a := transmit ($vasen);
#define $b := transmit (<$oikea> + <$sana> + <"">);

  define $i := $n - 1;

#assert ($i = length($vasen));

  ? ($oikea.luokka in $vasen.$i.jatko);

  ? (($vasen.$i.tiedot = nil) or not ((ei_ys in $vasen.$i.tiedot) or (ei_ysa in $vasen.$i.tiedot)));
  ? (($oikea.tiedot    = nil) or not ((ei_ys in $oikea.tiedot)    or (ei_ysj in $oikea.tiedot)));

  define $res := $vasen + <[alku: $sana] + $oikea>;

# Ehkei-tyyppiset sanat eivät voi olla yhdyssanojen osana.
#  if (kieltosana in $oikea.jatko) then
#    result $r, rules kieltosana;
#  end;
  if (nimisana in $oikea.jatko) then
    result $res, rules nimisana, teonsanasta_johdettu_nimisana,
                       paikannimen_ja_sukunimen_lainen_johdos;
    if (not (teonsana in $oikea.jatko)) then
      result $res, rules teonsana_teonsanasta_johdettu_nimisana;
    end;
  end;
  if (laatusana in $oikea.jatko) then
    result $res, rules laatusana, teonsanasta_johdettu_laatusana;
    if (not (teonsana in $oikea.jatko)) then
      result $res, rules teonsana_teonsanasta_johdettu_laatusana;
    end;
  end;
  if (nimi_laatusana in $oikea.jatko) then
    result $res, rules nimi_laatusana;
    if (not (nimisana in $oikea.jatko)) then
      result $res, rules nimisana, teonsanasta_johdettu_nimisana;
    end;
    if (not (laatusana in $oikea.jatko)) then
      result $res, rules laatusana, teonsanasta_johdettu_laatusana;
    end;
  end;
  if (teonsana in $oikea.jatko) then
    result $res, rules teonsana;
  end;

  # Tavuviiva ja etuliite ovat aina jatkossa eli niitä ei tarvitse testata.
  #
  result $res, rules tavuviiva, etuliite;
end;


combi_rule nimisana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimisana ($vasen, $oikea, $sana, $n):");
  define $i := $n - 1;

#assert ($i = length($vasen));

  define $res := $vasen + <[alku: $sana] + $oikea>;

  # Esim. (koillis)puolitse, (maan)teitse, (sähkö)postitse.
  #
  if ($oikea.luokka = seikkasana and $oikea.tiedot /= nil and
      ys_perusosa in $oikea.tiedot and $vasen /= <>) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if (liitesana in $oikea.jatko) then
       result $res, rules liitesana;
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    result $res, rules ei_ys_sijapääte;
  end;

  ? $oikea.luokka = nimisana;

#define $n1 := transmit (<"nimisana1"> + <$vasen.$i.jatko>);
#define $n2 := transmit (<"nimisana2"> + <$oikea.luokka>);
#define $n3 := transmit (<"nimisana3"> + <$sana>);
#define $n4 := transmit (<"nimisana4"> + <($oikea.luokka in $vasen.$i.jatko)>);
#define $n5 := transmit (<"nimisana5"> + <$oikea.tiedot>);

  ? $oikea.luokka in $vasen.$i.jatko;
  ? yhdyssana_oikein ($vasen, $oikea, $i);
###  ? ($oikea.tiedot = nil) or not (ei_ys in $oikea.tiedot); # Ei toimi!

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_laatusana;
  end;
  if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_seikkasana;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if ($vasen.$i.luokka = tavuviiva) or ($oikea.tiedot = nil or not (ei_ysj in $oikea.tiedot)) then
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules sijapääte;
  end;
end;


combi_rule laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = laatusana;

  define $i := $n - 1;

#define $nimi := transmit (<"laatusana"> + <$vasen.$i.alku> + <$oikea> + <$sana>);
#define $n := transmit (<"luokka"> + <$vasen.$i.luokka>);

  ? ($oikea.luokka in $vasen.$i.jatko);
  ? yhdyssana_oikein ($vasen, $oikea, $i);

#define $n := transmit (<$vasen> + <$oikea> + <$sana>);
#define $n1 := transmit ($vasen);
#define $n2 := transmit ($oikea);
#define $n3 := transmit ($sana);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  # Arka+mainen, halpa+mainen, oiva+llinen, täyde+llinen (= täysi + johdin_llinen), yms.
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if (@voittoaste + @yliaste * $oikea.jatko /= <>) then
    result $res, rules voitto_yliaste;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_laatusana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
#    ? ($oikea.tiedot = nil) or not ((ei_ys in $oikea.tiedot) or (ei_ysj in $oikea.tiedot));
    result $res, rules yhdyssana;
#    result $res, rules inen_päätteinen_laatusana;
  end;
  result $res, rules sijapääte;
end;


combi_rule inen_päätteinen_laatusana ($vasen, $oikea, $sana, $n):
  ? $oikea.perusmuoto /= nil;
  ? $oikea.luokka in <laatusana, nimi_laatusana>;
  ? $oikea.perusmuoto matches ".*inen";

define $i := $n - 1;
define $profile := transmit ("inen_päätteinen_laatusana ($vasen, $oikea, $sana, $n):");
define $a1 := transmit ($vasen.$i);
define $a2 := transmit ($oikea - jatko);
define $a3 := transmit ($sana);
define $a4 := transmit ($n);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if tavuviiva in $oikea.jatko then
    result $res, rules tavuviiva;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  if yhdyssana in $oikea.jatko then
    result $res, rules yhdyssana;
  end;
  result $res, rules sijapääte;
##  result $res, rules laatusanan_sijapäte; laatusanan_johdos;
end;


combi_rule nimi_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimi_laatusana ($vasen, $oikea, $sana, $n):");

  ? $oikea.luokka = nimi_laatusana;
  define $i := $n - 1;

##assert ($i = length($vasen));

#define $n4 := transmit ($oikea.luokka);

  if ($i greater 0) then
    ? $oikea.luokka in $vasen.$i.jatko;
    ? yhdyssana_oikein ($vasen, $oikea, $i);
  end;

#define $n1 := transmit ($vasen);
#define $n2 := transmit ($oikea);
#define $n3 := transmit ($sana);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  if (loppu in $oikea.jatko) then
     result $res, accept;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_laatusana;
  end;
  if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_laatusana;
  end;
  if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_seikkasana;
  end;
  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  result $res, rules sijapääte;
end;


combi_rule asemosana ($vasen, $oikea, $sana):
####define $profile := transmit ("asemosana ($vasen, $oikea, $sana):");
  ? $oikea.luokka = asemosana;

#define $nimi := transmit (<"asemosana"> + <$oikea> + <$sana>);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules asemosanan_sijapääte, liitesana, tavuviiva, voitto_yliaste;
end;


combi_rule lyhennesääntö ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("lyhennesääntö ($vasen, $oikea, $sana):");
  ? $oikea.luokka in <lyhenne>;

#define $nimi := transmit (<"lyhennesääntö"> + <$vasen> + <$oikea> + <$sana>);
#define $nimi1 := transmit (<"lyhennesääntö1"> + <$vasen.($n-1)>);
#define $nimi2 := transmit (<"lyhennesääntö2"> + <$oikea>);
#define $nimi3 := transmit (<"lyhennesääntö3"> + <$sana>);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules kaksoispiste, tavuviiva;
end;


combi_rule apusana ($vasen, $oikea, $sana):
####define $profile := transmit ("apusana ($vasen, $oikea, $sana):");
  ? $oikea.luokka in <huudahdussana, sidesana>;

#define $nimi := transmit (<"apusana"> + <$oikea> + <$sana>);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana, tavuviiva;
end;


combi_rule kaksoispiste ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("kaksoispiste ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <kaksoispiste>;

#define $nimi := transmit (<":"> + <$vasen> + <$oikea> + <$sana> + <length($vasen)>);

  result $vasen + <[alku: $sana] + $oikea + [äs: $vasen.($n-1).äs]>,
         rules kaksoispisteen_sijapääte;
end;


combi_rule tavuviiva ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("tavuviiva ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <tavuviiva>;
  ? $oikea.luokka in $vasen.($n-1).jatko;

#define $nimi1 := transmit (<"tavuviiva1"> + <$vasen>);
#define $nimi2 := transmit (<"tavuviiva2"> + <$oikea>);
#define $nimi3 := transmit (<"tavuviiva3"> + <$sana>);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules yhdyssana, asemosana, apusana, lyhennesääntö, erisnimi, lukusana;
end;


# Tämä sääntö on muokattu Harri Pitkäsen Voikko-versiosta.
#
combi_rule kaksoispisteen_sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("kaksoispisteen_sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;
  ? $oikea.sija /= keinonto_n;

  define $edellinen := $vasen.($n-1);
  ? ($edellinen.äs = aä) or ($oikea.äs = aä) or ($edellinen.äs = $oikea.äs);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules omistusliite, liitesana;
end;


define @sti_edeltäjä := <laatusana, nimi_laatusana, voittoaste, yliaste>
                        + @nimisanasta_johdettu_laatusana
                        + @laatusanasta_johdettu_laatusana
                        + @nimi_laatusanan_johdin
                        + @teonsanasta_johdettu_laatusana
                        + @lukusana + @laatutapa_1 + @laatutapa_2;

# Sijapääte, joka voi olla yhdyssanan alkuosassa.
#
#define @yhdyssanasija := <omanto_n,           # Talonpoika.
#                          omanto_en,
#                          omanto_ien,
#                          omanto_jen,
#                          omanto_in,          # Vanhempainloma.
#                          omanto_ten,         # Naistentanssit.
#                          omanto_iT,          # Hampaidenhoito, hampaittenhoito.
#                          sisätulento_hVn,    # Maahantulo, työhönotto.
#                          sisätulento_ihin>;  # Maihinnousu.

combi_rule sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;

#define $nimi := transmit (<$vasen> + <$oikea> + <$sana>);

  define $edellinen := $vasen.($n-1);

  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok ($edellinen,$oikea);

  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

#define $a1 := transmit ($edellinen);
#define $b1 := transmit ($oikea);
#define $c1 := transmit ($sana);

  if (($oikea.sija = kerronto_sti) and ($edellinen.luokka in @sti_edeltäjä)) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  else
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (yhdyssana in $oikea.jatko) and
       (($edellinen.tiedot = nil) or not ((ei_ys in $edellinen.tiedot) or (ei_ysa in $edellinen.tiedot))) then
      result $res, rules yhdyssana;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
  end;
end;


combi_rule ei_ys_sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;

#define $nimi := transmit (<$vasen> + <$oikea> + <$sana>);

  define $edellinen := $vasen.($n-1);

  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok ($edellinen,$oikea);

  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

#define $a1 := transmit ($edellinen);
#define $b1 := transmit ($oikea);
#define $c1 := transmit ($sana);

  if (($oikea.sija = kerronto_sti) and ($edellinen.luokka in @sti_edeltäjä)) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  else
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
  end;
end;


combi_rule asemosanan_sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("asemosanan_sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;

  define $edellinen := $vasen.($n-1);
  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok ($edellinen,$oikea);

  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (($oikea.sija = kerronto_sti) and ($edellinen.luokka in @sti_edeltäjä)) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  else
    if sijamuoto_sijasta($oikea.sija) = omanto then
      result $res, rules inen_johdos_nimisanasta; # Muun+tyyppinen yms.
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
#    result $res, rules liitesana, tavuviiva, loppu;
  end;
end;


combi_rule laatutavan_sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("laatutavan_sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;

  define $edellinen := $vasen.($n-1);
  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok ($edellinen,$oikea);

  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (($oikea.sija = kerronto_sti) and ($edellinen.luokka in @sti_edeltäjä)) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  else
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    if (liitesana in $oikea.jatko) then
      result $res, rules liitesana;
    end;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
###    result $res, rules omistusliite, liitesana, loppu;
  end;
end;


combi_rule teonsana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <teonsana>;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;

  if ((substring($vasen.$i.perusmuoto,1R) matches @ääntiö) and
      (substring($oikea.perusmuoto,1) matches @ääntiö)) then
   ? (substring($vasen.$i.perusmuoto,1R) /= substring($oikea.perusmuoto,1));
  end;

#define $n1 := transmit (<"teonsana1"> + <$vasen>);
#define $n2 := transmit (<"teonsana2"> + <$oikea>);
#define $n3 := transmit (<"teonsana3"> + <$sana>);
#define $n6 := transmit (<"">);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (liitesana in $oikea.jatko) then
    if (($oikea.tapaluokka = käskytapa) and
        ($oikea.tekijä = 2) and
        ($oikea.luku = yksikkö)) then
      result $res, rules liitesana_ei_kO;
    else
      result $res, rules liitesana;
    end;
  end;
  if (loppu in  $oikea.jatko) then
    result $res, accept;
  end;
  if (tavuviiva in  $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (@teonsanasta_johdettu_teonsana * $oikea.jatko /= <>) then
    result $res, rules teonsanan_johdos_teonsana;
  end;
#  if (@tekijämuodot * $oikea.jatko /= <>) then
#    result $res, rules tekijämuodot;
#  end;
#  if (@nimitavat * $oikea.jatko /= <>) then
#    result $res, rules nimitavat;
#  end;
#  if (@laatutavat * $oikea.jatko /= <>) then
#    result $res, rules laatutavat;
#  end;
# Tämä on nopeampi kuin kaikki erikseen!
  result $res, rules tekijämuodot, nimitavat, laatutavat;
end;


combi_rule tekijämuodot ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("tekijämuodot ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @tekijämuodot;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

#define $a := transmit ($vasen);
#define $b := transmit ($oikea.luokka);
#define $c := transmit ($sana);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  # Päätteessä on sama ääntiö kuin vartalon lopussa,
  # esim. puhua => puhuu, saada => saa.
  #
  if (($oikea.luokka = kestämän_tekijäpääte_y3)
      and ($vasen.$i.alku matches ".*" + @ääntiö)) then
    require (viimeinen_kirjain ($vasen.$i.alku) = $sana);
  end;

  # Vvi-päätteessä Avi hyväksytään missä tahansa sanassa, muuten päätteen alussa
  # on sama ääntiö kuin vartalon lopussa: puhuuvi, sanoovi.
  #
  # "Savu käypi sieramista,: Pöly musta pörnyävi,: Tulta Hurja henkiävi,: Säkeniä suitsuavi, ..."
  # Frans Pietari Kemelli: Höyrylaiva Oulu. Oulun Viikko-Sanomia 14.8.1841.
  # http://fi.wikisource.org/wiki/H%C3%B6yrylaiva_Oulu
  # http://digi.lib.helsinki.fi/sanomalehti/secure/showPage.html?id=122472&conversationId=1&action=entryPage
  #
  if (($oikea.luokka = kestämän_tekijäpääte_y3_Vvi)
      and ($vasen.$i.alku matches ".*" + @ääntiö)) then
    require (($sana in <"avi", "ävi">) or
             (viimeinen_kirjain ($vasen.$i.alku) = substring($sana,1,1)));
  end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana;
end;


combi_rule nimitavat ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimitavat ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @nimitavat;

  define $i := $n - 1;

#define $n1 := transmit (<"nimitavat1"> + <$vasen.$i>);
#define $n2 := transmit (<"nimitavat2"> + <$oikea>);

  ? $oikea.luokka in $vasen.$i.jatko;
#define $n3 := transmit (<"nimitavat3"> + <$vasen.$i.luokka> + <$oikea.jatko> + <$sana>);

  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if ($oikea.luokka in @nimitapa_1) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules nimitapa_1_pitkä, liitesana;
  elseif ($oikea.luokka in @nimitapa_2) then
    #
    # Ääntiö + "iss[aä] käy mutta ei kerake + "iss[aä]".
    # Esim. puno+issa (puno+essa) käy mutta ei tull+issa (tull+essa).
    # Nyt esim. "aitoissa" on sekä aitta-sanan että aitoa-sanan muoto (aitoessa).
    #
    if ($sana in <"issa", "issä">) then
      ? ($vasen.$i.alku matches ".*[aeiouyäö]");
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;    
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  elseif ($oikea.luokka in @nimitapa_3) then
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;    
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules liitesana;
  elseif ($oikea.luokka = nimitapa_5) then
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    result $res, rules omistusliite;
  end;
end;


combi_rule nimitapa_1_pitkä ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimitapa_1_pitkä ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = nimitapa_1_pitkä;

  define $i := $n - 1;
  ? nimitapa_1_pitkä in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

#define $nimi := transmit ("nimitapa_1_pitkä " + $vasen.$i.alku + " " + $sana);

  result $vasen + <[alku: $sana] + $oikea>, rules omistusliite;
end;


combi_rule laatutavat ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("laatutavat ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @laatutavat;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

#define $n1 := transmit (<"laatutapa1"> + <$vasen>);
#define $n2 := transmit (<"laatutapa2"> + <$oikea>);
#define $n3 := transmit (<"laatutapa3"> + <$sana>);
#define $n4 := transmit (<"laatutapa4"> + <$i>);
#define $n5 := transmit (<"">);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules laatutavan_voitto_yliaste;
  end;
  result $res, rules laatutavan_sijapääte;
end;


combi_rule kieltosana ($vasen, $oikea, $sana):
####define $profile := transmit ("kieltosana ($vasen, $oikea, $sana):");
  ? $oikea.luokka = kieltosana;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana, liitesana_kä, tavuviiva;
end;


combi_rule liitesana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("liitesana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <liitesana, liitesana2, liitesana_kO, liitesana_s>;

  define $i := $n - 1;
  #
  # Testin jälkiosa  on lisätty siksi, että on otettu
  # käyttöön sääntö liitesana_ei_kO. Toinen mahdollisuus olisi ollut
  # vaihtaa jokaisessa jatko-kentässä "liitesana" muotoon "liitesana, liitesana_kO",
  # paitsi silloin, kun sanaa ei voi seurata liitesana_kO.
  # Lisäämällä tämän pääsi helpommalla. Liitesana "ko", "kö" ei voi olla teonsanan
  # jatkona samalla tavalla kuin muut liitesanat: "puhuko" on "puhu" + käskytapa_kielto
  # (älkää puhuko), ei "puhu" + liitesana_kO (mutta "puhuuko" on "puhuu" + liitesana_kO).
  # Huomaa toisaalta, että "puhukin" on "puhu" + litesana "kin".
  #
##? $oikea.luokka in $vasen.$i.jatko;
  ? ($oikea.luokka in $vasen.$i.jatko) or (liitesana in $vasen.$i.jatko and $oikea.luokka = liitesana_kO);
  ? äs_ok ($vasen.$i, $oikea);

#define $a := transmit (<$vasen.$i.luokka> + <$vasen.$i.tapaluokka> + 
#                       <$vasen.$i.tekijä> + <$vasen.$i.luku> + <$sana> + <$oikea.luokka> + <$i>);

  result $vasen + <[alku: $sana] + $oikea>, accept;
end;


# Merkkijonon viimeinen merkki
# Tekijä: Björn Beutel.
#subrule last($string):
#  ! $string matches ".*", ".": $last;
#  return $last;
#end;


combi_rule liitesana_ei_kO ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("liitesana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <liitesana, liitesana2, liitesana_s>;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

#define $a := transmit (<$vasen.$i.luokka> + <$vasen.$i.tapaluokka> + 
#                       <$vasen.$i.tekijä> + <$vasen.$i.luku> + <$sana> + <$oikea.luokka> + <$i>);

  result $vasen + <[alku: $sana] + $oikea>, accept;
end;


combi_rule liitesana_kä ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("liitesana_kä ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = liitesana_kä;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana;
end;


combi_rule omistusliite ($vasen, $oikea, $sana, $n):
#define $profile := transmit ("omistusliite ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = omistusliite;
  define $i := $n - 1;

  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;
  define $result := $vasen + <[alku: $sana] + $oikea>;

  # Hyväksytään yksikön ensimmäisen omistusliite "in" eräissä
  # sijamuodoissa, esim. matkallain, matkaltain, matkallein;
  # ja myös esim. tyttärein (tyttäreni).
  #
  if (($sana = "in") and ($oikea.tekijä = 1)
      and ((($vasen.$i.luokka in <sijapääte, nimitapa_2, nimitapa_3>) and
             (not ($vasen.$i.sija in <vajanto_ttA, vajanto_ittA>)) and
             (substring (value_string ($vasen.$i.sija), 1R) in <"A", "e">))
            or (($vasen.$i.alku /= nil) and substring ($vasen.$i.alku, 1R) in <"e">))) then
    if (loppu in $oikea.jatko) then
      result $result, accept;
    end;
    result $result, rules liitesana;
  end;

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);

  # Jos sijamuoto on omanto_in, hyväksytään vain omistusliitteet "nsa", "nsä", "mme".
  # Omistusliitteet "nsa" ja "nsä" hyväksytään aina, mutta omistusliite "mme" vain, jos
  # sanan vartalon viimeinen kirjain on a tai e tai ä. Tämä algoritmi kelpaa Juhani
  # Ahon teoksille (http://www.lonnrot.net/etext.html).
  #
  # Siis hyväksytään esim. isä+i+nsä, vala+i+nsa, kasvo+i+nsa, pöytä+i+mme, synte+i+mme, mutta ei usko+i+mme.
  #
  if ($sana in <"ni", "si", "s", "nsa", "nsä", "ns", "mme", "nne">) then
    if ($vasen.$i.sija = omanto_in) then
      if (($sana in <"nsa", "nsä">) or
          (($sana in <"mme">) and (substring ($vasen.($i-1).alku, 1R) in <"a", "e", "ä">))) then
        if (loppu in $oikea.jatko) then
          result $result, accept;
        end;
        result $result, rules liitesana;
      else
        stop;
      end;
    else
      if (loppu in $oikea.jatko) then
        result $result, accept;
      end;
      result $result, rules liitesana;
    end;
  elseif omistusliite3_oikein ($vasen.$i.alku, $sana) then
#define $o1 := transmit ($vasen.$i.alku + " " + $sana);
#define $o2 := transmit ((strcat ($vasen) matches ".*(aa|ee|ii|oo|uu|yy|ää|öö)"));
#define $o3 := transmit ((not (strcat ($vasen) matches ".*(aa|ee|ii|oo|uu|yy|ää|öö)")));
#define $o4 := transmit (($vasen.$i.luokka = sijapääte));
    if ((not (strcat ($vasen) matches ".*(aa|ee|ii|oo|uu|yy|ää|öö)"))
        and ((($vasen.$i.luokka = sijapääte) and
              ($vasen.$i.sija in <osanto_A, osanto_iA,
                                  osanto_tA, osanto_ttA, osanto_itA,
                                  osanto_jA,
                                  olento_nA, olento_inA,
                                  tulento_ksi, tulento_iksi,
                                  sisäolento_ssA, sisäolento_issA,
                                  sisäeronto_stA, sisäeronto_istA,
                                  ulko_olento_llA, ulko_olento_illA,
                                  ulkoeronto_ltA, ulkoeronto_iltA,
                                  ulkotulento_lle, ulkotulento_ille,
                                  vajanto_ttA, vajanto_ittA,
                                  seuranto_ine>))
             or ($vasen.$i.luokka in <seikkasana, suhdesana>)
             or (($vasen.$i.luokka = nimisana) and ($vasen.$i.sijamuoto /= nil)))) then # Omallatunnolla+an yms.
      if (loppu in $oikea.jatko) then
        result $result, accept;
      end;
      result $result, rules liitesana;
    elseif (($vasen.$i.luokka in <nimitapa_1_pitkä, nimitapa_2, nimitapa_3, nimitapa_3_saama, nimitapa_5>)
            and not ($vasen.$i.alku in <"maa", "mää">)) then  ## Tämä ehto on kopsattu Harri Pitkäsen Voikko-versiosta:
                                                              ## eroa+maa+an on väärin.
      if (loppu in $oikea.jatko) then
        result $result, accept;
      end;
      result $result, rules liitesana;
    end;
  end;
end;


combi_rule voitto_yliaste ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("voitto_yliaste ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @voittoaste + @yliaste;
  define $i := $n - 1;

#define $nimi1a := transmit ("voitto_yliaste1 " + $vasen.$i.alku + " " + $sana);
#define $nimi2a := transmit (<"voitto_yliaste2"> + <$vasen.$i>);
#define $nimi3a := transmit (<"voitto_yliaste3"> + <$oikea> + <$sana>);

  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if ($oikea.luokka = voittoaste and yhdyssana in $oikea.jatko) then
    #
    # Jos jatkossa on yhdyssana, niin on myös tavuviiva.
    #
    result $res, rules yhdyssana, tavuviiva;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (johdin_UUs in $oikea.jatko) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;

  result $res, rules sijapääte;
end;


combi_rule laatutavan_voitto_yliaste ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("laatutavan_voitto_yliaste ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @voittoaste + @yliaste;
  define $i := $n - 1;

  ? $oikea.luokka in $vasen.$i.jatko;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;

  result $res, rules sijapääte;
end;


subrule strcat ($vasen):
  define $s := "";

  foreach $k in $vasen:
    $s :=+ $k.alku;
  end;

  return $s;
end;


subrule sana1 ($tietue):
  if ($tietue.perusmuoto2 /= nil) then
    return $tietue.perusmuoto2;
  elseif ($tietue.perusmuoto = nil) then
    return $tietue.alku;
  else
    return $tietue.perusmuoto;
  end;
end;


subrule sisätulento_Vn_oikein ($sija, $ed_kirjain, $sana):
  return ((($ed_kirjain matches @a) and ($sana matches "an?")) or
          (($ed_kirjain matches @e) and ($sana matches "en?")) or
          (($ed_kirjain matches @i) and ($sana matches "in?")) or
          (($ed_kirjain matches @o) and ($sana matches "on?")) or
          (($ed_kirjain matches @u) and ($sana matches "un?")) or
          (($ed_kirjain matches @y) and ($sana matches "yn?")) or
          (($ed_kirjain matches @ä) and ($sana matches "än?")) or
          (($ed_kirjain matches @ö) and ($sana matches "ön?")));
end;


subrule sisätulento_hVn_oikein ($sija, $ed_kirjain, $sana):
  return ((($ed_kirjain matches @a) and ($sana matches "han?")) or
          (($ed_kirjain matches @e) and ($sana matches "hen?")) or
          (($ed_kirjain matches @i) and ($sana matches "hin?")) or
          (($ed_kirjain matches @o) and ($sana matches "hon?")) or
          (($ed_kirjain matches @u) and ($sana matches "hun?")) or
          (($ed_kirjain matches @y) and ($sana matches "hyn?")) or
          (($ed_kirjain matches @ä) and ($sana matches "hän?")) or
          (($ed_kirjain matches @ö) and ($sana matches "hön?")) or
          (($ed_kirjain matches "'"))); # Parfait'hen yms.
end;


subrule omistusliite3_oikein ($sana, $liite):
  return ((($sana matches ".*" + @a) and ($liite = "an")) or
          (($sana matches ".*" + @e) and ($liite in <"en", "hen">)) or
          (($sana matches ".*" + @i) and ($liite = "in")) or
          (($sana matches ".*" + @o) and ($liite = "on")) or
          (($sana matches ".*" + @u) and ($liite = "un")) or
          (($sana matches ".*" + @y) and ($liite = "yn")) or
          (($sana matches ".*" + @ä) and ($liite = "än")) or
          (($sana matches ".*" + @ö) and ($liite = "ön")));
end;


subrule viimeinen_kirjain ($sana):
  if     ($sana matches ".*" + @a) then return "a";
  elseif ($sana matches ".*" + @e) then return "e";
  elseif ($sana matches ".*" + @i) then return "i";
  elseif ($sana matches ".*" + @o) then return "o";
  elseif ($sana matches ".*" + @u) then return "u";
  elseif ($sana matches ".*" + @y) then return "y";
  elseif ($sana matches ".*" + @ä) then return "ä";
  elseif ($sana matches ".*" + @ö) then return "ö";
  else
    error "Viimeinen kirjain ei ole ääntiö sanassa " + $sana;
  end;
end;


subrule yhdyssana_oikein ($vasen, $oikea, $n):
#define $profile := transmit ("subrule yhdyssana_oikein ($vasen, $oikea, $n):");
#define $a := transmit (<$oikea.luokka> + <$oikea.perusmuoto> + <$vasen.$n.luokka>);
#define $b := transmit (<$vasen.$n.perusmuoto> + <$oikea.perusmuoto>);


  # Jotkut sanat eivät voi olla yhdyssanojen osina.
  #
  if ((($vasen.$n.tiedot /= nil) and ((ei_ys in $vasen.$n.tiedot) or (ei_ysa in $vasen.$n.tiedot))) or
      (($oikea.tiedot    /= nil) and ((ei_ys in $oikea.tiedot)))) then
#define $d := transmit (<"yhdyssana_oikein-d">);
    return no;
  end;

  if (($vasen.$n.luokka /= tavuviiva) and ($oikea.tiedot /= nil) and (ei_ysj in $oikea.tiedot)) then
    return no;
  end;

  if (($vasen.$n.perusmuoto /= nil) and ($oikea.perusmuoto /= nil)) then
    #
    # Yhdyssanan eka osa ei voi loppua samaan ääntiöön, millä toka osa alkaa.
    # Siis esim. ensiilta ei käy, vaan pitäisi olla ensi-ilta.
    #
    define $s := $vasen.$n.perusmuoto;
    if (length($vasen.$n.alku) greater length($vasen.$n.perusmuoto)) then
      $s := $vasen.$n.alku;
    end;
    define $s1 := substring($s,1R);
    define $s2 := substring($oikea.perusmuoto,1);
    if (($s1 = $s2) and ($s1 matches @ääntiö) and ($s2 matches @ääntiö)) then
      return no;
##      return ($s1 /= $s2);
    end;
  end;
  return yes;
end;


#####################################################


output_filter tulosta ($tulos):
  foreach $j in $tulos:
#define $a := transmit ($j);
#define $b := transmit ("");
    result perusmuoto ($j);
  end;
end;


subrule sana2 ($tietue):
#  ? ($tietue.luokka = lukusana);
#define $a := transmit ($tietue);
  if ($tietue.perusmuoto = "kymmenen") then
    return "kymmentä";
  elseif ($tietue.perusmuoto = "tuhat") then
    return "tuhatta";
  elseif ($tietue.alaluokka in <sata, miljoona> and $tietue.perusmuoto /= "sadas") then
    return $tietue.perusmuoto + "a";
  elseif ($tietue.perusmuoto2 /= nil) then
    return $tietue.perusmuoto2;
  elseif ($tietue.perusmuoto = nil) then
    return $tietue.alku;
  else
    return $tietue.perusmuoto;
  end;
end;


subrule lukusanan_perusmuoto ($tietue, $i, $k, $s):
#define $a := transmit (<"a"> + <$tietue.$i.alku> + <$tietue.$i.perusmuoto> + <length($tietue)> + <$i> + <$k> + <$s>);

  if ($i = 1 and $tietue.$i.alaluokka in <sata, tuhat, miljoona>) then
    return $tietue.$i.perusmuoto;
  end;

  if ($tietue.$i.luokka = lukusanan_jälkiliite) then
    return $tietue.$i.perusmuoto;
  end;

  if ($tietue.$i.alaluokka = yksiyhdeksän) then
    if (($k greater 2) and ($i less $k) and ($tietue.($i+1).luokka = sijapääte) and ($tietue.($i+2).luokka /= lukusana)) then
      return $tietue.$i.alku;
    else
      return $tietue.$i.perusmuoto;
    end;
  elseif ($tietue.$i.perusmuoto = "kymmenen") then
    if (($i = $k) and (($s matches ".+sataa") or ($s = "sata"))) then
      return "kymmenen";
    else
      return "kymmentä";
    end;
  elseif ($tietue.$i.alaluokka in <sata, tuhat, miljoona>) then
    if ($tietue.($i+1).luokka in <lukusana, sijapääte>) then
#define $b := transmit (<"b"> + <$tietue.$i.alku> + <$tietue.$i.perusmuoto> + <length($tietue)> + <$i> + <$k> + <$s>);
      return sana2 ($tietue.$i);
    else
#define $c := transmit (<"c"> + <$tietue.$i.alku> + <$tietue.$i.perusmuoto> + <length($tietue)> + <$i> + <$k> + <$s>);
      return $tietue.$i.perusmuoto;
    end;
  elseif ($tietue.$i.luokka = sijapääte) then
#define $ö := transmit (<"ö"> + <$tietue.$i.alku> + <$tietue.$i.perusmuoto> + <length($tietue)> + <$i> + <$k> + <$s>);
    if ($i less $k and $tietue.($i+1).luokka in <lukusana, lukusanan_jälkiliite>) then
      return "";
    else
      return $tietue.$i.alku;
    end;
  end;
  return alku ($tietue.$i);
end;


subrule alku ($tietue):
#define $a := transmit (<"ALKU"> + <$tietue>);

  if ($tietue.perusmuoto = nil) then return $tietue.alku; end;

  if ($tietue.alku2 /= nil) then
##define $aaaa := transmit ($tietue.alku);
    return $tietue.alku2;
  end;


  # Antautua/antauta. Antau(du)ttu-muodolle tulee väärä perusmuoto antauttu.
  #
#  if ($tietue.perusmuoto matches ("...*(au|äy)" : $alku, "t(ua|yä)" : $loppu)) then
#    define $n := length ($alku);
#    return substring($tietue.perusmuoto,1,$n+2);
#  end;

  # Onneto(i)n.
  #
  if (($tietue.luokka = johdin_tOn) and ($tietue.alku matches ("t[oö]in"))) then
#define $ton := transmit ("ton");
    return $tietue.perusmuoto;
  end;

  define $taulukko := <
#       <"",    "",   "",    "">,
#        <"kamari",      "kammar",    "kamar">,
        <"kirjoitelma", "kirjotelm", "kirjoitelm">,
        <"kulttuuri",   "kultuur",   "kulttuur">,
        <"liipaisin",   "liipasi",   "liipaisi">,
        <"mahdoton",    "maho",      "mahdo">,
#        <"poliitikko",  "politik",   "poliitik">,
#        <"politiikka",  "politik",   "politiik">,
        <"punainen",    "punan",     "punain">,
        <"punainen",    "punas",     "punais">,
        <"teatteri",    "teaatte",   "teatte">
      >;
  foreach $k in $taulukko:
    if ($tietue.perusmuoto = $k.1) then
#define $e := transmit ($k);
      define $n := length ($k.2);
      if (length($tietue.alku) greater_equal $n) and (substring ($tietue.alku,1,$n) = $k.2) then
#define $ö := transmit ("Huu " + $tietue.alku + " " + $k.3 + " " + substring ($tietue.alku, $n + 1, 1R));
        return $k.3 + substring ($tietue.alku, $n + 1, 1R);
      end;
    end;
  end;
#define $buu := transmit (<"boo"> + <$tietue.alku>);
  return $tietue.alku;
end;


# Muutetaan sana perusmuotoon: Väisäsillemmekö -> Väisänen.
#
subrule perusmuoto ($tietue):
  define $n := length ($tietue);
  define $k := $n;

#define $z := transmit (<"1"> + <$tietue> + <$k>);

  # Poistetaan sanan lopusta osat, jotka eivät vaikuta perusmuotoon.
  #
  repeat while ($tietue.$k.luokka in <sijapääte, liitesana, liitesana2, liitesana_kO, liitesana_s,
                                      omistusliite, kaksoispiste,
                                      voittoaste, yliaste,
                                      liitesana_kä, nimitapa_1_pitkä, nimitapa_5>
                                      + @tositapa + @ehtotapa + @mahtotapa + @käskytapa
                                      + @nimitapa_1 + @nimitapa_2 + @nimitapa_3
                                      + @laatutapa_1 + @laatutapa_2);
#define $a := transmit (<"2"> + <$tietue.$k.perusmuoto> + <$tietue.$k.alku> + <$k>);
    $k :=- 1;
  end;

#define $a := transmit (<"A"> + <$tietue.$k.perusmuoto> + <$tietue.$k.alku> + <$k>);

  define $s := "";
  foreach $i in $k - 1:
    if ($tietue.$i.lukutyyppi /= nil and $tietue.$i.lukutyyppi = perusluku) then
      if ($i = 1 and $tietue.($i+1).luokka = sijapääte and $tietue.($i+2).luokka /= lukusana) then
        $s :=+ $tietue.$i.alku;
      else
        $s :=+ lukusanan_perusmuoto ($tietue, $i, $k, $s);
      end;
#    elseif ($tietue.($i+1).luokka = tavuviiva) then
#      if ($tietue.$i.luokka = sijapääte and $tietue.$i.sija in <omanto_n>) then
#        $s :=+ alku ($tietue.$i);
#      end;
    else
      $s :=+ alku ($tietue.$i);
#define $b := transmit (<"B"> + <$tietue.$i.perusmuoto> + <$tietue.$i.alku>  + <$tietue.$i.luokka> + <$i> + <$s>);
    end;
  end;
  if ($k = 1) then
    $s :=+ sana1 ($tietue.$k);
#define $b := transmit (<"C"> + <$tietue.$k.perusmuoto> + <$tietue.$k.alku> + <$k> + <$s>);
  elseif ($tietue.$k.lukutyyppi /= nil and $tietue.$k.lukutyyppi = perusluku) then
    $s :=+ lukusanan_perusmuoto ($tietue, $k, $k, $s);
#define $c := transmit (<"D"> + <$tietue.$k.perusmuoto> + <$tietue.$k.alku> + <$k> + <$s>);
  else
    $s :=+ sana2 ($tietue.$k);
#define $d := transmit (<"E"> + <$tietue.$k.perusmuoto> + <$tietue.$k.alku> + <$k> + <$s>);
  end;
  return $s;
end;



##==========================================


combi_rule teonsana_teonsanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsana_teonsanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = teonsana;
  ? nimisana in $vasen.($n-1).jatko;
  ? $oikea.jatko * (@teonsanasta_johdettu_teonsana) /= <>;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $a := transmit ($vasen.($n-1));
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);
#define $e := transmit (<"">);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;
  result $vasen + <[alku: $sana] + $oikea>, rules teonsanan_johdos_teonsana_nimisana;
end;


combi_rule teonsana_teonsanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsana_teonsanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = teonsana;
  ? laatusana in $vasen.($n-1).jatko;
  ? $oikea.jatko * (@teonsanasta_johdettu_teonsana) /= <>;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $a := transmit ($vasen.($n-1));
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);
#define $e := transmit (<"">);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;
  result $vasen + <[alku: $sana] + $oikea>, rules teonsanan_johdos_teonsana_laatusana;
end;


combi_rule teonsanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = teonsana;
  ? $oikea.jatko * (@teonsanasta_johdettu_nimisana) /= <>;

#define $a := transmit ($vasen.($n-1).jatko);
#define $b := transmit ($oikea.jatko);
#define $c := transmit ($sana);
#define $d := transmit ($n);
#define $e := transmit ($vasen.($n-1));

#  ? yhdyssana_oikein ($vasen, $oikea, $n-1);
  if ((substring($vasen.($n-1).perusmuoto,1R) = substring($oikea.perusmuoto,1)) and
      (substring($oikea.perusmuoto,1) matches @ääntiö)) then
    stop;
  end;
  ? $vasen.($n-1).jatko * <nimisana, nimi_laatusana, laatusana, etuliite> /= <>;


  result $vasen + <[alku: $sana] + $oikea>,
         rules teonsanan_johdos_nimisana;
end;


combi_rule teonsanan_johdos_nimisana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsanan_johdos_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @teonsanasta_johdettu_nimisana;

  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;

  # Vaihdetaan ääntiösointu oikeaksi.
  # Esim. pitää => pito, kiertää => kierros.
  #
  if ($oikea.luokka in <johdin_O, johdin_Os, johdin_tO_leuto, johdin_tO_liitto>) then
    $vasen.$i :=+ [alkuperäinen_äs: $vasen.($n-1).äs];
    if ($vasen.$i.alku matches ".*" + @yäö + ".*") then
      $vasen.$i.äs := ä;
    else
      $vasen.$i.äs := a;
    end;
  end;

  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  $vasen.$i :=+ [lähtöluokka: $vasen.$i.luokka];
  $vasen.$i.luokka := nimisana;

#define $a := transmit ($vasen.$i.luokka);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_seikkasana;
  end;
  if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_laatusana;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  result $res, rules sijapääte;
end;


#################################################

combi_rule teonsanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = teonsana;
  ? $oikea.jatko * @teonsanasta_johdettu_laatusana /= <>;

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

##  ? yhdyssana_oikein ($vasen, $oikea, $n-1);
  if ((substring($vasen.($n-1).perusmuoto,1R) = substring($oikea.perusmuoto,1)) and
      (substring($oikea.perusmuoto,1) matches @ääntiö)) then
    stop;
  end;
  ? $vasen.($n-1).jatko * <nimisana, nimi_laatusana, laatusana, etuliite> /= <>;

  result $vasen + <[alku: $sana] + $oikea>,
         rules teonsanan_johdos_laatusana;
end;


combi_rule teonsanan_johdos_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsanan_johdos_laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @teonsanasta_johdettu_laatusana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_laatusana;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  result $res, rules sijapääte;
end;


combi_rule teonsanan_johdos_teonsana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("teonsanan_johdos_teonsana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @teonsanasta_johdettu_teonsana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $a := transmit (<"vasen"> + <$vasen>);
#define $b := transmit (<"oikea"> + <$oikea>);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  $oikea :=+ [lähtöluokka: $oikea.luokka];
  $oikea.luokka := teonsana;

#define $a2 := transmit (<"vasen2"> + <$vasen>);
#define $b2 := transmit (<"oikea2"> + <$oikea>);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana_ei_kO;
  end;
  if (loppu in  $oikea.jatko) then
    result $res, accept;
  end;
  if (tavuviiva in  $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
##  if (@teonsanasta_johdettu_teonsana * $oikea.jatko /= <>) then
##    result $res, rules teonsanan_johdos_teonsana;
##  end;
#  if (@tekijämuodot * $oikea.jatko /= <>) then
#    result $res, rules tekijämuodot;
#  end;
#  if (@nimitavat * $oikea.jatko /= <>) then
#    result $res, rules nimitavat;
#  end;
#  if (@laatutavat * $oikea.jatko /= <>) then
#    result $res, rules laatutavat;
#  end;
# Tämä on nopeampi kuin kaikki erikseen!
  result $res, rules tekijämuodot, nimitavat, laatutavat,
                     teonsanan_johdos_nimisana, teonsanan_johdos_laatusana;
end;


combi_rule teonsanan_johdos_teonsana_nimisana ($vasen, $oikea, $sana, $n):
#define $profile := transmit ("teonsanan_johdos_teonsana_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @teonsanasta_johdettu_teonsana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  result $vasen + <[alku: $sana] + $oikea>, rules teonsanan_johdos_nimisana;
end;


combi_rule teonsanan_johdos_teonsana_laatusana ($vasen, $oikea, $sana, $n):
#define $profile := transmit ("teonsanan_johdos_teonsana_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @teonsanasta_johdettu_teonsana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  result $vasen + <[alku: $sana] + $oikea>, rules teonsanan_johdos_laatusana;
end;


#################################################

combi_rule nimisanasta_johdettu_seikkasana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimisanasta_johdettu_seikkasana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @nimisanasta_johdettu_seikkasana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana;
end;


#################################################


combi_rule nimisanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimisanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @nimisanasta_johdettu_laatusana;
  define $i := $n - 1;
  ? $oikea.luokka in $vasen.$i.jatko;

  if ($oikea.luokka = johdin_inen) then
    if (($n = 2) and (($vasen.$i.tiedot = nil) or (not (inen in $vasen.$i.tiedot)))) then
#define $aa := transmit ("Plonk " + $vasen.$i.perusmuoto + " " + $vasen.$i.alku);
      stop;
    end;
  end;

  ? äs_ok ($vasen.$i, $oikea);
  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
##############################################
 if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;

#  if (yhdyssana in $oikea.jatko) then
#    if ($oikea.luokka = johdin_inen and $n = 2) then
#define $a := transmit (<"a"> + <$vasen.$i.perusmuoto>);
#define $b := transmit (<"b"> + <$oikea - jatko>);
#      result $res, rules inen_päätteinen_laatusana;
#    else
#      result $res, rules yhdyssana;
#    end;
#  end;
###################################
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_laatusana;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  result $res, rules sijapääte;
end;


#################################################


combi_rule laatusanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):
#define $profile := transmit ("laatusanasta_johdettu_nimisana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @laatusanasta_johdettu_nimisana;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

#define $a := transmit ($vasen.($n-1));
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_laatusana;
  end;
  if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
    result $res, rules nimisanasta_johdettu_seikkasana;
  end;
  if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
    result $res, rules nimi_laatusanan_johdin;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules sijapääte;
end;


combi_rule laatusanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("laatusanasta_johdettu_laatusana ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @laatusanasta_johdettu_laatusana;
  ? $oikea.luokka in $vasen.($n-1).jatko;

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;             # Hyvänlaisuus.
  end;
  result $res, rules sijapääte;
end;


###########################################################################33

# Erisnimien käsittely on muokattu Harri Pitkäsen Voikko-versiosta.

combi_rule erisnimi ($vasen, $oikea, $sana):
####define $profile := transmit ("erisnimi ($vasen, $oikea, $sana):");
  ? $oikea.luokka in @erisnimi;

  define $res := $vasen + <[alku: $sana] + $oikea>;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules erisnimen_sijapääte;
end;


# Paikannimestä johdettu kansallisuuden nimi tai
# sukunimestä johdettu aatteen kannattajan nimi.
#
combi_rule paikannimen_ja_sukunimen_lainen_johdos ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("paikannimen_ja_sukunimen_lainen_johdos ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <paikannimi, sukunimi>;
#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  ? ($vasen.($n-1).jatko * <nimisana, nimi_laatusana, laatusana, etuliite> /= <>);
  ? johdin_lAinen in $oikea.jatko;

  result $vasen + <[alku: $sana] + $oikea>, rules paikannimen_ja_sukunimen_lainen_johdin;
end;


combi_rule paikannimen_lainen_johdos_paikannimen_omannon_jälkeen ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("paikannimen_lainen_johdin ($vasen, $oikea, $sana, $n):");

  ? ($oikea.luokka = paikannimi and johdin_lAinen in $oikea.jatko);
  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;
  define $res := $vasen + <[alku: $sana] + $oikea>;

  result $res, rules paikannimen_ja_sukunimen_lainen_johdin;
end;


combi_rule paikannimen_ja_sukunimen_lainen_johdin ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("paikannimen_ja_sukunimen_lainen_johdin ($vasen, $oikea, $sana, $n):");
  ? ($oikea.luokka = johdin_lAinen) or
    (($oikea.luokka = johdin_mAinen) and ($vasen.($n-1).tiedot = nil or not (ei_mAinen in $vasen.($n-1).tiedot)));
  ? äs_ok ($vasen.($n-1), $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
  if (loppu in$oikea.jatko) then
    result $res, accept;
  end;
  if (<voittoaste, yliaste> * $oikea.jatko /= <>) then
    result $res, rules voitto_yliaste;
  end;
  result $res, rules sijapääte;
end;


combi_rule erisnimen_sijapääte ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("erisnimen_sijapääte ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = sijapääte;

#define $nimi := transmit (<$vasen> + <$oikea> + <$sana>);
#define $a := transmit ($vasen);
#define $b := transmit ($oikea);
#define $c := transmit ($sana);
#define $d := transmit ($n);
#define $e := transmit ($vasen.($n-1).perusmuoto);
#define $f := transmit ($vasen.($n-1).luokka);

  define $edellinen := $vasen.length ($vasen);

#define $e := transmit ($edellinen);

  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);

  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if ($oikea.sija = omanto_n) then
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    if ($vasen.($n-1).luokka = paikannimi) then
      result $res, rules paikannimen_lainen_johdos_paikannimen_omannon_jälkeen;
    end;
    result $res, rules yhdysmerkki_erisnimen_jälkeen, johdettu_erisnimi;
  else
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    if tavuviiva in $oikea.jatko then
      result $res, rules yhdysmerkki_erisnimen_jälkeen;
    end;
  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  result $res, rules liitesana;
end;


combi_rule yhdysmerkki_erisnimen_jälkeen ($vasen, $oikea, $sana):
####define $profile := transmit ("yhdysmerkki_erisnimen_jälkeen ($vasen, $oikea, $sana):");
  ? $oikea.luokka = tavuviiva;

  define $res := $vasen + <[alku: $sana] + $oikea>;
  result $res, rules yhdyssana, erisnimi;
end;


combi_rule johdettu_erisnimi ($vasen, $oikea, $sana):
####define $profile := transmit ("johdettu_erisnimi ($vasen, $oikea, $sana):");
  ? ($oikea.paikannimen_jälkiliite = yes) or ($oikea.etunimen_jälkiliite = yes);
  define $res := $vasen + <[alku: $sana] + $oikea>;
  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  if tavuviiva in $oikea.jatko then
    result $res, rules yhdysmerkki_erisnimen_jälkeen;
  end;
#  result $res, rules erisnimen_sijapääte;
  result $res, rules sijapääte;
end;


# inen-johdinta edeltävä nimisana.
# Sääntö ei voi olla sanan alussa.
#
combi_rule inen_johdos_nimisanasta ($vasen, $oikea, $sana, $n):
##define $profile := transmit ("inen_johdos_nimisanasta ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in <nimisana, nimi_laatusana>;
  ? johdin_inen in $oikea.jatko;
  ? $oikea.tiedot = nil or
    (not ei_ys in $oikea.tiedot and not ei_ysj in $oikea.tiedot);

#  define $a := transmit ($vasen);
#  define $b := transmit ($oikea);
#  define $c := transmit ($sana);
#  define $d := transmit ($n);

  result $vasen + <[alku: $sana] + $oikea>, rules johdin_inen;
end;


combi_rule johdin_inen ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("johdin_inen ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka = johdin_inen;

#  define $ö := transmit ("AAAAAAAAAAAAAAA");
#  define $a := transmit ($vasen);
#  define $b := transmit ($oikea.jatko);
#  define $c := transmit ($sana);
#  define $d := transmit ($n);
#  define $x := transmit ("BBBBBBBBBBBBBBB");

  define $i := $n - 1;
  ? äs_ok ($vasen.$i, $oikea);

  if $oikea.äs = aä then $oikea.äs := $vasen.$i.äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if tavuviiva in $oikea.jatko then
    result $res, rules tavuviiva;
  end;
#  if (yhdyssana in $oikea.jatko) then
#    result $res, rules yhdyssana;
#  end;
  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
#  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
#    result $res, rules laatusanasta_johdettu_laatusana;
#  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  result $res, rules sijapääte;
end;


subrule äs_ok ($edellinen, $oikea):
#define $a := transmit (<"äs_ok"> + <$edellinen.äs> + <$oikea.äs>);
#define $b := transmit (<"äs_ok"> + <$edellinen>);
#define $c := transmit (<"äs_ok"> + <$oikea>);

  return ($oikea.äs = aä or $edellinen.äs = aä or $oikea.äs = $edellinen.äs);
end;


combi_rule nimi_laatusanan_johdin ($vasen, $oikea, $sana, $n):
####define $profile := transmit ("nimi_laatusanan_johdin ($vasen, $oikea, $sana, $n):");
  ? $oikea.luokka in @nimi_laatusanan_johdin;
  ? $oikea.luokka in $vasen.($n-1).jatko;
  ? äs_ok ($vasen.($n-1), $oikea);

#define $xb := transmit ($oikea.luokka);
#define $xa := transmit ($vasen);
#define $yb := transmit ($oikea);
#define $yc := transmit ($sana);
#define $yd := transmit ($n);

  if $oikea.äs = aä then $oikea.äs := $vasen.($n-1).äs; end;

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
  if (yhdyssana in $oikea.jatko) then
    result $res, rules yhdyssana;
  end;
  if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
    result $res, rules laatusanasta_johdettu_nimisana;
  end;
#  if (@laatusanasta_johdettu_laatusana * $oikea.jatko /= <>) then
#    result $res, rules laatusanasta_johdettu_laatusana;
#  end;
  if (loppu in $oikea.jatko) then
    result $res, accept;
  end;
  if (omistusliite in $oikea.jatko) then
    result $res, rules omistusliite;
  end;
  if (liitesana in $oikea.jatko) then
    result $res, rules liitesana;
  end;
  if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
    result $res, rules voitto_yliaste;
  end;
  result $res, rules sijapääte;
end;


##########################################

# Lukusanojen käsittely on muokattu Harri Pitkäsen Voikko-versiosta.

combi_rule lukusana ($vasen, $oikea, $sana, $index):
#define $profile := transmit ("lukusana ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = lukusana;
  define $sijamuoto := nil;
  define $luku := nil;

#define $a1 := transmit ($vasen);
#define $a2 := transmit ($oikea);
#define $a3 := transmit ($sana);
#define $a4 := transmit ($index);

  if ($oikea.alaluokka = erikoisluku) then
    define $erikoisres := $vasen + <[alku: $sana] + $oikea>;
    if (loppu in $oikea.jatko) then
      result $erikoisres, accept;
    end;
    if (liitesana in $oikea.jatko) then
      result $erikoisres, rules liitesana;
    end;
    if (lukusanan_jälkiliite in $oikea.jatko) then
      result $erikoisres, rules lukusanan_jälkiliite;
    end;
     result $erikoisres, rules yhdyssana, tavuviiva;
    stop;
  end;

  if ($index = 1) then
    if ($oikea.alaluokka = toista) then
      stop;   # "Toistamaan" ei ole toista+maa+n.
    end;
  else
#  if ($index greater 1) then
    define $edellinen := $vasen.($index - 1);
    ? $edellinen.lukutyyppi = nil or $edellinen.lukutyyppi = $oikea.lukutyyppi;
    $sijamuoto := $edellinen.sijamuoto;
    $luku := $edellinen.luku;
    # Estetään peräkkäiset "satasata", "tuhattuhat" jne.
    if ($edellinen.luokka = lukusana) then
      ? not ($oikea.alaluokka = $edellinen.alaluokka);
    end;
  end;
  define $res := $vasen + <[alku: $sana, sijamuoto: $sijamuoto, luku: $luku] + $oikea>;

  if ($oikea.alaluokka = yksiyhdeksän) then
    if (nimentö in $oikea.jatko) then
      if loppu in $oikea.jatko then
        result $res, accept;
      end;
      result $res, rules lukusana_toista, lukusana_sisäkerroin,
                         liitesana; ##, lukusanan_jälkiliite;
#, yhdyssana;
##                   nimisana, laatusana;
##                   nimisana, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
  elseif ($oikea.alaluokka in <kymmenen, sata>) then
    if (nimentö in $oikea.jatko) then
      if omistusliite in $oikea.jatko then
        result $res, rules omistusliite;
      end;
      if loppu in $oikea.jatko then
        result $res, accept;
      end;
      result $res, rules lukusana_sisäkerroin,
                         liitesana, lukusanan_jälkiliite;
##                   nimisana, laatusana;
##                   nimisana, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
    end;
  elseif ($oikea.alaluokka in <tuhat, miljoona> and nimentö in $oikea.jatko) then
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    if loppu in $oikea.jatko then
      result $res, accept;
    end;
    result $res, rules liitesana, lukusanan_jälkiliite;
##                       yhdyssana;
##                 nimisana, laatusana;
##                 nimisana, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
  elseif ($oikea.alaluokka = numeromerkki) then
    $res := $vasen + <[alku: $sana, pilkku: no, rakenne: "=q"] + $oikea>;
    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
   result $res, rules lukusana_numero; ##, yhdysmerkki_numeron_jälkeen;
  end;
  if ($oikea.alaluokka in <sata, tuhat>) then
    if (nimentö in $oikea.jatko) then
      result $res, rules lukusana;
    end;
  end;
  if (tavuviiva in $oikea.jatko) then
    result $res, rules tavuviiva;
  end;
#  result $res, rules lukusana_sijapääte;
  result $res, rules yhdyssana, lukusana_sijapääte;
end;


combi_rule lukusana_sijapääte ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_sijapääte ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = sijapääte;
  define $edellinen := $vasen.($index - 1);
  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok($edellinen, $oikea);
  ? ($oikea.sija /= sisätulento_Vn)  or sisätulento_Vn_oikein  ($oikea.sija, substring($edellinen.alku,1R), $sana);
  ? ($oikea.sija /= sisätulento_hVn) or sisätulento_hVn_oikein ($oikea.sija, substring($edellinen.alku,1R), $sana);
  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;

  define $sijamuoto := sijamuoto_sijasta($oikea.sija);
  ? $edellinen.sijamuoto = nil or $edellinen.sijamuoto = $sijamuoto;
  ? $edellinen.luku = nil or $edellinen.luku = $oikea.luku;

  define $res := $vasen + <[alku: $sana, sijamuoto: $sijamuoto, lukutyyppi: $edellinen.lukutyyppi] + $oikea>;
  if ($edellinen.alaluokka = yksiyhdeksän) then
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    result $res, rules lukusana_toista, lukusana_sisäkerroin,
##                       yhdyssana;
                 lukusanan_jälkiliite;
##                 nimisana, laatusana;
##                 nimisana, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
  elseif ($edellinen.alaluokka in <kymmenen, sata>) then
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    result $res, rules lukusana_sisäkerroin, lukusanan_jälkiliite;
  else
    result $res, rules lukusanan_jälkiliite;
##    result $res, rules yhdyssana;
                       ## , nimisana, lukusanan_jälkiliite, laatusana;
                       ##, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  if ($edellinen.alaluokka in <sata, tuhat>) then
    result $res, rules lukusana;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  if tavuviiva in $oikea.jatko then
    result $res, rules tavuviiva;
  end;
  if yhdyssana in $oikea.jatko then
    result $res, rules yhdyssana;
  end;
end;


combi_rule lukusana_sisäkerroin ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_sisäkerroin ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = lukusana;
  ? $oikea.alaluokka in <kymmenen, sata, tuhat, miljoona>;

  define $edellinen := $vasen.($index - 1);
  ? $edellinen.lukutyyppi = $oikea.lukutyyppi;
  define $res := $vasen + <[alku: $sana, sijamuoto: $edellinen.sijamuoto, luku: $edellinen.luku] + $oikea>;

  if ($edellinen.sijamuoto in <nil, nimentö>) then
    result $res, rules lukusana_sisäkerroin_osanto;
  else
    result $res, rules lukusana_sisäkerroin_sijapääte;
  end;
  
  if ($oikea.lukutyyppi = järjestysluku) then
    result $res, rules lukusana;
  end;
  
  if ($oikea.lukutyyppi = järjestysluku and loppu in $oikea.jatko) then
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    if loppu in $oikea.jatko then
      result $res, accept;
    end;
    result $res, rules liitesana;
  end;
end;


combi_rule lukusana_sisäkerroin_sijapääte ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_sisäkerroin_sijapääte ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = sijapääte;
  define $edellinen := $vasen.($index - 1);
  ? $oikea.sija in $edellinen.jatko;
  ? äs_ok($edellinen, $oikea);
  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;

  define $sijamuoto := sijamuoto_sijasta($oikea.sija);
  ? $edellinen.sijamuoto = nil or $edellinen.sijamuoto = $sijamuoto;
  ? $edellinen.luku = nil or $edellinen.luku = $oikea.luku;

  define $res := $vasen + <[alku: $sana, sijamuoto: $sijamuoto, lukutyyppi: $edellinen.lukutyyppi] + $oikea>;
  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  result $res, rules lukusana,
                     liitesana, lukusanan_jälkiliite;
##               nimisana, laatusana;
##               nimisana, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
end;

combi_rule lukusana_sisäkerroin_osanto ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_sisäkerroin_osanto ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = sijapääte;
  define $edellinen := $vasen.($index - 1);
  ? $oikea.sija in $edellinen.jatko;
  ? sijamuoto_sijasta($oikea.sija) = osanto;
  ? äs_ok($edellinen, $oikea);
  if $oikea.äs = aä then $oikea.äs := $edellinen.äs; end;

  define $res := $vasen + <[alku: $sana, lukutyyppi: $edellinen.lukutyyppi] + $oikea>;
  if omistusliite in $oikea.jatko then
    result $res, rules omistusliite;
  end;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  result $res, rules lukusana, lukusanan_jälkiliite,
                     liitesana;
##, yhdyssana;
##               nimisana, laatusana; ##, inen_johdos_nimisanasta, inen_päätteinen_laatusana;
end;

combi_rule lukusana_toista ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_toista ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = lukusana;
  ? $oikea.alaluokka = toista;
  define $edellinen := $vasen.($index - 1);
  define $res := $vasen + <[alku: $sana, sijamuoto: $edellinen.sijamuoto,
                            luku: $edellinen.luku, lukutyyppi: $edellinen.lukutyyppi] + $oikea>;
  if loppu in $oikea.jatko then
    result $res, accept;
  end;
  if liitesana in $oikea.jatko then
    result $res, rules liitesana;
  end;
  result $res, rules lukusana_sisäkerroin,
##                     lukusanan_jälkiliite,
##                     inen_johdos_nimisanasta,
##                     inen_päätteinen_laatusana,
                     yhdyssana,
                     tavuviiva;
end;


combi_rule lukusanan_jälkiliite ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusanan_jälkiliite ($vasen, $oikea, $sana, $index):");
#  define $a := transmit ($vasen);
#  define $b := transmit ($oikea);
#  define $c := transmit ($sana);
#  define $d := transmit ($index);

  ? $oikea.luokka in <lukusanan_jälkiliite, tavuviiva>;
#  ? $oikea.luokka in <nimisana, laatusana, nimi_laatusana, tavuviiva>;
  # FIXME: yhdysviivan käyttöä ei vielä tueta lukusanan ja sen jälkiliitteen välissä
  define $res := $vasen + <[alku: $sana] + $oikea>;
  if $oikea.luokka = tavuviiva then
    result $res, accept;
  else
    define $edellinen := $vasen.($index - 1);
    ? $edellinen.lukutyyppi = nil or $edellinen.lukutyyppi = $oikea.lukutyyppi;
    if omistusliite in $oikea.jatko then
      result $res, rules omistusliite;
    end;
    if tavuviiva in $oikea.jatko then
      result $res, rules tavuviiva;
    end;
    if loppu in $oikea.jatko then
      result $res, accept;
    end;
    result $res,
           rules ## ei_ys_sijapääte,
                 yhdyssana,
                 sijapääte,
                 liitesana;
  end;
end;

combi_rule lukusana_numero ($vasen, $oikea, $sana, $index):
####define $profile := transmit ("lukusana_numero ($vasen, $oikea, $sana, $index):");
  ? $oikea.luokka = lukusana;

  define $edellinen := $vasen.($index - 1);
  $oikea :=+ [pilkku: $edellinen.pilkku];
  define $res := $vasen + <[alku: $sana] + $oikea>;

  if $oikea.alaluokka = numeromerkki then
    result $res, rules lukusana_numero; ##, yhdysmerkki_numeron_jälkeen;
    result $res, accept;
  elseif $oikea.alaluokka = pilkku and $oikea.pilkku /= yes then
    $oikea :=+ [pilkku: yes];
    result $vasen + <[alku: $sana] + $oikea>, rules lukusana_numero;
  end;
end;


# Aputaulukko funktiolle sijamuoto_sijasta
define @sijamuoto_sijasta_muunnostaulu :=
[nimentö:     nimentö,
 nimentö_t:   nimentö,
 nimentö_tkA: nimentö,
 omanto_n:    omanto,
 omanto_nkA:  omanto,
 omanto_ien:  omanto,
 omanto_jen:  omanto,
 omanto_en:   omanto,
 omanto_in:   omanto,
 omanto_ten:  omanto,
 omanto_iT:   omanto,
 omanto_idän: omanto,
 kohdanto_idät: kohdanto,
 kohdanto_t: kohdanto,
 osanto_A:    osanto,
 osanto_AA:   osanto,
 osanto_iA:   osanto,
 osanto_jA:   osanto,
 osanto_ttA:  osanto,
 osanto_itA:  osanto,
 osanto_tA:   osanto,
 olento_nA:   olento,
 olento_inA:  olento,
 tulento_ksi: tulento,
 tulento_iksi: tulento,
 sisäolento_ssA: sisäolento,
 sisäolento_issA: sisäolento,
 sisäolento_nA: sisäolento,
 sisäeronto_stA: sisäeronto,
 sisäeronto_istA: sisäeronto,
 sisäeronto_tA: sisäeronto,
 sisätulento_Vn: sisätulento,
 sisätulento_VVn: sisätulento,
 sisätulento_hVn: sisätulento,
 sisätulento_iin: sisätulento,
 sisätulento_ihin: sisätulento,
 sisätulento_seen: sisätulento,
 sisätulento_isiin: sisätulento,
 sisätulento_sen: sisätulento,
 sisätulento_isin: sisätulento,
 sisätulento_nne: sisätulento,
 ulko_olento_llA: ulko_olento,
 ulko_olento_illA: ulko_olento,
 ulkoeronto_ltA: ulkoeronto,
 ulkoeronto_iltA: ulkoeronto,
 ulkotulento_lle: ulkotulento,
 ulkotulento_ille: ulkotulento,
 vajanto_ttA: vajanto,
 vajanto_ittA: vajanto,
 seuranto_ine: seuranto,
 keinonto_n: keinonto,
 keinonto_in: keinonto,
 tulento_s: tulento
];


# Palauttaa annettua sijasymbolia vastaavan sijamuodon
subrule sijamuoto_sijasta($sija):
  return @sijamuoto_sijasta_muunnostaulu.$sija;
end;


#robust_rule robust ($surface, $remain_input):
#  define $a := transmit (<"a"> + <$surface>);
#  define $b := transmit (<"b"> + <$remain_input>);
#end;


combi_rule yhdyssana ($vasen, $oikea, $sana, $n):
#define $profile := transmit ("yhdyssana ($vasen, $oikea, $sana, $n):");

#  ? $oikea.luokka /= lukusana;
#if ($oikea.luokka = lukusana) then
#define $a := transmit (($vasen.1).alku);
#define $b := transmit ($oikea.luokka);
#define $c := transmit (<$sana> + <"§">);
#end;

  define $i := $n - 1;

##assert ($n-1 = length($vasen));

#define $aa := transmit (<"Huu"> + <$vasen.$i> + <$sana> + <$oikea>);

  if ($oikea.luokka = seikkasana and $oikea.tiedot /= nil and
      ys_perusosa in $oikea.tiedot and $vasen /= <>) then
    define $res := $vasen + <[alku: $sana] + $oikea>;

    if (loppu in $oikea.jatko) then
      result $res, accept;
    end;
    if (liitesana in $oikea.jatko) then
       result $res, rules liitesana;
    end;
    if (omistusliite in $oikea.jatko) then
      result $res, rules omistusliite;
    end;
    result $res, rules ei_ys_sijapääte;
  end;

  ? ($oikea.luokka in $vasen.$i.jatko) or
    (yhdyssana in $vasen.$i.jatko) or
    ($oikea.luokka in <paikannimi, sukunimi, teonsana>);


  if ((($vasen.$i.tiedot /= nil) and ((ei_ys in $vasen.$i.tiedot) or (ei_ysa in $vasen.$i.tiedot))) or
      (($oikea.tiedot    /= nil) and ((ei_ys in $oikea.tiedot)))) then
#define $d := transmit (<"yhdyssana_oikein-d">);
    stop;
  end;
  if (($vasen.$i.luokka /= tavuviiva) and ($oikea.tiedot /= nil) and (ei_ysj in $oikea.tiedot)) then
    stop;
  end;
  if (($vasen.$i.perusmuoto /= nil) and ($oikea.perusmuoto /= nil)) then
    define $s := $vasen.$i.perusmuoto;
#define $iii := transmit($vasen.$i.alku);
    if (length($vasen.$i.alku) greater length($vasen.$i.perusmuoto)) then
      $s := $vasen.$i.alku;
    end;
    define $s1 := substring($s,1R);
    define $s2 := substring($oikea.perusmuoto,1);
    if (($s1 = $s2) and ($s1 matches @ääntiö) and ($s2 matches @ääntiö)) then
      stop;
    end;
  end;
##  ? yhdyssana_oikein ($vasen, $oikea, $i);

  define $res := $vasen + <[alku: $sana] + $oikea>;

  if ($oikea.luokka in <nimisana, laatusana, nimi_laatusana>) then

#define $a := transmit ($vasen);
    if (tavuviiva in $oikea.jatko) then
      result $res, rules tavuviiva;
    end;
    if (yhdyssana in $oikea.jatko) then
      result $res, rules yhdyssana;
    end;

    if ($vasen.$i.luokka = tavuviiva) or
######       ($oikea.tiedot = nil or not (ei_ysj in $oikea.tiedot)) then
       ($oikea.tiedot = nil or (not ((ei_ys in $oikea.tiedot) or (ei_ysj in $oikea.tiedot)))) then
#define $a := transmit ($oikea);
#define $b := transmit ($oikea.tiedot);
      if (omistusliite in $oikea.jatko) then
        result $res, rules omistusliite;
      end;
      if (liitesana in $oikea.jatko) then
        result $res, rules liitesana;
      end;
      if (loppu in $oikea.jatko) then
        result $res, accept;
      end;
      result $res, rules sijapääte;
    end;

    if ($oikea.luokka = nimisana) then
      if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
        result $res, rules nimisanasta_johdettu_laatusana;
      end;
      if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
        result $res, rules nimisanasta_johdettu_seikkasana;
      end;
      if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
        result $res, rules nimi_laatusanan_johdin;
      end;

    elseif ($oikea.luokka = laatusana) then
      if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
        result $res, rules nimi_laatusanan_johdin;
      end;
      if (@voittoaste + @yliaste * $oikea.jatko /= <>) then
        result $res, rules voitto_yliaste;
      end;
      if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
        result $res, rules laatusanasta_johdettu_nimisana;
      end;

    elseif ($oikea.luokka = nimi_laatusana) then
      if ((voittoaste in $oikea.jatko) or (yliaste in $oikea.jatko)) then
        result $res, rules voitto_yliaste;
      end;
      if (@laatusanasta_johdettu_nimisana * $oikea.jatko /= <>) then
        result $res, rules laatusanasta_johdettu_nimisana;
      end;
      if (@nimisanasta_johdettu_seikkasana * $oikea.jatko /= <>) then
        result $res, rules nimisanasta_johdettu_seikkasana;
      end;
      if (@nimi_laatusanan_johdin * $oikea.jatko /= <>) then
        result $res, rules nimi_laatusanan_johdin;
      end;
      if (@nimisanasta_johdettu_laatusana * $oikea.jatko /= <>) then
        result $res, rules nimisanasta_johdettu_laatusana;
      end;
    end;

  elseif ($oikea.luokka = teonsana) then
    ? $vasen.$i.jatko * <yhdyssana, nimisana, laatusana, nimi_laatusana, etuliite> /= <>;
    if ($oikea.jatko * @teonsanasta_johdettu_nimisana /= <>) then
      result $res, rules teonsanan_johdos_nimisana;
    end;
    if ($oikea.jatko * @teonsanasta_johdettu_laatusana /= <>) then
      result $res, rules teonsanan_johdos_laatusana;
    end;
    if ($oikea.jatko * @teonsanasta_johdettu_teonsana /= <>) then
      result $res, rules teonsanan_johdos_teonsana_nimisana,
                         teonsanan_johdos_teonsana_laatusana;
    end;

  elseif ($oikea.luokka = etuliite) then
    ? ($vasen.$i.luokka = tavuviiva) or
      (($oikea.tiedot = nil) or not ((ei_ys in $oikea.tiedot) or (ei_ysj in $oikea.tiedot)));
    ? (<nimisana, nimi_laatusana, laatusana> * $oikea.jatko /= <>);
    result $res, rules yhdyssana, tavuviiva;

  elseif ($oikea.luokka in <paikannimi, sukunimi>) then
    ? johdin_lAinen in $oikea.jatko;
    result $res, rules paikannimen_ja_sukunimen_lainen_johdin;
  end;
end;