Ocenite etot tekst:








          Proizvodstvenno-vnedrencheskij kooperativ

                    "I N T E R F E J S"













                Dialogovaya Edinaya Mobil'naya

                    Operacionnaya Sistema

                        Demos/P 2.1










        Generator programm leksicheskogo analiza  lex
















                           Moskva

                            1988















Annotaciya

     V dokumente opisan yazyk programmirovaniya lex,  prednaz-
nachennyj dlya razrabotki programm leksicheskogo analiza.  Pri-
vodyatsya pravila raboty s kompilyatorom yazyka lex OS DEMOS.





























































1.  Vvedenie

     lex - generator programm leksicheskogo  analiza.  Leksi-
cheskij  analiz  - eto raspoznavanie leksem vo vhodnom potoke
simvolov. Predpolozhim, chto zadano  nekotoroe  konechnoe  mno-
zhestvo  slov  (leksem) v nekotorom yazyke i nekotoroe vhodnoe
slovo.  Neobhodimo ustanovit', kakoj element mnozhestva (esli
on sushchestvuet) sovpadaet s dannym vhodnym slovom.

     Obychno leksicheskij analiz  vypolnyaetsya  tak  nazyvaemym
leksicheskim  analizatorom.   Leksicheskij  analizator  -  eto
programma.

     Leksicheskij analiz primenyaetsya vo mnogih sluchayah,  nap-
rimer,  dlya  postroeniya  paketnogo  redaktora ili v kachestve
raspoznavatelya  direktiv  v  dialogovoj  programme  i   t.d.
Odnako,  naibolee vazhnoe primenenie leksicheskogo analizatora
- eto ispol'zovanie ego  v  kompilyatore.  Zdes'  leksicheskij
analizator vypolnyaet funkciyu programmy vvoda dannyh.

     Leksicheskij analizator vypolnyaet pervuyu stadiyu kompilya-
cii  -  chitaet stroki kompiliruemoj programmy, vydelyaet lek-
semy i peredaet ih na dal'nejshie stadii kompilyacii  (gramma-
ticheskij razbor, kodogeneraciyu i t.d.).

     Leksicheskij analizator raspoznaet tip kazhdoj leksemy  i
sootvetstvuyushchim  obrazom pomechaet ee. Naprimer, pri kompilya-
cii Si-programmy mogut byt' vydeleny sleduyushchie tipy  leksem:
chislo, identifikator, operator, ogranichitel' i t.d.

     Leksicheskij analizator dolzhen ne tol'ko  vydelit'  lek-
semu,  no  i  vypolnit'  nekotorye preobrazovaniya. Naprimer,
esli leksema - chislo, to ego neobhodimo perevesti  vo  vnut-
rennyuyu  (dvoichnuyu)  formu  zapisi  kak chislo s plavayushchej ili
fiksirovannoj tochkoj.  A esli leksema  -  identifikator,  to
ego  neobhodimo  razmestit'  v  tablice,  chtoby v dal'nejshem
obrashchat'sya k nemu ne po imeni, a po adresu v tablice.

     Hotya leksicheskij analiz po svoej  idee  prost,  tem  ne
menee eta faza raboty kompilyatora chasto zanimaet bol'she vre-
meni, chem lyubaya drugaya. CHastichno eto proishodit iz-za  neob-
hodimosti  prosmatrivat' i analizirovat' ishodnyj tekst sim-
vol za simvolom. Inogda dazhe byvaet neobhodimo vernut'  pro-
chitannyj  simvol  vo  vhodnoj  potok  s tem, chtoby povtorit'
prosmotr i analiz.

     Proishodit eto potomu, chto chasto byvaet trudno  oprede-
lit', gde prohodyat granicy leksemy.

     Dopustim, imeyutsya dve leksemy:

            make
            makefile


 3










Pust' iz vhodnogo potoka postupaet nabor simvolov:

            ...makefile...

Pri analize vhodnogo potoka simvolov budet vydelena  leksema
make, hotya pravil'no bylo by vydelit' leksemu makefile.

     Edinstvennyj sposob preodolet' eto zatrudnenie -  pros-
motr  poluchennoj  cepochki  simvolov  nazad i vpered. V nashem
primere pri vydelenii leksemy  make  my  dolzhny  prosmotret'
sleduyushchij  postupayushchij simvol i, esli on budet simvolom "f",
to vpolne vozmozhno, chto postupaet leksema makefile.

     Process prosmotra vhodnogo potoka  mozhno  rassmatrivat'
kak dvizhenie vlevo i vpravo ramki nad cepochkoj simvolov. Pri
etom analiziruetsya tol'ko tot simvol, kotoryj  ohvachen  ram-
koj.

                   ...
                   . .
       source  make.f.ile file compiler
                   . .
                   ...
        <===                      ===>

Analiz zaklyuchaetsya v opredelenii sootvetstviya  rassmatrivae-
moj  posledovatel'nosti  simvolov nekotoromu tak nazyvaemomu
regulyarnomu vyrazheniyu.

     Naprimer, regulyarnoe vyrazhenie

            (+?[0-9])+|(-?[0-9])+

pozvolyaet vydelit' v cepochke vse leksemy tipa  celoe,  pered
kotorymi  libo  ukazan  znak  (+ ili -), libo ne ukazan. Dlya
chisel s tochkoj eto vyrazhenie imelo by vid:

            (+?[0-9.])+|(-?[0-9.])+

V teh sluchayah, kogda vydelenie leksemy  zatrudneno  libo  po
prichine  togo, chto odno regulyarnoe vyrazhenie ne pozvolyaet ee
odnoznachno opredelit', libo iz-za togo, chto leksema yavlyaetsya
chast'yu  drugoj,  prihoditsya pribegat' k kontekstno-zavisimym
algoritmam analiza s ispol'zovaniem levogo i pravogo naprav-
lenij prosmotra vhodnoj cepochki simvolov.

     lex   chastichno  ili  polnost'yu  avtomatiziruet  process
napisaniya programmy leksicheskogo analiza. lex - eto program-
miruyushchaya programma ili generator programm.  lex stroit prog-
rammu  - leksicheskij analizator na tak nazyvaemom host-yazyke
(ili "glavnom" yazyke). |to znachit, chto Lex-programma pishetsya
na  "yazyke" lex, a Lex-generator, v svoyu ochered', generiruet
programmu leksicheskogo analiza na kakom-libo  drugom  yazyke.


 4










Dannaya versiya lex generiruet leksicheskie analizatory na yazy-
kah Si i Ratfor (racional'nyj dialekt Fortrana). V  kachestve
host-yazyka  my  budem  ispol'zovat'  yazyk  Si.  Svedeniya  ob
ispol'zovanii  v  kachestve  host-yazyka  Ratfor  vydeleny   v
otdel'nyj paragraf.

     V kataloge /usr/lib/lex imeetsya fajl-zagotovka  ncform,
kotoryj  ispol'zuetsya  Lex-generatorom dlya postroeniya leksi-
cheskogo analizatora.  |tot fajl yavlyaetsya uzhe  gotovoj  prog-
rammoj  leksicheskogo  analiza, no v nem ne opredeleny dejst-
viya, kotorye neobhodimo vypolnit' pri raspoznavanii leksemy,
otstutstvuyut i sami leksemy, ne sformirovany rabochie massivy
i t.d.

     lex na osnove Lex-programmy dostraivaet fajl ncform.  V
rezul'tate  my poluchaem fajl so standartnym imenem lex.yy.c,
kotoryj yavlyaetsya tekstom Si-programmy, osushchestvlyayushchej leksi-
cheskij analiz.

     Lex-programma imeet sleduyushchij format:

            opredeleniya
            %%
            pravila
            %%
            podprogrammy, sostavlennye
            pol'zovatelem

Lyuboj iz etih razdelov mozhet byt' pustym.   Prostejshaya  Lex-
programma imeet vid:

            %%

Zdes' net nikakih opredelenij i nikakih pravil.

     Vse razdely Lex-programmy my podrobno rassmotrim  nizhe.
Sejchas  celesoobrazno  rassmotret',  chto  predstavlyayut soboj
pravila.

     Pravilo sostoit iz dvuh chastej:

            REGULYARNOE_VYRAZHENIE   DEJSTVIE

Po regulyarnym vyrazheniyam, soderzhashchimsya v levoj chasti pravil,
lex  stroit determinirovannyj konechnyj avtomat. |tot avtomat
osushchestvlyaet interpretaciyu, a ne kompilyaciyu. Kolichestvo pra-
vil  i  ih slozhnost' ne vliyayut na skorost' leksicheskogo ana-
liza, esli tol'ko pravila ne trebuyut slishkom bol'shogo ob®ema
povtornyh  prosmotrov  vhodnoj  posledovatel'nosti simvolov.
Odnako, s rostom chisla pravil i ih slozhnosti  rastet  razmer
konechnogo  avtomata,  interpretiruyushchego ih i, sledovatel'no,
rastet razmer Si-programmy, realizuyushchej etot konechnyj  avto-
mat.


 5










     Rassmotrim v kachestve primera sleduyushchuyu Lex-programmu:

     %%
    [jJ][aA][nN][uU][aA][rR][yY]          {
                         printf("YAnvar'");    }
    [fF][eE][bB][rR][uU][aA][rR][yY]      {
                         printf("Fevral'");   }
    [mM][aA][rR][cC][hH]                  {
                         printf("Mart");      }
    [aA][pP][rR][iI][lL]                  {
                         printf("Aprel'");    }
    [mM][aA][yY]                          {
                         printf("Maj");       }
    [jJ][uU][nN][eE]                      {
                         printf("Iyun'");      }
    [jJ][uU][lL][yY]                      {
                         printf("Iyul'");      }
    [aA][uU][gG][uU][sS][tT]              {
                         printf("Avgust");    }
    [sS][eE][pP][tT][eE][mM][bB][eE][rR]  {
                         printf("Sentyabr'");  }
    [oO][cC][tT][oO][bB][eE][rR]          {
                         printf("Oktyabr'");   }
    [nN][oO][vV][eE][mM][bB][eE][rR]     {
                         printf("Noyabr'");    }
    [dD][eE][cC][eE][mM][bB][eE][rR]      {
                         printf("Dekabr'");   }
    [mM][oO][nN][dD][aA][yY]              {
                        printf("Ponedel'nik");}
    [tT][uU][eE][sS][dD][aA][yY]          {
                         printf("Vtornik");   }
    [wW][eE][dD][nN][eE][sS][dD][aA][yY]  {
                         printf("Sreda");     }
    [tT][hH][uU][rR][sS][dD][aA][yY]      {
                         printf("CHetverg");   }
    [fF][rR][iI][dD][aA][yY]              {
                         printf("Pyatnica");   }
    [sS][aA][tT][uU][rR][dD][aA][yY]      {
                         printf("Subbota");   }
    [sS][uU][nN][dD][aA][yY]              {
                        printf("Voskresen'e");}

Programma stroit konechnyj avtomat, kotoryj  raspoznaet  ang-
lijskie  naimenovaniya  mesyacev i dnej nedeli. Kazhdoe pravilo
zdes'  opredeleyaet  dejstvie  (kotoroe  vzyato   v   figurnye
skobki).  Obratite  vnimanie na to, chto otkryvayushchaya figurnaya
skobka stoit v toj zhe stroke, chto i pravilo - eto trebovanie
lex.

     Dejstvie v kazhdom pravile dannoj  Lex-programmy  -  eto
vyvod  russkogo  znacheniya  najdennogo  anglijskogo  slova. V
kachestve operatora, vypolnyayushchego dejstvie, ispol'zuetsya bib-
liotechnaya funkciya yazyka Si.


 6










     Para figurnyh skobok opredelyaet blok  (v  smysle  yazyka
Si),  kotoryj  mozhet  soderzhat' lyuboe kolichestvo strok. Esli
dejstvie soderzhit vsego odnu stroku Si, to mozhno ee  ukazat'
bez figurnyh skobok, kak obychno.  Edinstvennoe uslovie - ona
dolzhna nachinat'sya v toj zhe stroke,  gde  ukazano  regulyarnoe
vyrazhenie.

     V programme soderzhitsya tol'ko razdel pravil,  ih  vsego
19. Regulyarnoe vyrazhenie kazhdogo pravila opredelyaet anglijs-
koe slovo, napisannoe  malen'kimi  ili  bol'shimi  latinskimi
simvolami.    Naprimer    "May"    (Maj)    opredelen    kak
"[mM][aA][yY]". Po etomu regulyarnomu vyrazheniyu  budet  vyde-
lena vo vhodnom potoke simvolov leksema "May", a po dejstviyu
etogo pravila budet vyvedeno "Maj". Nalichie bol'shoj i  maloj
bukvy  v kvadratnyh skobkah obespechivaet raspoznavanie slova
"May", napisannogo lyubymi latinskimi simvolami.

     Takim  obrazom,   dannaya   Lex-programma   stroit   Si-
programmu, kotoraya perevodit na russkij yazyk imena mesyacev i
dnej nedeli.

     Dopustim, Lex-programma  razmeshchena  v  fajle  source.l,
togda,  chtoby poluchit' leksicheskij analizator na Si, neobho-
dimo vypolnit' sleduyushchij nabor komand:

            % lex  source.l
            % cc -O  lex.yy.c  -ll  -o  program
            %

lex vsegda, esli ne ukazano drugoe, stroit vyhodnoj  fajl  s
imenem lex.yy.c - Si-programmu - leksicheskij analizator.  Vo
vtoroj stroke  etoj  posledovatel'nosti  komand  zapuskaetsya
Si-kompilyator, kotoryj vyvodit rezul'tat v fajl program.

     Program mozhet rabotat' kak fil'tr v  konvejere  komand,
kak samostoyatel'naya komanda i v interaktivnom rezhime. Napri-
mer:

            % program

            May
            Maj
            MONDAY
            Ponedel'nik
            MoNdaY
            Ponedel'nik

            CNTRL/C
            %

Flag -ll trebuet podklyucheniya  biblioteki  /usr/lib/libl.a  -
biblioteki  lex.   Esli  neobhodimo poluchit' samostoyatel'nuyu
programmu,  kak  v  dannom  sluchae,  podklyuchenie  biblioteki


 7










obyazatel'no,  poskol'ku  togda  iz nee podklyuchaetsya golovnoj
razdel main. V protivnom sluchae, esli imeetsya  neobhodimost'
vklyuchit'  analizator  v  kachestve funkcii v druguyu programmu
(naprimer, v programmu grammaticheskogo razbora), etu biblio-
teku  neobhodimo  vyzvat'  uzhe pri sborke i togda, esli main
opredelen v  vyzyvayushchej  leksicheskij  analizator  programme,
redaktor  svyazej  ne budet podklyuchat' razdel main iz biblio-
teki lex.

     Esli neobhodimo poluchit' fajl  s  imenem,  otlichnym  ot
lex.yy.c, mozhno vospol'zovat'sya flagom -t :

            % lex  -t  source.l  >&gt;  file

Po etomu flagu rezul'tat postupaet v fajl file.

2.  Regulyarnye vyrazheniya v Lex-pravilah


     Regulyarnye  vyrazheniya  opredelyayut  leksemu.  Regulyarnoe
vyrazhenie  mozhet  soderzhat'  simvoly  latinskogo  i russkogo
alfavitov v  verhnem  i  nizhnem  registrah,  drugie  simvoly
(cifry, znaki prepinaniya i t.d.) i simvoly-operatory.

     Operatory pozvolyayut osushchestvlyat' razlichnye dejstviya nad
vydelennoj  cepochkoj simvolov.  Operatory takzhe oboznachayutsya
simvolami.

2.1.  Oboznacheniya simvolov v vyrazheniyah

     V vyrazhenii mozhno  ispol'zovat'  lyuboj  simvol.  Simvol
mozhno ukazyvat' v dvojnyh kavychkah. V etom sluchae eto vsegda
prosto simvol - ego special'noe znachenie otmenyaetsya.  Napri-
mer:

            "abc"
             abc

eti posledovatel'nosti simvolov identichny.

.    tochka oznachaet lyuboj simvol, krome simvola novoj stroki
     "\n";

\vos'merichnyj_kod_simvola
     ukazanie simvola ego vos'merichnym kodom (kak v Si);

\n   simvol novoj stroki;

\t   simvol tabulyacii;

\b   vozvrat kursora na odin shag nazad;




 8










probel
     lyuboj simvol probela v vyrazhenii, esli on ne  nahoditsya
     vnutri  kvadratnyh skobok, neobhodimo zaklyuchat' v dvoj-
     nye kavychki. |to neobhodimo, tak kak probel i tabulyaciya
     ispol'zuyutsya lex v kachestve razdelitelya mezhdu opredele-
     niem i dejstviem v pravile.

2.2.  Operatory regulyarnyh vyrazhenij

     Operatory  oboznachayutsya  simvolami-operatorami,  k  nim
otnosyatsya:

         \   ^   ?   *   +   |   $   /   %
               []   {}   ()   <&lt;>&gt;

Kazhdyj iz etih simvolov ili par skobok v regulyarnom  vyrazhe-
nii  igraet rol' operatora.  Esli neobhodimo otmenit' speci-
al'noe znachenie simvola, oboznachayushchego operator,  pered  nim
nuzhno postavit' simvol \ ili ukazat' ego v dvojnyh kavychkah.
Naprimer:

            abc+    - simvol "+" - operator;
            abc\+   - simvol "+";
            abc"+"  - simvol "+".


2.3.  Operator vydeleniya klassov simvolov

     Kvadratnye skobki zadayut klassy simvolov, kotorye v nih
zaklyucheny.

[abc]
     oznachaet libo simvol "a", libo "b", libo simvol "c";

     Znak - ispol'zuetsya dlya ukazaniya lyubogo simvola iz lek-
sikograficheski uporyadochennoj posledovatel'nosti:

[A-z]
     oznachaet lyuboj latinskij simvol;

[A-YA]
     lyubaya propisnaya russkaya bukva;

[+-0-9]
     vse cifry i znaki "+" i "-".

2.4.  Povtoriteli

     Kogda neobhodimo ukazat' povtoryaemost'  vhozhdeniya  sim-
vola   v   regulyarnom   vyrazhenii,   ispol'zuyut   operatory-
povtoriteli * i  +.




 9










     Operator * oznachaet lyuboe (v tom chisle i 0) chislo vhozh-
denij simvola ili klassa simvolov. Naprimer:

x*   lyuboe chislo vhozhdenij simvola "x";

abc* lyuboe chislo vhozhdenij cepochki "abc";

[A-z]*
     lyuboe chislo vhozhdenij lyuboj latinskoj bukvy;

[A-ZA-YAa-za-ya_0-9]*
     lyuboe vhozhdenie russkih i latinskih bukv, znaka podcher-
     kivaniya i cifr.

     Operator + oznachaet odno i bolee vhozhdenij. Naprimer:

x+   odno ili bolee vhozhdenij "x";

[0-9]+
     odno ili bolee vhozhdenij cifr;

abc+ odno ili bolee vhozhdenij cepochki abc;

[A-z]+
     odno ili bolee vhozhdenij lyuboj latinskoj bukvy.

2.5.  Operatory vybora

     Operatory:

            /   |   ?   $   ^

upravlyayut processom vybora simvolov.

     Operator /:

ab/cd
     "ab" uchityvaetsya tol'ko togda,  kogda  za  nim  sleduet
     "cd".

     Operator |:

ab|cd
     ili "ab", ili "cd".

     Operator ?:

x?   oznachaet neobyazatel'nyj simvol "x".

_?[A-Za-z]*
     oznachaet, chto pered cepochkoj lyubogo kolichestva  latins-
     kih bukv mozhet byt' neobyazatel'nyj znak podcherkivaniya.



 10










-?[0-9]+
     vydelit lyuboe celoe chislo s neobyazatel'nym minusom vpe-
     redi.

     Operator $:

x$   oznachaet vybrat' simvol "x", esli on yavlyaetsya poslednim
     v stroke. Stoit pered simvolom "\n"!

abc$ oznachaet vybrat'  cepochku  "abc",  esli  ona  zavershaet
     stroku.

     Operator ^:

^x   oznachaet vybrat' simvol "x", esli  on  yavlyaetsya  pervym
     simvolom stroki;

^abc oznachaet vybrat' cepochku simvolov "abc", esli ona nachi-
     naet stroku.

[^A-Z]*
     oznachaet vse simvoly, krome propisnyh  latinskih  bukv.
     Kogda simvol ^ stoit pered vyrazheniem ili vnutri [], on
     vypolnyaet operaciyu dopolnenie. Vnutri kvadratnyh skobok
     simvol ^ dolzhen obyazatel'no stoyat' pervym u otkryvayushchej
     skobki!

2.6.  Operator {}

     Operator {} imeet dva razlichnyh primeneniya:

x{n,m}  zdes' n i m natural'nye, m > n.  Oznachaet ot n do  m
        vhozhdenij  x, naprimer, x{2,7} - ot 2 do 7 vhozhdenij
        x.

{imya}   vmesto {imya} v dannoe mesto vyrazheniya budet podstav-
        leno  opredelenie  imeni iz oblasti opredelenij Lex-
        programmy.

     Primer:

    BUKVA           [A-ZA-YAa-za-ya_]
    CIFRA           [0-9]
    IDENTIFIKATOR   {BUKVA}({BUKVA}|{CIFRA})*
     %%
    {IDENTIFIKATOR} printf("\n%s",yytext);

lex postroit leksicheskij analizator, kotoryj  budet  oprede-
lyat'  i vyvodit' vse "slova" iz vhodnogo fajla. Pod slovom v
dannom sluchae podrazumevaetsya identifikator Si-programmy.  V
etom    primere    {IDENTIFIKATOR}    budet    zamenen    na
{BUKVA}({BUKVA}|{CIFRA})*, zatem  na  [A-ZA-YAa-za-ya_]([A-ZA-
YAa-za-ya_]|[0-9])*.


 11










     yytext  -  eto  vneshnij   massiv   simvolov   programmy
lex.yy.c, kotoruyu stroit lex.  yytext formiruetsya v processe
chteniya vhodnogo fajla i soderzhit tekst, dlya  kotorogo  usta-
novleno sootvetstvie kakomu-libo vyrazheniyu. |tot massiv dos-
tupen pol'zovatel'skim razdelam Lex-programmy.

     Operator printf vyvodit kazhdyj identifikator  na  novoj
stroke.

     Pravilo ".|\n           ;" ispol'zuetsya dlya togo, chtoby
propustit'  (ne  vyvodit')  vse cepochki simvolov, kotorye ne
sootvetstvuyut regulyarnomu vyrazheniyu {IDENTIFIKATOR}.

2.7.  Operator <&lt;>&gt;. Sluzhebnye slova START i BEGIN

     Razdel pravil Lex-programmy mozhet soderzhat' aktivnye  i
neaktivnye  pravila.  Aktivnye  pravila  vypolnyayutsya vsegda.
Neaktivnye vypolnyayutsya tol'ko v teh sluchayah, kogda  vypolnya-
etsya nekotoroe nachal'noe uslovie.

     Nachal'nye usloviya  Lex-programmy  pomeshchayutsya  v  razdel
opredelenij,  a  neaktivnye pravila pomechayutsya sootvetstvuyu-
shchimi usloviyami.  Operator  START  pozvolyaet  ukazat'  spisok
nachal'nyh  uslovij Lex-programmy, a operator BEGIN pozvolyaet
aktivirovat' pravila, pomechennye nachal'nymi usloviyami.

     Aktivnye    pravila    imeyut    sleduyushchij    sintaksis:
REGULYARNOE_VYRAZHENIE   DEJSTVIE

     Neaktivnye   pravila   imeyut    sleduyushchij    sintaksis:
<&lt;METKA_USLOVIYA>&gt;REG_VYRAZHENIE  DEJSTVIE

     VAZHNO: lyuboe pravilo dolzhno nachinat'sya s pervoj pozicii
stroki,  probely  i tabulyacii nedopustimy - oni ispol'zuyutsya
kak razdeliteli mezhdu regulyarnym vyrazheniem  i  dejstviem  v
pravile!

     Rassmotrim primer:

















 12










    %START  COMMENT

    KOMM_NACHALO     "/*"
    KOMM_KONEC      "*/"

     %%
    {KOMM_NACHALO}         { ECHO;
                            BEGIN COMMENT;};
    [\t\n]*               ;


    <COMMENT>[^*]*          ECHO;
    <COMMENT>[^/]        ECHO;
    <COMMENT>{KOMM_KONEC} {
                            ECHO;
                            printf("0);
                            BEGIN 0;};

lex postroit leksicheskij analizator, kotoryj  vydelyaet  kom-
mentarii  v  Si-programme i zapisyvaet ih v standartnyj fajl
vyvoda. Programma nachinaetsya s klyuchevogo slova START,  koto-
roe  ukazano  posle  simvola  %.  Klyuchevoe slovo START mozhno
ukazat' i tak:  Start, ili S, ili s  .  Za  klyuchevym  slovom
START ukazana metka nachal'nogo usloviya COMMENT.

     Operator "<COMMENT>x" oznachaet  -  x,  esli  analizator
nahoditsya v nachal'nom uslovii COMMENT.

     Operator  "BEGIN  COMMENT;"  perevodit   analizator   v
nachal'noe  uslovie  COMMENT (smotrite pervoe pravilo razdela
pravil etoj Lex-programmy). Posle etogo analizator uzhe naho-
ditsya v novom sostoyanii i teper' razbor vhodnogo potoka sim-
volov budet osushchestvlyaetsya i temi pravilami, kotorye nachina-
yutsya operatorom "<COMMENT>".  Naprimer, pravilo

            <COMMENT>[^*]*          ECHO;

vypolnyaetsya tol'ko togda, kogda vo vhodnom  potoke  simvolov
budet  obnaruzheno  nachalo kommentariev ("/*"). V etom sluchae
analizator zapisyvaet v standartnyj fajl vyvoda lyuboe  chislo
(v tom chisle i nol') simvolov, otlichnyh ot simvola "*". Ope-
rator "BEGIN 0;" perevodit analizator v ishodnoe sostoyanie.

     Lex-programma  mozhet  soderzhat'  neskol'ko   pomechennyh
nachal'nyh  uslovij.  Naprimer, esli Lex-programma nachinaetsya
strokoj

            %START AA BB CC DD

to eto oznachaet, chto ona upravlyaet chetyr'mya nachal'nymi  sos-
toyaniyami  analizatora.  V kazhdoe iz etih nachal'nyh sostoyanij
analizator mozhno perevesti, ispol'zuya operator BEGIN.



 13










     Kazhdoe pravilo,  pered  kotorym  ukazan  operator  tipa
"<&lt;METKA>&gt;", my budem nazyvat' pomechennym pravilom. Metka for-
miruetsya tak zhe, kak i metka v Si.

     Kolichestvo pomechennyh pravil ne ogranichivaetsya.   Krome
togo, razreshaetsya odno pravilo pomechat' neskol'kimi metkami,
naprimer:

            <&lt;METKA1,METKA2,METKA3>&gt;x  DEJSTVIE

Zapyataya - obyazatel'nyj razdelitel' spiska metok!

     Rassmotrim primer s neskol'kimi nachal'nymi usloviyami:

    %START AA BB CC

    BUKVA           [A-ZA-YAa-za-ya_]
    CIFRA           [0-9]
    IDENTIFIKATOR   {BUKVA}({BUKVA}|{CIFRA})*

     %%
    ^#                          BEGIN AA;
    ^[ \t]*main                 BEGIN BB;
    ^[ \t]*{IDENTIFIKATOR}      BEGIN CC;
    \t                          ;
    \n                          BEGIN 0;

    <AA>define       printf("Opredelenie.\n");
    <AA>include      printf("Vklyuchenie.\n");
    <AA>ifdef        {
        printf("Uslovnaya kompilyaciya.\n"); }

    <BB>[^\,]*","[^\,]*")"  {
        printf("main s argumentamii.\n"); }

    <BB>[^\,]*")"   {
        printf("main bez argumentov.\n"); }

    <CC>":"/[ \t]    printf("Metka.\n");

Programma soderzhit aktivnye i neaktivnye pravila.  Vse neak-
tivnye pravila pomecheny, pered nimi ukazana metka nachal'nogo
usloviya. Lex-programma upravlyaet tremya nachal'nymi usloviyami,
v sootvetstvii s kotorymi aktiviruyutsya pomechennye pravila.

     V rezul'tate raboty lex my poluchim leksicheskij analiza-
tor,  kotoryj budet raspoznavat' v Si-programme stroki prep-
rocessora Si-kompilyatora, vydelyat' funkciyu  main,  raspozna-
vaya,  s  argumentami  ona  ili  bez nih, raspoznavat' metki.
Leksicheskij analizator ne vyvodit nichego, krome soobshchenij  o
vydelennyh leksemah.




 14










3.  Struktura Lex-programmy

     Lex-programma vklyuchaet razdely  opredelenij,  pravil  i
pol'zovatel'skih  programm.   Rassmotrim  podrobnee  sposoby
oformleniya etih razdelov.

     Vse stroki, v kotoryh zanyata pervaya poziciya,  otnosyatsya
k  Lex-programme. Lyubaya stroka, ne yavlyayushchayasya chast'yu pravila
ili dejstviya, kotoraya nachinaetsya s  probela  ili  tabulyacii,
kopiruetsya  v sgenerirovannuyu programmu lex.yy.c - rezul'tat
raboty lex.

3.1.  Razdel opredelenij Lex-programmy

     Opredeleniya, prednaznachennye dlya lex, pomeshchayutsya  pered
pervym %%. Lyubaya stroka etogo razdela, ne soderzhashchayasya mezhdu
%{ i %} i nachinayushchayasya v pervoj kolonke, yavlyaetsya  opredele-
niem   stroki   podstanovki  lex.  Razdel  opredelenij  Lex-
programmy mozhet vklyuchat':

       nachal'nye usloviya,
       opredeleniya,
       fragmenty programmy pol'zovatelya,
       tablicy naborov simvolov,
       ukazateli host-yazyka,
       izmeneniya razmerov vnutrennih massivov,
       kommentarii v formate host-yazyka.


     NACHALXNYE USLOVIYA zadayutsya v forme:

            %START imya1 imya2 ...

Esli nachal'nye usloviya opredeleny, to eta stroka dolzhna byt'
pervoj v Lex-programme.

     OPREDELENIYA zadayutsya v forme:

            imya  translyaciya

V kachestve razdelitelya ispol'zuetsya odin ili bolee  probelov
ili tabulyacij. Primer:

    BUKVA           [A-ZA-YAa-za-ya_]
    DIGIT           [0-9]
    IDENTIFIKATOR   {BUKVA}({BUKVA}|{DIGIT})*

Imya - kak obychno,  lyubaya  posledovatel'nost'  bukv  i  cifr,
nachinayushchayasya s bukvy.  Translyaciya - eto regulyarnoe vyrazhenie
(ili ego chast'), kotoroe budet podstavleno  vsyudu  tam,  gde
ukazano imya (smotrite tret'yu stroku etogo primera).




 15










     FRAGMENTY PROGRAMMY POLXZOVATELYA ukazyvayutsya dvumya spo-
sobami:

     - v vide "probel fragment";

     - v vide:

                   %{
                   stroki
                   fragmenta
                   programmy
                   pol'zovatelya
                   %}

       Takaya  forma  vklyucheniya  pol'zovatel'skogo  fragmenta
       neobhodima  dlya vvoda, naprimer, makroopredelenij Si,
       kotorye dolzhny nachinat'sya v  pervoj  kolonke  stroki.
       Vse stroki fragmenta pol'zovatel'skoj programmy, raz-
       meshchennye v razdele opredelenij, budut yavlyat'sya  vnesh-
       nimi dlya lyuboj funkcii programmy lex.yy.c

     TABLICA NABOROV SIMVOLOV zadaetsya v vide:

            %T
            celoe_chislo   stroka_simvolov
            .........
            celoe_chislo   stroka_simvolov
            %T

Sgenerirovannaya programma lex.yy.c  osushchestvlyaet  vvod-vyvod
simvolov  posredstvom  bibliotechnyh  funkcij  lex  s imenami
input, output, unput. Takim obrazom, lex pomeshchaet  v  yytext
simvoly  v  predstavlenii,  ispol'zuemom v etih bibliotechnyh
funkciyah. Dlya vnutrennego ispol'zovaniya  simvol  predstavlya-
etsya  celym  chislom,  znachenie  kotorogo  obrazovano naborom
bitov, predstavlyayushchih simvol v konkretnoj |VM.  Pol'zovatelyu
predostavlyaetsya  vozmozhnost'  menyat'  predstavlenie simvolov
(celyh konstant) s pomoshch'yu tablicy  naborov  simvolov.  Esli
tablica  simvolov  prisutstvuet  v  razdele  opredelenij, to
lyuboj simvol, poyavlyayushchijsya libo vo vhodnom  potoke,  libo  v
pravilah, dolzhen byt' opredelen v tablice simvolov. Simvolam
nel'zya naznachat' chislo 0 i chislo, bol'shee chisla, vydelennogo
dlya vnutrennego predstavleniya simvolov konkretnoj |VM.

     Primer:










 16










            %T
            1       Aa
            2       Bb
            3       Cc
            .
            .
            .
            26      Zz
            27
            28      +
            29      -
            30      0
            31      1
            .
            .
            .
            39      9
            %T

V etom primere simvoly verhnego i nizhnego registrov  perevo-
dyatsya  v  chisla  1-26,  simvol  novoj stroki v 27, "+" i "-"
perevodyatsya v chisla 28 i 29, a cifry - v chisla 30-39.

     IZMENENIYA RAZMERA VNUTRENNIH MASSIVOV zadayutsya v forme:

            %x   chislo

    chislo - novyj razmer massiva;
    x     - odna iz bukv:

         p   - pozicii;
         n   - sostoyaniya;
         e   - uzly dereva;
         a   - upakovannye perehody;
         k   - upakovannye klassy simvolov;
         o   - massiv vyhodnyh elementov.

lex imeet vnutrennie tablicy,  razmery  kotoryh  ogranicheny.
Pri  postroenii  programmy  leksicheskogo analiza mozhet proi-
zojti perepolnenie lyuboj iz etih tablic, o chem lex  soobshchaet
pri  postroenii  leksicheskogo analizatora. Pol'zovatelyu pre-
dostavlyaetsya vozmozhnost' izmenit' razmery  tablic  (sokrashchaya
razmery  odnih  i  uvelichivaya razmery drugih) takim obrazom,
chtoby oni ne perepolnyalis'. Estestvenno, eti izmeneniya  voz-
mozhny  lish'  v  predelah  toj pamyati, kotoraya vydelyaetsya pod
process.

     Nizhe perechisleny razmery tablic,  kotorye  ustanavliva-
yutsya po umolchaniyu:






 17










    p - pozicij                      1500
    n - sostoyanij                     300
    e - uzlov                         600
    a - upakovannyh perehodov        1500
    k - upakovannyh klassov simvolov 1000
    o - vyhodnyh elementov           1500

Dlya togo chtoby opredelit', kakovy razmery tablic i naskol'ko
oni zanyaty, mozhno ispol'zovat' flag -v, naprimer:

    %  lex -v source.l
    33/600 uzlov(%e)
    97/1500 pozicij(%p)
    17/300 sostoyanij(%n)
    2551 perehodov
    18/1000 upakovannyh klassov simvolov(%k)
    41/1500 upakovannyh perehodov(%a)
    68/1500 vyhodnyh elementov(%o)
    %

Zdes' pokazano soobshchenie, kotoroe vyvodit lex po  flagu  -v.
CHislo pered simvolom "/" ukazyvaet skol'ko elementov massiva
zanyato, a chislo za simvolom "/" ukazyvaet ustanovlennyj raz-
mer massiva.

     KOMMENTARII v  razdele  opredelenij  zadayutsya  v  forme
host-yazyka i dolzhny nachinat'sya ne s pervoj kolonki stroki.

3.2.  Razdel pravil

     Vse, chto ukazano posle pervoj pary %% i do  konca  Lex-
programmy ili do vtoroj pary %%, esli ona ukazana, otnositsya
k razdelu pravil. Razdel pravil mozhet  soderzhat'  pravila  i
fragmenty  programm. Fragmenty programm, soderzhashchiesya v raz-
dele pravil, stanovyatsya chast'yu funkcii yylex fajla lex.yy.c,
v  kotoroj  osushchestvlyaetsya vypolnenie dejstvij aktivnyh pra-
vil.  Fragment programmy ukazyvaetsya sleduyushchim obrazom:

            %{
            stroki
            fragmenta
            programmy
            %}

    Naprimer:

            %%
            %{
            #include file.h
            %}
            .
            .
            .


 18










Zdes'  stroka  "#include  file.h"  stanet  strokoj   funkcii
yylex().

     Razdel pravil mozhet vklyuchat' spisok aktivnyh i neaktiv-
nyh  (pomechennyh)  pravil.   Aktivnye  i  neaktivnye pravila
mogut byt' ukazany v lyubom poryadke, v tom chisle byt'  "pere-
meshannymi"  v  spiske.  Aktivnye pravila vypolnyayutsya vsegda,
neaktivnye tol'ko po ssylke na nih operatorom BEGIN.

     Aktivnoe pravilo imeet vid:

            VYRAZHENIE  DEJSTVIE


     Neaktivnoe pravilo imeet vid:

            <METKA>VYRAZHENIE  DEJSTVIE
    ili
            <SPISOK_METOK>VYRAZHENIE DEJSTVIE

    gde SPISOK_METOK imeet vid:

            metka1,metka2,...

V kachestve pervogo pravila razdela pravil mozhet byt' pravilo
vida:

            BEGIN METKA;

V etom pravile otsutstvuet VYRAZHENIE, i pervym  dejstviem  v
razdele  pravil  budet  aktivizaciya  pomechennyh  pravil. Dlya
vozvrashcheniya avtomata v ishodnoe sostoyanie mozhno ispol'zovat'
dejstvie:

            BEGIN 0;

Vazhno otmetit' sleduyushchee. Esli Lex-programma soderzhit aktiv-
nye  i  neaktivnye  pravila,  to  aktivnye  pravila rabotayut
vsegda. Operator  "BEGIN  METKA;"  prosto  rasshiryaet  spisok
aktivnyh pravil, aktiviruya pomechennye metkoj METKA. A opera-
tor "BEGIN 0;" udalyaet iz spiska aktivnyh pravil  vse  pome-
chennye  pravila,  kotorye do etogo byli aktivirovany.  Krome
togo, esli iz pomechennogo i aktivnogo v dannyj  moment  vre-
meni  pravila  osushchestvlyaetsya  dejstvie  BEGIN  METKA, to iz
pomechennyh pravil aktivnymi  ostanutsya  tol'ko  te,  kotorye
pomecheny metkoj METKA.

3.2.1.  Dejstviya v pravilah Lex-programmy

     Dejstvie mozhno predstavlyat' libo kak operator lex, nap-
rimer,  "BEGIN  METKA;",  libo kak operator Si. Esli imeetsya
neobhodimost' vypolnit' dostatochno bol'shoj nabor  preobrazo-
vanij,  to  dejstvie  oformlyayut  kak  blok  Si-programmy (on


 19










nachinaetsya otkryvayushchej figurnoj skobkoj i zavershaetsya zakry-
vayushchej figurnoj skobkoj), soderzhashchij neobhodimye fragmenty.

     Dejstvie v pravile ukazyvaetsya cherez ne menee, chem odin
probel  ili  tabulyaciyu posle vyrazheniya (obyazatel'no v toj zhe
stroke, gde i vyrazhenie), a ego prodolzhenie mozhet byt'  uka-
zano  v sleduyushchih strokah tol'ko v tom sluchae, esli dejstvie
oformleno kak blok Si-programmy.

     Oblast' dejstviya peremennyh, ob®yavlennyh vnutri  bloka,
rasprostranyaetsya  tol'ko  na etot blok. Vneshnimi peremennymi
dlya vseh dejstvij budut yavlyat'sya tol'ko te peremennye, koto-
rye ob®yavleny v razdele opredelenij Lex-programmy.

     Dejstviya v  pravilah  Lex-programmy  vypolnyayutsya,  esli
pravilo  aktivno, i esli avtomat raspoznaet cepochku simvolov
iz vhodnogo potoka kak sootvetstvuyushchuyu regulyarnomu vyrazheniyu
dannogo pravila.  Odnako, odno dejstvie vypolnyaetsya vsegda -
ono zaklyuchaetsya v kopirovanii  vhodnogo  potoka  simvolov  v
vyhodnoj.  |to  kopirovanie  osushchestvlyaetsya dlya vseh vhodnyh
strok, kotorye ne sootvetstvuyut pravilam, preobrazuyushchim  eti
stroki.   Kombinaciya simvolov, ne uchtennaya v pravilah i poya-
vivshayasya na vhode, budet napechatana na  vyhode.  Mozhno  ska-
zat', chto dejstvie - eto to, chto delaetsya vmesto kopirovaniya
vhodnogo potoka simvolov na vyhod.  CHasto byvaet  neobhodimo
ne  kopirovat'  na vyhod nekotoruyu cepochku simvolov, kotoraya
udovletvoryaet nekotoromu  regulyarnomu  vyrazheniyu.  Dlya  etoj
celi ispol'zuetsya pustoj operator Si, naprimer:

            [ 0         ;

|to pravilo ignoriruet (zapreshchaet) vyvod probelov, tabulyacij
i  simvola  novaya  stroka.   Zapret vyrazhaetsya v tom, chto na
ukazannye simvoly vo vhodnom potoke osushchestvlyaetsya  dejstvie
";"  -  pustoj  operator  Si,  i eti simvoly ne kopiruyutsya v
vyvodnoj potok simvolov.

     Sushchestvuet vozmozhnost' dlya neskol'kih regulyarnyh  vyra-
zhenij ukazyvat' odno dejstvie. Dlya etogo ispol'zuetsya simvol
"|", kotoryj ukazyvaet, chto dejstvie dannogo pravila  sovpa-
daet s dejstviem dlya sleduyushchego, naprimer:

            " "             |
                          |
                          ;

Rezul'tat budet tot zhe, chto i v primere, ukazannom vyshe.

     Kogda neobhodimo vyvesti ili preobrazovat' tekst, soot-
vetstvuyushchij  nekotoromu  regulyarnomu vyrazheniyu, ispol'zuetsya
vneshnij massiv simvolov, kotoryj formiruet lex.   Nazyvaetsya
on yytext i dostupen v dejstviyah pravil. Naprimer:



 20










       [A-Z]+          printf("%s",yytext);

Po etomu pravilu raspoznaetsya  slovo,  soderzhashchee  propisnye
latinskie bukvy i vyvoditsya s pomoshch'yu printf, esli ono vyde-
leno. Operaciya vyvoda raspoznannogo  vyrazheniya  ispol'zuetsya
ochen'  chasto, poetomu imeetsya sokrashchennaya forma zapisi etogo
dejstviya:

            [A-Z]+          ECHO;

Rezul'tat dejstviya etogo pravila budet analogichen rezul'tatu
predydushchego primera.  V vyhodnom fajle lex.yy.c ECHO oprede-
leno kak makropodstanovka:

    #define ECHO fprintf(yyout, "%s",yytext);

Kogda neobhodimo znat' dlinu obnaruzhennoj posledovatel'nosti
simvolov,  ispol'zuetsya  schetchik  najdennyh simvolov yyleng,
kotoryj takzhe dostupen v dejstviyah. Naprimer:

    [A-Z]+    printf("%c",yytext[yyleng-1]);

V etom primere budet vyvoditsya poslednij simvol slova, soot-
vetstvuyushchego  regulyarnomu  vyrazheniyu  [A-Z]+. Rassmotrim eshche
odin primer:

    [A-Z]+ {chislo_slov++;chislo_bukv += yyleng;}

Zdes' vedetsya podschet chisla raspoznannyh slov  i  kolichestva
simvolov vo vseh slovah.

3.2.2.  Poryadok dejstviya aktivnyh pravil

     Spisok pravil Lex-programmy mozhet soderzhat' aktivnye  i
neaktivnye  pravila,  razmeshchennye  v lyubom poryadke v razdele
pravil.  V processe raboty leksicheskogo  analizatora  spisok
aktivnyh pravil mozhet vidoizmenyat'sya za schet dejstvij opera-
tora  BEGIN.  V  processe  raspoznavaniya  simvolov  vhodnogo
potoka  mozhet okazat'sya tak, chto odna cepochka simvolov budet
udovletvoryat' neskol'kim pravilam i,  sledovatel'no,  vozni-
kaet problema: dejstvie kakogo pravila dolzhno vypolnyat'sya?

     Dlya razresheniya etogo  protivorechiya  mozhno  ispol'zovat'
kvantovanie  (razbienie)  regulyarnyh  vyrazhenij  etih pravil
Lex-programmy na takie novye regulyarnye  vyrazheniya,  kotorye
dadut,  po  vozmozhnosti,  odnoznachnoe raspoznavanie leksemy.
Odnako, kogda eto ne sdelano,  lex  ispol'zuet  opredelennyj
determinirovannyj mehanizm razresheniya takogo protivorechiya:

     - vybiraetsya dejstvie togo pravila, kotoroe  raspoznaet
       naibolee dlinnuyu posledovatel'nost' simvolov iz vhod-
       nogo potoka;



 21










     - esli neskol'ko pravil  raspoznayut  posledovatel'nosti
       simvolov  odnoj  dliny,  to vypolnyaetsya dejstvie togo
       pravila, kotoroe zapisano  pervym  v  spiske  razdela
       pravil Lex-programmy.

     Rassmotrim primer:

            .
            .
            .
            [Mm][Aa][Jj]          ECHO;
            [A-YAa-ya]+             ECHO;
            .
            .
            .

Slovo "Maj" raspoznayut oba pravila, odnako, vypolnitsya  per-
voe  iz  nih,  tak kak i pervoe, i vtoroe pravilo raspoznali
leksemu odinakovogo razmera (3  simvola).  Esli  vo  vhodnom
potoke budet, dopustim, slovo "majskij", to pervye 3 simvola
udovletvoryayut pervomu pravilu, a vse 7  simvolov  udovletvo-
ryayut  vtoromu pravilu, sledovatel'no, vypolnitsya vtoroe pra-
vilo, tak kak emu udovletvoryaet bolee dlinnaya posledovatel'-
nost' simvolov.

3.3.  Razdel programm pol'zovatelya

     Vse, chto razmeshcheno za vtorym naborom  %%,  otnositsya  k
razdelu  programm  pol'zovatelya.   Soderzhimoe  etogo razdela
kopiruetsya v vyhodnoj fajl lex.yy.c bez  kakih-libo  izmene-
nij.  V  fajle lex.yy.c stroki etogo razdela rassmatrivayutsya
kak funkcii v smysle Si.  |ti  funkcii  mogut  vyzyvat'sya  v
dejstviyah pravil i, kak obychno, peredavat' i vozvrashchat' zna-
cheniya argumentov.

3.4.  Kommentarii Lex-programmy

     Kommentarii  mozhno  ukazyvat'  vo  vseh  razdelah  Lex-
programmy.  Format  kommentariev dolzhen sootvetstvovat' for-
matu kommentariev host-yazyka. Odnako, v kazhdom razdele  Lex-
programmy  kommentarii  ukazyvayutsya  po  raznomu.  V razdele
opredelenij kommentarii dolzhny nachinat'sya ne s pervoj  pozi-
cii  stroki.  V  razdele  pravil kommentarii mozhno ukazyvat'
tol'ko vnutri blokov,  prinadlezhashchih  dejstviyam.  V  razdele
programm  pol'zovatelya kommentarii ukazyvayutsya kak i v host-
yazyke.

3.5.  Primery Lex-programm

     Primer1.





 22










    %Start  KOMMENT
          /*
           *  Programma zapisyvaet v
           *  standartnyj fajl vyvoda
           *  kommentarii Si-programmy.
           *  Obratite vnimanie na to, chto
           *  zdes' stroki kommentariev ukazany
           *  ne s pervoj pozicii stroki!
           */

    KOMM_NACHALO     "/*"
    KOMM_KONEC      "*/"

     %%
    {KOMM_NACHALO}         { ECHO;
                            BEGIN KOMMENT;}
    [0*               ;

    <KOMMENT>[^*]*          ECHO;
    <KOMMENT>[^/]        ECHO;
    <KOMMENT>{KOMM_KONEC} {
                            ECHO;
                            printf("0);
    /*
     *    Zdes' priveden  primer
     *    ispol'zovaniya kommentariev v
     *    razdele pravil Lex-programmy.
     *    Obratite vnimanie na to, chto
     *    kommentrij ukazan vnutri bloka,
     *    opredelyayushchego dejstvie pravila.
     */
                            BEGIN 0;}
     %%

    /*
     *  Zdes' priveden primer kommentariev
     *  v razdele programm pol'zovatelya.
     */


     Primer 2.














 23










    %Start IC1 IC2 Normal

           /*
            *   Otladochnyj fragment
            *   Lex-programmy, kotoraya stroit
            *   leksicheskij analizator dlya
            *   kompilyatora yazyka Paskal'.
            *   Dejstvie return(...)
            *   vozvrashchaet tip leksemy v
            *   v vyzyvayushchuyu analizator
            *   programmu.
            *   Obratite vnimanie na to, chto v
            *   etoj Lex-programme otsutstvuyut
            *   aktivnye pravila. |to sdelano
            *   v svyazi s tem, chto net
            *   neobhodimosti imet' pravila,
            *   kotorye vsegda aktivny.
            *   Vse cepochki simvolov vhodnogo
            *   potoka, ne raspoznannye v
            *   pravilah, kopiruyutsya v vyhodnoj
            *   potok simvolov.
            */

    LETTER          [A-ZA-YAa-za-ya_]
    DIGIT           [0-9]
    IDENT           {LETTER}({LETTER}|{DIGIT})*
    INT             {DIGIT}+
    FIXED           {INT}?.{INT}
    WHISP           [ 0*

     %%
                          BEGIN Normal;
    <Normal>"{"           BEGIN IC1;
    <IC1>[^}]             ;
    <IC1>"}"              BEGIN Normal;
    <Normal>"(*"          BEGIN IC2;
    <IC2>[^*]|[^)]     ;
    <IC2>&gt;"*)"             BEGIN Normal;

    <Normal>'([^']|'')*'  return( stroka );
    <Normal>"<>"          return( ne_ravno );
    <Normal>"="           return( ravno );
    <Normal>"<"           return( men'she );
    <Normal>">"           return( bol'she );
    <Normal>">="      return(bol'she_ili_ravno);
    <Normal>"<="      return(men'she_ili_ravno);
    <Normal>".."         return( tochka_tochka );
    <Normal>"+"           return( plyus );
    <Normal>"-"           return( minus );
    <Normal>":="          return( prisvoit' );
    <Normal>"*"           return( umnozhit' );
    <Normal>"/"           return( razdelit' );
    <Normal>mod           return( t_mod );


 24










    <Normal>div           return( t_div );
    <Normal>and           return( t_and );
    <Normal>or            return( t_or );
    <Normal>not           return( t_not );
    <Normal>"("           return( lpar );
    <Normal>")"           return( rpar );
    <Normal>"["           return( lbracket );
    <Normal>"]"           return( rbracket );
    <Normal>","           return( comma );
    <Normal>":"           return( colon );
    <Normal>"^"           return( circumflex );
    <Normal>";"           return( semicolon );
    <Normal>write         return( Write );
    <Normal>writeln       return( Writeln );
    <Normal>label         return( t_label );
    <Normal>program       return(  );
    <Normal>const         x( "konstanty" ) ;
    <Normal>type          x( "tipy" ) ;
    <Normal>var           x( "perem" ) ;
    <Normal>procedure     x( "procedura" ) ;
    <Normal>function      x( "funkciya" ) ;
    <Normal>begin         x( "nachalo" ) ;
    <Normal>end{WHISP}.  x( "konec progr" ) ;
    <Normal>end           x( "konec" ) ;
    <Normal>array         x( "massiv" ) ;
    <Normal>of            x( "iz" ) ;
    <Normal>record        x( "zapis'" ) ;
    <Normal>case          x( "vybor" ) ;
    <Normal>in            x( "v" ) ;
    <Normal>file          x( "fajl" ) ;
    <Normal>for           x( "dlya" ) ;
    <Normal>to            x( "k" ) ;
    <Normal>downto        x( "vniz k" ) ;
    <Normal>do            x( "vypoln" ) ;
    <Normal>while         x( "poka" ) ;
    <Normal>repeat        x( "povt" ) ;
    <Normal>until         x( "do" ) ;
    <Normal>set           x( "mnozhestvo" ) ;
    <Normal>with          x( "s" );
    <Normal>nil           x( "nil" ) ;
    <Normal>if            x( "esli" ) ;
    <Normal>then          x( "to" ) ;
    <Normal>else          x( "inache" ) ;
    <Normal>{FIXED}       x( "float" ) ;
    <Normal>{INT}         x( "c.b.z" ) ;
    <Normal>{IDENT}       x( "ident" ) ;
    <Normal>[ 0]       ;

     %%

    x( s )
    char *s ;
    {


 25










    printf("%-15.15s 177> %s <1770,
                            s, yytext ) ;

    }


4.  Struktura fajla lex.yy.c

     lex stroit programmu - leksicheskij analizator na  yazyke
Si,  kotoraya  razmeshchaetsya  v  fajle  so  standartnym  imenem
lex.yy.c. |ta programma soderzhit dve osnovnyh funkcii i nes-
kol'ko vspomogatel'nyh. Osnovnye - eto:

funkciya yylex()
     Ona soderzhit  razdely  dejstvij  vseh  pravil,  kotorye
     opredeleny pol'zovatelem;

funkciya yylook()
     Ona realizuet determinirovannyj konechnyj avtomat, koto-
     ryj  osushchestvlyaet  razbor  vhodnogo  potoka  simvolov v
     sootvetstvii  s  regulyarnymi  vyrazheniyami  pravil  Lex-
     programmy.

     Vspomogatel'nye funkcii, kotorye  yavlyayutsya  podprogram-
mami vvoda-vyvoda. K nim otnosyatsya:

input()
     chitaet i vozvrashchaet simvol iz vhodnogo potoka simvolov;

unput(c)
     vozvrashchaet simvol obratno vo vhodnoj potok dlya  povtor-
     nogo chteniya;

output(c)
     vyvodit v vyhodnoj potok simvol c.

     |ti funkcii opredeleny kak  makropodstanovki  sleduyushchim
obrazom:

     input -

            fprintf( fout, "%s%d%s0,
      "#define input() (((yytchar=yysptr>yysbuf
            ctable['0],
     "?(yylineno++,yytchar):yytchar)==EOF?0:yyt


     unput -







 26










    #define unput(c){
            yytchar = (c);
            if( yytchar == '\n' ) yylineno--;
            *yysptr++ = yytchar;
    }


     output -

    #define output(c) putc(c,yyout)


     |ti funkcii mozhno izmenit', ukazav im  te  zhe  imena  i
razmestiv v razdele programm pol'zovatelya.

     Pri  sborke  programmy  leksicheskogo  analiza  redaktor
svyazi ld po flagu -ll podklyuchaet golovnuyu funkciyu main, esli
ona ne opredelena. Nizhe priveden tekst etoj funkcii iz  bib-
lioteki /usr/lib/libl.a

    # include "stdio.h"
    main(){
            yylex();
            exit(0);
    }


5.  Funkciya yywrap()

     Funkciya  yywrap  ispol'zuetsya  dlya  opredeleniya   konca
fajla,  iz kotorogo leksicheskij analizator chitaet potok sim-
volov. Esli  yywrap  vozvrashchaet  1,  leksicheskij  analizator
prekrashchaet  rabotu.  Odnako,  inogda  imeetsya  neobhodimost'
nachat' vvod dannyh iz drugogo istochnika i prodolzhit' rabotu.
V etom sluchae pol'zovatel' dolzhen napisat' svoyu podprogrammu
yywrap, kotoraya organizuet novyj vhodnoj potok i  vozvrashchaet
0,  chto sluzhit signalom k prodolzheniyu raboty analizatora. Po
umolchaniyu yywrap vsegda vozvrashchaet 1 pri zavershenii vhodnogo
potoka simvolov.

     V Lex-programme nevozmozhno  zapisat'  pravilo,  kotoroe
budet obnaruzhivat' konec fajla. Edinstvennyj sposob eto sde-
lat' - ispol'zovat' funciyu yywrap. |ta funkciya takzhe udobna,
kogda neobhodimo vypolnit' kakie-libo dejstviya po zaversheniyu
vhodnogo potoka simvolov, opredeliv v razdele programm pol'-
zovatelya novyj variant funkcii yywrap. Primer:









 27










    %START AA BB CC

       /*
        *   Stroitsya leksicheskij analizator,
        *   kotoryj raspoznaet nalichie
        *   vklyuchenij fajlov v Si-programme,
        *   uslovnyh kompilyacij,
        *   makroopredelenij,
        *   metok i golovnoj funkcii main.
        *   Analizator nichego ne vyvodit, poka
        *   osushchestvlyaetsya chtenie vhodnogo
        *   potoka, a po ego zavershenii
        *   vyvodit statistiku.
        */

    BUKVA       =    [A-ZA-YAa-za-ya_]
    CIFRA           [0-9]
    IDENTIFIKATOR   {BUKVA}({BUKVA}|{CIFRA})*

                    int a1,a2,a3,b1,b2,c;
     %%
            {a1 = a2 = a3 = b1 = b2 = c = 0;}

    ^#                          BEGIN AA;
    ^[ \t]*main                 BEGIN BB;
    ^[ \t]*{IDENTIFIKATOR}      BEGIN CC;
    \t                          ;
    \n                          BEGIN 0;

    <AA>define                  { a1++; }
    <AA>include                 { a2++; }
    <AA>ifdef                   { a3++; }

    <BB>[^\,]*","[^\,]*")"     { b1++; }
    <BB>[^\,]*")"               { b2++; }

    <CC>":"/[ \t]               { c++;  }

     %%

    yywrap(){

    if( b1 == 0 && b2 == 0 )
            printf("V programme\
     otsutstvuet  funkciya main.\n");

    if( b1 >= 1 && b2 >= 1 ){
            printf("Mnogokratnoe\
     opredelenie funkcii main.\n");

    } else {
        if(b1 == 1 )
            printf("Funkciya main\


 28










     s argumentami.\n");

        if( b2 == 1 )
            printf("Funkciya main\
     bez argumentov.\n");
    }

    printf("Vklyuchenij fajlov: %d.\n",a2);
    printf("Uslovnyh kompilyacij: %d.\n",a3);
    printf("Opredelenij: %d.\n",a1);
    printf("Metok: %d.\n",c);

    return(1);

    }


     Operator return(1) v funkcii yywrap ukazyvaet, chto lek-
sicheskij analizator dolzhen zavershit' rabotu. Esli neobhodimo
prodolzhit' rabotu analizatora dlya chteniya  dannyh  iz  novogo
fajla,  nuzhno  ukazat'  return(0), predvaritel'no osushchestviv
operacii zakrytiya i otkrytiya fajlov i, v etom sluchae, anali-
zator  prodolzhit chtenie i obrabotku vhodnogo potoka. Odnako,
esli yywrap ne vozvrashchaet 1, to eto privodit k  beskonechnomu
ciklu.

6.  Funkciya REJECT

     Obychno lex  razdelyaet  vhodnoj  potok,  ne  osushchestvlyaya
poisk  vseh  vozmozhnyh  sootvetstvij  kazhdomu vyrazheniyu. |to
oznachaet, chto kazhdyj simvol rassmatrivaetsya  odin  i  tol'ko
odin raz. Predpolozhim, chto my hotim podschitat' vse vhozhdeniya
cepochek she i he vo vhodnom tekste. Dlya etogo  my  mogli  by
zapisat' sleduyushchie pravila:

            she     s++;
            he      h++;
            .       |
            \n      ;

Tak kak she vklyuchaet v sebya he, analizator ne raspoznaet  te
vhozhdeniya he, kotorye vklyucheny v she, tak kak, prochitav odin
raz she, eti simvoly on ne vernet vo vhodnoj potok.

     Inogda zhelatel'no pereopredelit' etot  vybor.  Dejstvie
funkcii  REJECT  oznachaet  "vybrat' sleduyushchuyu al'ternativu".
|to privodit k tomu, chto kakim by  ni  bylo  pravilo,  posle
nego neobhodimo vypolnit' vtoroj vybor. Sootvetstvenno izme-
nitsya i polozhenie ukazatelya vo vhodnom potoke:






 29










            she         { s++; REJECT; }
            he          { h++; REJECT; }
            .           |
            \n          ;

Zdes' posle vypolneniya odnogo pravila  simvoly  vozvrashchayutsya
nazad vo vhodnoj potok, i vypolnyaetsya drugoe pravilo.

     Funkciya REJECT polezna v tom sluchae, kogda ona primenya-
etsya  dlya  opredeleniya  vseh  vhozhdenij kakogo-libo ob®ekta,
prichem  vhozhdeniya  mogut  perekryvat'sya  ili  vklyuchat'  drug
druga.   Predpolozhim,  neobhodimo  poluchit' iz odnogo potoka
tablicu vseh dvuhbukvennyh sochetanij, kotorye obychno  perek-
ryvayutsya,  naprimer,  slovo  the  soderzhit kak th, tak i he.
Dopustim, imeetsya dvumernyj massiv digram, togda:

     %%
    [a-z][a-z]      {

              digram[yytext[0]][yytext[1]]++;
              REJECT;
                    }
    \n              ;

Zdes' REJECT ispol'zuetsya dlya vydeleniya bukvennyh par, nachi-
nayushchihsya na kazhdoj bukve, a ne na kazhdoj sleduyushchej.

7.  Funkcii yyless i yymore

     V obychnoj situacii soderzhimoe yytext obnovlyaetsya vsyakij
raz,  kogda  na vhode poyavlyaetsya sleduyushchaya stroka. Napomnim,
chto v yytext vsegda nahodyatsya simvoly raspoznannoj  posledo-
vatel'nosti. Inogda voznikaet neobhodimost' dobavit' k teku-
shchemu soderzhimomu yytext sleduyushchuyu raspoznannuyu cepochku  sim-
volov.  Dlya  etoj  celi  ispol'zuetsya funkciya yymore. Format
vyzova etoj funkcii:
                          yymore()
V nekotoryh sluchayah voznikaet neobhodimost' ispol'zovat'  ne
vse  simvoly  raspoznannoj  posledovatel'nosti  v  yytext, a
tol'ko neobhodimoe ih  chislo.  Dlya  etoj  celi  ispol'zuetsya
funkciya yyless. Format ee vyzova:
                         yyless(n)
gde n ukazyvaet, chto v dannyj  moment  neobhodimy  tol'ko  n
simvolov  stroki v yytext. Ostal'nye najdennye simvoly budut
vozvrashcheny vo vhodnoj potok.

     Primer ispol'zovaniya funckii yymore:








 30










     .
     .
     .
    \"[^"]*         {
             if( yytext[yyleng - 1] == '\\'){
                            yymore();
             }else{

                 /*
                  *  zdes' dolzhna byt' chast'
                  *  programmy, obrabatyvayushchaya
                  *  zakryvayushchuyu kavychku.
                  */

             }
                    }
     .
     .
     .

V etom primere raspoznayutsya stroki simvolov, vzyatye v  dvoj-
nye  kavychki,  prichem  simvol  dvojnaya  kavychka  vnutri etoj
stroki mozhet izobrazhat'sya  s  predshestvuyushchej  kosoj  chertoj.
Analizator   dolzhen   raspoznavat'  kavychku,  ogranichivayushchuyu
stroku, i kavychku, yavlyayushchuyusya chast'yu stroki, kogda ona izob-
razhena kak \".

     Dopustim, na vhod postupaet stroka "abv\"gde".  Snachala
budet raspoznana cepochka "abv\ i, tak kak poslednim simvolom
v etoj cepochke budet simvol "\", vypolnitsya vyzov  yymore().
V  rezul'tate  k  cepochke  "abv\  budet  dobavleno "gde, i v
yytext my poluchim:  "abv\"gde, chto i trebovalos'.

     Primer ispol'zovaniya funcii yyless:

     .
     .
     .
    =-[A-ZA-YAa-za-ya]       {

      printf("Operator (=-) dvusmyslennyj.\n");
      yyless(yyleng - 2);

     /*
      * zdes' neobhodimo ukazat'
      * dejstviya dlya sluchaya "=-"
      */
                            }
     .
     .
     .

V etom primere  razreshaetsya  dvusmyslennost'  vyrazheniya  "=-


 31










bukva" v yazyke Si.  |to vyrazhenie mozhno rassmatrivat' kak

            "=- bukva" (ravnosil'no "-=" )
    ili
            "= -bukva"

Predpolozhim, chto zhelatel'no etu situaciyu  rassmatrivat'  kak
"=  -bukva"  i  vyvodit' perduprezhdenie. Ukazannoe v primere
pravilo raspoznaet etu situaciyu  i  vyvodit  preduprezhdenie.
Zatem,  v  rezul'tate vyzova "yyless(yyleng - 2);"  dva sim-
vola "-bukva" budut vozvrashcheny vo vhodnoj potok, a znak  "="
ostanetsya v yytext dlya obrabotki, kak v normal'noj situacii.
Takim obrazom, pri prodolzhenii chteniya  vhodnogo  potoka  uzhe
budet obrabatyvat'sya cepochka "-bukva", chto i trebovalos'.

8.  Sovmestnoe ispol'zovanie lex i yacc

     yacc trebuet ukazanie  leksicheskomu  analizatoru  imeni
yylex(). Imenno poetomu eta funkciya tak nazyvaetsya v lex.

     Izvestno, chto  yacc  stroit  vyhodnoj  fajl  y.tab.c  .
Osnovnoj v fajle y.tab.c yavlyaetsya funkciya yyparse, realizuyu-
shchaya algoritm grammaticheskogo razbora. Funkciya yyparse soder-
zhit  mnogokratnoe  obrashchenie  k funkcii leksicheskogo analiza
yylex.

     Dlya obespecheniya korrektnoj raboty grammaticheskogo  ana-
lizatora  funkciya yylex dolzhna byt' soglasovana s konkretnoj
specifikaciej grammatiki i udovletvoryat' opredelennym trebo-
vaniyam.

     Pol'zovatel'  pri  opisanii  grammatiki  reshaet,  kakie
konstrukcii celesoobraznee neposredstvenno vydelyat' iz vhod-
nogo teksta na etape leksicheskogo analiza.

     Slozhnost' leksicheskogo  analizatora  zavisit  ot  togo,
kakie strukturnye edinicy vzyaty za osnovu pri opisanii gram-
maticheskih pravil.  Detalizovav grammatiku do otdel'nyh sim-
volov,  mozhno  obojtis' prostejshim leksicheskim analizatorom.
Odnako, v etom sluchae chislo pravil rastet, a  grammaticheskij
razbor  okazyvaetsya  menee effektivnym. Poetomu pol'zovatel'
obychno dolzhen najti nekotoryj kompromiss pri  vybore  nabora
leksem.

     Zametim, chto klyuchevye slova opisyvaemogo vhodnogo yazyka
chasto  byvaet  udobno  schitat' leksemami. Imena leksem mogut
sovpadat' s etimi klyuchevymi slovami,  nedopustimym  yavlyaetsya
lish'  sovpadenie  imen  leksem  s zarezervirovannymi slovami
yazyka Si.

     Osnovnaya zadacha funkcii yylex sostoit vo vvode iz vhod-
nogo  potoka  ryada ocherednyh simvolov do vyyavleniya konstruk-
cii, sootvetstvuyushchej odnoj iz leksem, i  vozvrashchenii  nomera


 32










tipa  etoj  leksemy  i,  kogda eto neobhodimo, znacheniya etoj
leksemy.

     Vse vidy leksem, krome literalov, oboznachayutsya  nekoto-
rymi   imenami  i  pod  etimi  imenami  figuriruyut  v  Yacc-
programme, gde ob®yavlenie imen leksem osushchestvlyaetsya  direk-
tivoj token:

            %token <spisok imen leksem>

Blagodarya ob®yavleniyu imen leksem  v  direktive  token,  yacc
otlichaet imena leksem ot imen neterminal'nyh simvolov.

     Primer ob®yavleniya imen leksem v Yacc-programme:

        %token IDENT CONST ZNAK IF THEN GOTO

Pri pervom poyavlenii leksemy ili literala v sekcii  ob®yavle-
nij  Yacc-programmy za kazhdym iz nih mozhet sledovat' neotri-
catel'noe celoe chislo, rassmatrivaemoe kak  nomer_tipa  lek-
semy.

     Po umolchaniyu nomera tipov vseh leksem opredelyayutsya yacc
sleduyushchim obrazom:

     - dlya literala nomerom tipa leksemy schitaetsya  chislovoe
       znachenie  dannogo literal'nogo simvola, rassmatrivae-
       mogo kak odnobajtovoe celoe chislo;

     - leksemy, oboznachennye imenami, v sootvetstvii s  oche-
       rednost'yu  ih  ob®yavleniya  poluchayut  posledovatel'nye
       nomera, nachinaya s 257.

     Dlya kazhdogo imeni leksemy nezavisimo ot togo, pereopre-
delen  li ee nomer pol'zovatelem, yacc generiruet v vyhodnom
fajle y.tab.c operator preprocessora:

          #define <imya_leksemy> <nomer_tipa>

Znachenie, vozvrashchaemoe funkciej yylex, yavlyaetsya nomerom tipa
leksemy. Takim obrazom, spisok leksem i nomera ih tipov uka-
zyvayutsya v Yacc-programme, a opredeleniya etih leksem v  Lex-
programme.  Voznikaet  problema  sootvetstviya  nomerov tipov
leksem v fajlah y.tab.c i lex.yy.c, kotoroya razreshaetsya sle-
duyushchim obrazom:

     - pri vyzove yacc s flagom -d posledovatel'nost' opera-
       torov #define pomeshchaetsya v fajl y.tab.h.;

     - etot fajl posredstvom operatora #include vklyuchaetsya v
       Lex-programmu.




 33










     V procedure leksicheskogo analiza krome vydeleniya leksem
mozhno  predusmotret' nekotoruyu obrabotku leksem opredelennyh
tipov, v chastnosti, zapominanie konkretnyh znachenij leksem.

     Primerom znacheniya leksemy mogut sluzhit' chislovoe znache-
nie  simvola  - cifry, vychislennoe znachenie konstanty, adres
identifikatora v tablice imen (postroenie tablicy imen  osu-
shchestvlyaet  lex).   Krome togo, eti znacheniya obychno trebuetsya
peredat' grammaticheskomu analizatoru. S  etoj  cel'yu  nuzhnoe
znachenie  dolzhno  byt'  prisvoeno  vneshnej peremennoj celogo
tipa s imenem yylval. Esli funkciya yylex nahoditsya v otdel'-
nom fajle, to eta peremennaya dolzhna byt' ob®yavlena:

            extern int yylval;

Utochnim,  chto  znacheniem_leksemy  budem  nazyvat'  znachenie,
prisvoennoe pri ee raspoznavanii peremennoj yylval. Zametim,
chto v yylval  vsegda  dolzhno  nahoditsya  znachenie  poslednej
vydelennoj leksemy.

     Dopustim,  my  raspolagaem  Yacc-programmoj   v   fajle
source.y  i Lex-programmoj v fajle source.l, kotorye neobho-
dimo sobrat' v rabotayushchuyu programmu. Sushchestvuet dva  sposoba
sborki:

     - sborka  Lex-  i  Yacc-programmy  s  sozdaniem   fajla
       y.tab.h;

     - sborka  Lex-  i  Yacc-programmy  bez  sozdaniya  fajla
       y.tab.h.

     Rassmotrim pervyj sposob sborki.

     Nizhe priveden primer makefile dlya programmy make, koto-
raya  osushchestvlyaet  posledovatel'nuyu  obrabotku  i sborku eih
programm i razmeshchaet rezul'tat v fajle program:



















 34










    program:        y.tab.o lex.yy.o
          cc y.tab.o lex.yy.o -ly -ll -o program

    y.tab.o:        y.tab.c
                    cc -c -O y.tab.c

    lex.yy.o:       lex.yy.c  y.tab.h
                    cc -c -O lex.yy.c

    y.tab.h:
    y.tab.c:        source.y
                    yacc -d source.y

    lex.yy.c:       source.l
                    lex -v source.l

    clear:
           rm -f yy.tab.?  lex.yy.? program

V  fajle  source.l  razmeshchena  Yacc-programma,   realizuyushchaya
nebol'shoj   nastol'nyj  kal'kulyator.  Kal'kulyator  imeet  52
registra, pomechennyh bukvami ot A do z, i razreshaet  ispol'-
zovat'  arifmeticheskie  vyrazheniya, soderzhashchie operacii +, -,
*, /, % (ostatok ot deleniya), & (pobitovoe i), |  (pobitovoe
ili) i prisvaivanie. Kak i v Si, celye chisla, nachinayushchiesya s
0, schitayutsya vos'merichnymi,  vse  ostal'nye  -  desyatichnymi.
Rezul'tat vsegda vyvoditsya desyatichnymi chislami.

     Kal'kulyator rabotaet v interaktivnom rezhime s  postroch-
nym  formirovaniem  vyhoda,  mozhet chitat' zadanie iz fajla i
vyvodit' rezul'tat v fajl.

     Znak "=" ispol'zuetsya dlya prisvaivaniya, a dlya vyvedeniya
rezul'tata dostatochno nazhat' klavishu <VK>. Raspoznayutsya sko-
bochnye struktury, izmenyayushchie poryadok prioritetov pri  vychis-
leniyah. Kal'kulyator rabotaet tol'ko s celymi tipa integer.



















 35










    %token DIGIT LETTER
    %left '|'
    %left '&'
    %left '+' '-'
    %left '*' '/' '%'
    %left UMINUS
    %{
    int base, regs[26];
    %}
     %%
    list:
        |
         list stat '\n'
        |list stat error '\n' { yyerrok; }

    stat:
         expr { printf( "%d\n",$1 ); }
        |LETTER '=' expr { regs[$1]=$3; }

    expr:
         '(' expr ')'   { $$=$2;  }
        |expr '+' expr  { $$=$1+$3; }
        |expr '-' expr  { $$=$1-$3; }
        |expr '*' expr  { $$=$1*$3; }
        |expr '/' expr  { $$=$1/$3; }
        |expr '%' expr  { $$=$1%$3; }
        |expr '&' expr  { $$=$1&$3; }
        |expr '|' expr  { $$=$1|$3; }
        |'-' expr %prec UMINUS  { $$= -$2; }
        | LETTER  { $$=regs[$1]; }
        | number;

    number:
         DIGIT  { $$=$1;
                  base=10;
                  if($1==0) base=8;  }
        |number DIGIT  { $$=base*$1+$2; }


     V fajle source.l  razmeshchena  Lex-programma  leksichskogo
analizatora dlya etogo kal'kulyatora:














 36










    %{
    #include   "y.tab.h"
    extern int yylval;
    %}
     %%
    ^\n             ;
    [ \t]*          ;
    [A-Za-z]     {
           yylval = yytext[yyleng-1] - 'a';
           return(LETTER);}
    [0-9]              {
           yylval = yytext[yyleng-1] - '0';
           return(DIGIT);}


     Rassmotrim  vtoroj  sposob  sborki.   Makefile   teper'
sushchestvenno proshche:

    program:       y.tab.c lex.yy.c
          cc -O y.tab.c -ly -ll -o program

    y.tab.c:        source.y
                    yacc  source.y

    lex.yy.c:       source.l
                    lex -v source.l
    clear:
            rm -f y.tab.? lex.yy.? program


No v fajlah source.y i source.l proizojdut sleduyushchie izmene-
niya. V razdele vhodnoj informacii dlya Yacc-programmy neobho-
dimo ukazat' stroku #include lex.yy.c,  a  iz  Lex-programmy
neobhodimo  ubrat'  stroku  #include  "y.tab.h". Teper' fajl
source.y vyglyadit sleduyushchim obrazom:




















 37










    %token DIGIT LETTER
    %left '|'
    %left '&'
    %left '+' '-'
    %left '*' '/' '%'
    %left UMINUS
    %{
    #include "lex.yy.c"
    int base, regs[26];
    %}
     %%
    list:
        |
         list stat '\n'
        |list stat error '\n' { yyerrok; }

    stat:
         expr { printf( "%d\n",$1 ); }
        |LETTER '=' expr { regs[$1]=$3; }

    expr:
         '(' expr ')'   { $$=$2;  }
        |expr '+' expr  { $$=$1+$3; }
        |expr '-' expr  { $$=$1-$3; }
        |expr '*' expr  { $$=$1*$3; }
        |expr '/' expr  { $$=$1/$3; }
        |expr '%' expr  { $$=$1%$3; }
        |expr '&' expr  { $$=$1&$3; }
        |expr '|' expr  { $$=$1|$3; }
        |'-' expr %prec UMINUS  { $$= -$2; }
        | LETTER  { $$=regs[$1]; }
        | number;

    number:
         DIGIT  { $$=$1;
                  base=10;
                  if($1==0) base=8;  }
        |number DIGIT  { $$=base*$1+$2; }


A fajl source.l vyglyadit sleduyushchim obrazom:














 38










    %{
    extern int yylval;
    %}
     %%
    ^\n             ;
    [ \t]*          ;
    [A-Za-z]     {
           yylval = yytext[yyleng-1] - 'a';
           return(LETTER);}
    [0-9]              {
           yylval = yytext[yyleng-1] - '0';
           return(DIGIT);}


9.  Ispol'zovanie Ratfora

     lex mozhno ispol'zovat' dlya generacii programm leksiches-
kogo  analiza na Ratfore.  Dlya etogo v pervoj stroke razdela
opredelenij neobhodimo ukazat' %R.  Vse  skazannoe  vyshe  ob
ispol'zovanii  Si  v  kachestve host-yazyka otnositsya i k Rat-
foru.  Neobhodimo uchest', chto Ratfor imeet  svoyu  biblioteku
vvoda-vyvoda. Odnako, sostav funkcij lex dlya Ratfora tot zhe,
chto i dlya Si. Est' i funkciya, vydelennaya tol'ko dlya  Ratfora
- lexshf.  Funkciya lexshf perevodit vnutrennee predstavlenie
simvola (mladshij bajt) iz  Si  vo  vnutrennee  predstavlenie
simvola v Fortrane (starshij bajt).

     Destviya pravil Lex-programmy dlya Ratfora oformlyayutsya  v
vide  vychislyaemyh  goto v vyhodnom fajle, kotoryj nazyvaetsya
lex.yy.r.

     Dopustim, imeetsya ishodnyj fajl source.l s  Ratforom  v
kachestve host-yazyka, togda dlya polucheniya leksicheskogo anali-
zatora neobhodimy sleduyushchie dejstviya:

            % lex source.l
            % rc lex.yy.r -llr

Napomnim, chto v Ratfore indeksy  massivov  nachinayutsya  s  1,
poetomu,  naprimer, yytex[yyleng] - eto poslednij poluchennyj
iz vhodnogo potoka simvol.

10.  Flagi Lex

-t   pomestit' rezul'tat v standartnyj fajl vyvoda, a  ne  v
     fajl lex.yy.c;

-v   vyvesti razmery vnutrennih tablic;

-f   uskorit' rabotu,  ne  upakovyvaya  tablicy  (tol'ko  dlya
     nebol'shih programm);




 39










-n   ne vyvodit' razmery tablic (ustanavlivaetsya po  umolcha-
     niyu);

-d   ispol'zuetsya pri otladke lex.

     Imeetsya vozmozhnost' sobrat' analizator dlya diagnostiki.
Dlya etogo neobhodimo kompilyaciyu fajla lex.yy.c  osushchestvlyat'
s podklyucheniem razdelov diagnostiki:

            cc -d -DLEXDEBUG  lex.yy.c

Pri rabote poluchennogo takim obrazom analizatora budet vyvo-
dit'sya  diagnostika dejstvij. Flag -d, krome togo, pozvolyaet
proverit' tekst  programmy  lex.yy.c  s  pomoshch'yu  tekstovogo
otladchika cdeb.








































 40










                         SODERZHANIE



.   Annotaciya .........................................    2

1.  Vvedenie ..........................................    3

2.  Regulyarnye vyrazheniya v Lex-pravilah ...............    8
2.1.  Oboznacheniya simvolov v vyrazheniyah ...............    8
2.2.  Operatory regulyarnyh vyrazhenij ..................    9
2.3.  Operator vydeleniya klassov simvolov .............    9
2.4.  Povtoriteli .....................................    9
2.5.  Operatory vybora ................................   10
2.6.  Operator {} .....................................   11
2.7.  Operator <>. Sluzhebnye slova START i BEGIN ......   12

3.  Struktura Lex-programmy ...........................   15
3.1.  Razdel opredelenij Lex-programmy ................   15
3.2.  Razdel pravil ...................................   18
3.2.1.  Dejstviya v pravilah Lex-programmy .............   19
3.2.2.  Poryadok dejstviya aktivnyh pravil ..............   21
3.3.  Razdel programm pol'zovatelya ....................   22
3.4.  Kommentarii Lex-programmy .......................   22
3.5.  Primery Lex-programm ............................   22

4.  Struktura fajla lex.yy.c ..........................   26

5.  Funkciya yywrap() ..................................   27

6.  Funkciya REJECT ....................................   29

7.  Funkcii yyless i yymore ...........................   30

8.  Sovmestnoe ispol'zovanie lex i yacc ...............   32

9.  Ispol'zovanie Ratfora .............................   39

10. Flagi Lex .........................................   39
















 41




Last-modified: Mon, 29 Jun 1998 14:01:55 GMT
Ocenite etot tekst: