l eksinė analizė ( sk e navimas)
DESCRIPTION
L eksinė analizė ( sk e navimas). DO 10 I = 1 ,5 DO 10 I = 1 .5. Pirmasis teksto supratimo žingsnis – atpažinti žodžius Vienu i š pagrindini ų leksinio analizatoriaus užduočių, yra išskirti programos tekste tokenus - PowerPoint PPT PresentationTRANSCRIPT
1
Leksinė analizė (skenavimas)
• Pirmasis teksto supratimožingsnis – atpažinti žodžius
• Vienu iš pagrindinių leksinio analizatoriaus užduočių, yra išskirti programos tekste tokenus– tokenas yra leksemų rūšis (tokenas gali
būti simboliu, operatoriumi, identifikatoriumi ar raktiniu žodžiu), tai simbolinė konstanta.
– komentarai ir tušti tarpai atmetami.
Leksinisanalizatorius
Programos tekstas
Tokenų srautas
DO 10 I = 1,5DO 10 I = 1.5
2
Leksinė analizė (skenavimas)PavyzdysPavyzdys
TokenasIDENTIFIERASSIGN_OPIDENTIFIERSUBTRACT_OPIDENTIFIERDIVISION_OPINIT_LITERALSEMICOLON
Leksemasum=oldsum-value/100;
sum = oldsum – value / 100;
3
Kaip aprašyti tokenus?
• Šablonas – tai taisyklė aprašanti tokeną atitinkančias leksemas.– Šioms taisyklėms aprašyti yra sukurtas
specialus žymėjimas – reguliarios išraiškos.– Programavimo kalbos atominiai vienetai –
tokenai yra aprašomi reguliariomis išraiškomis• Tarp tokenų reikia mokėti išskirti rezervuotus
žodžius, jei kalboje nėra rezervuotų žodžių, leksinė analizė tampa labai sudėtinga– Pavyzdžiui PL/I kalba:
IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
– FORTRAN kalba.
4
Kalba (apibrėžimai)
• Terminas alfabetas žymi bet kokią baigtinę simbolių aibę.– Aibė {0, 1} yra binarinis alfabetas– ASCII, EBCDIC yra kompiuteriniai alfabetai
• Alfabeto eilutė, tai baigtinė alfabeto simbolių seka.
• Kalba, tai bet kokia alfabeto eilučių aibė.– tuščia aibė irgi kalba!– tuščių eilučių aibė {ε} irgi kalba!– visi lietuvių kalbos sakiniai irgi sudaro kalbą.
5
Operacijos su kalbomis
• Kalbų L ir M junginys LUM={s|sєL arba sєM}• Kalbų L ir M konkatenacija LM={st|sєL ir tєM}
• Kalbos L Kleene uždarinys L*=Ui=0∞Li
– tai nulis arba daugiau konkatenacijų
• Kalbos L teigiamas uždarinys L+=Ui=1∞Li
– tai viena arba daugiau konkatenacijų – čia L0={ε}, Li=Li-1L.
6
Kalbų kūrimo pavyzdžiai
• Tegu L={A,B,...,Z,a,b,...,z} ir M={0,1,2,...,9}– Kadangi simbolį galima laikyti vienetinio ilgio
eilute, aibės L ir M yra baigtinės kalbos.1. LUM yra raidžių ir skaičių aibė2. LM yra aibė eilučių sudarytų iš raidės po kuria
seka skaičius3. L4 tai visų keturraidžių eilučių aibė4. L* aibė eilučių iš visų raidžių, tame tarpe ir
tuščia eilutė ε5. L(LUM)* aibė eilučių iš raidžių ir skaičių
prasidedanti raide6. M+ aibė eilučių sudarytų iš vieno ir daugiau
simbolių
7
Reguliarios išraiškos
• Daugumos programavimo kalbų leksinė struktūra gali būti apibrėžta reguliariomis išraiškomis.
• Reguliarios išraiškos yra apibrėžiamos virš tam tikro (nustatyto) alfabeto Σ– Dauguma programavimo kalbų alfabetu naudoja
ASCII arba Unicode• Jei re yra reguliari išraiška, tuomet L(re ) yra re
sugeneruota kalba (simbolinių eilučių rinkinys)
8
Reguliarios išraiškos
• Reguliari išraiška apibrėžiama tam tikromis taisyklėmis.1. tuščia eilutė ε yra reguliari išraiška2. simbolis, pavyzdžiui a yra reguliari išraiška3. Jei R ir S yra reguliarios išraiškos, tai reguliaria
išraiška bus ir1. R|S (reiškia R arba S) 2. RS (konkatenacija) 3. R* (nulis arba daug R tarpusavio konkatenacijų) 4. (R) (reguliarias išraiškas galima grupuoti)
• Reguliarias išraiškas galima įvardinti: vardas → r
9
Kalbų generavimo taisyklės
re L(re ) Pastabos
RS L(R)L(S)konkatenacij
a
R|S L(R)UL(S) junginys
R* L(R)*Kleene
uždarinys
10
Pavyzdžiai (1)
• Reguliari išraiška a|b reiškia aibę {a,b}
• Reguliari išraiška (a|b)(a|b) reiškia aibę {aa,ab,ba,bb} – aibę visų eilučių iš a ir b, kurių ilgis 2.
– Kita šiai aibei atitinkanti reguliari išraiška aa|ab|ba|bb
• Reguliari išraiška a* reiškia aibę {ε,a,aa,aaa,...}
• Reguliari išraiška (a|b)* reiškia aibę visų galimų eilučių iš a ir b: {ε,a,b,aa,ab,bb,...}
– Kita šiai aibei atitinkanti reguliari išraiška (a*b*)*
11
Pavyzdžiai (2)
• Reguliari išraiška a|a*b reiškia aibę turinčią a ir eilutes turinčias nulį ar daugiau a ir besibaigiančias b
• Reguliari išraiška ba* reiškia aibę {b,ba,baa,baaa,...}
• Reguliari išraiška a*|b reiškia aibę {b,ε,a,aa,aaa,...}
• (0|1)*1 reiškia aibę binarinių skaičių besibaigiančių 1
12
Algebrinės reguliarių išraiškų savybės
• r|s=s|r – operatorius | komutatyvus• r|(s|t)=(r|s)|t – operatorius | asociatyvus• (rs)t=r(st) – konkatenacija asociatyvi• r(s|t)=rs|rt, (s|t)r=sr|tr – konkatenacija
distributyvi operatoriaus | atžvilgiu• εr=r, rε=r – konkatenacijos atžvilgiu, ε yra
vienetinis elementas • r*=(r|ε)* - ryšys tarp * ir ε• r**=r*
13
Pavyzdžiai
• Paskalio identifikatorių apibrėžianti reguliari išraiškaletter → A|B|...|Z|a|b|...|zdigit → 0|1|2|3|4|5|6|7|8|9id → letter(letter|digit)*
• Skaičius (pvz.: 1.89E-4) apibrėžianti reguliari išraiškadigit → 0|1|...|9digits → digit digit*
optional_fraction → .digits|εoptional_exponent → (E(+|-|ε)digits)|εnum → digits optional_fraction
optional_exponent
14
Sutrumpinimai
• R+ (vienas ar daugiau R)– a+ aprašo visas eilutes sudarytas iš vieno ar
daugiau a simbolių a, aa, aaa, ...– r* = r+|ε, r+ = rr*
• R? (nulis arba vienas R)– r? = r|ε
• [a-z], [A-Z], [0-9] (sutrumpintas simbolių klasės žymėjimas)– [abc] = a|b|c– [a-z] = a|b|...|z– Paskalio identifikatorius [A-Za-z] [A-Za-z0-9]*
15
Pavyzdys
• Skaičius (pvz.: 1.89E-4) apibrėžianti reguliari išraiška, naudojanti sutrumpinimusdigit → [0-9]digits → digit+optional_fraction → (.digits)?optional_exponent → (E(+|-)?digits)?num → digits optional_fraction
optional_exponent
16
Prioritetai
1. Operatoriai *, + ir ? turi aukščiausią prioritetą.
2. Konkatenacijos prioritetas žemesnis.3. | turi žemiausią prioritetą.• Visi operatoriai yra asociatyvūs iš kairės.
• Pavyzdžiui, šių susitarimų dėka išraiška (a)|
((b)*(c)) yra ekvivalenti a|b*c – Tai aibė eilučių kurias sudaro arba
vienintelis a, arba nulis arba keletas b, po kurių seka vienintelis c.
17
Leksinių analizatorių istorija
• LEX– Leksinis analizatorius sukurtas Lesk ir Schmidt iš
Bell Labs 1975 UNIX operacinei sistemai – Šiuo metu egzistuoja daugumoje operacinių
sistemų– LEX generuoja leksinį analizatorių - C kalba
parašytą programą – LEX nurodytoms reguliarioms išraiškoms įgalina
atlikti nurodytas veikas• JLex
– sukurtas Elliot Berk iš Princeton University 1996– tai Lex generuojantis leksinį analizatorių - Java
kalba parašytą programą– JLex pats parašytas Java kalba
18
JLexveikla
Tokenų apibrėžimas
Reguliarios Išraiškos
JLex
Java Failas: Scanner Class
(Yylex, leksinę analizę atlieka metodas yylex())
Tokenų atpažinimasRegular expression NFA DFA lexer
19
JLex aprašymo failo struktūra
vartotojo kodas (user code)%%JLex direktyvos (JLex directives)%%reguliarių išraiškų taisyklės (regular expression
rules)
• Komentarai – prasideda // – arba keletui eilučių /* */ galimi tik pirmose
dvejose dalyse
20
vartotojo kodas
• JLex suteikia vartotojui galimybę, esant reikalui, panaudoti savo parašytą programinį kodą.
• Vartotojo kodas bus be pakeitimų įrašytas į JLex išvesties failą, pačioje jo pradžioje.
• Dažniausiai tai būna:– paketų deklaracijos– import deklaracijos– Papildomos, vartotojo parašytos klasės
21
JLex direktyvos
• Šiame skyriuje pateikiami – makrosų apibrėžimai (macro definitions) –
reguliarių išraiškų sutrumpinti pavadinimai• naudojami apibrėžti kas yra raidės,
skaičiai ir tušti tarpai.– būsenų deklaracijos– analizatoriaus savybių pasirinkimai– standartinės leksinio analizatoriaus klasės
papildymai• Kiekviena JLex direktyva turi būti užrašoma
atskiroje eilutėje ir turi pradėti tą eilutę.
22
Reguliarių išraiškų taisyklės
• Šį skyrių sudaro taisyklių rinkinys nurodantis kaip suskaidyti įvesties srautą į tokenus.
• Reguliarių išraiškų taisyklės yra sudarytos iš trijų dalių:– būsenų sąrašas (nebūtinas)– reguliari išraiška– susieta veika (Java kodo fragmentai)
• Kiekviena veika turi grąžinti reikšmę apibrėžtą %type deklaracijoje esančioje antrojoje JLex aprašo dalyje.
• Jei veika nieko negrąžina, einamasis tokenas atmetamas ir leksinis analizatorius dar kartą iškviečia pats save.
[<būsenos>] <reg. išraiška> { <veika>}
23
JLex direktyvos (1)• Direktyva %{...%} leidžia vartotojui įrašyti Java
kodą tiesiai į leksinio analizatoriaus klasę.• Direktyvos naudojimo pavyzdys:%{ <kodas> %} • JLex įrašys Java kodą į sukuriamą leksinio
analizatoriaus klasę:class Yylex { ... <kodas> ... } • Tai leidžia aprašyti papildomus vidinius leksinio
analizatoriaus klasės kintamuosius ir metodus.• Pažymėtina, kad negalima naudoti kintamųjų vardų
prasidedančių yy – tokius vardus naudoja pati leksinio analizatoriaus klasė.
24
JLex direktyvos (2)
• Direktyva %init{ ... %init} leidžia vartotojui įrašyti Java kodą tiesiai į leksinio analizatoriaus klasės konstruktorių
%init{ <kodas>%init} • JLex įrašys Java kodą į sukuriamą leksinio
analizatoriaus klasės konstruktorių:class Yylex { Yylex () { ... <kodas> ... } • Ši direktyva leidžia atlikti papildomą leksinio
analizatoriaus klasės inicializaciją.
25
JLex direktyvos (3)
• Makrosų paskirtis:– vieną kartą apibrėžus reguliarią išraišką,
toliau atitinkamose vietose galima naudoti tik jos vardą (daugelį kartų).
– Praktiškai būtini didesnėms reguliarioms išraiškoms.
• Makrosų apibrėžimo formatas:<vardas> = <apibrėžimas>
– Makroso vardas yra identifikatorius, t.y. gali būti sudarytas iš raidžių, skaitmenų ir apatinių brūkšnių, bei turi prasidėti raide arba apatiniu brūkšniu.
– Makroso apibrėžimas turi būti teisingai užrašyta reguliari išraiška.
• Makrosų apibrėžimuose gali būti panaudoti kiti makrosai - {<vardas>}
26
JLex direktyvos (4)
• Leksinių būsenų pagalba kontroliuojamas reguliarių išraiškų atitikimas.
• Leksinių būsenų deklaravimo formatas%state state[0][, state[1], state[2], ...]• Leksinės būsenos vardas turi būti
identifikatorius. • Pagal nutylėjimą JLex pats deklaruoja vieną
būseną - YYINITIAL, kuria sukurtas leksinis analizatorius pradeda leksinę analizę.
• Jei būsenų sąrašas nenurodytas, reguliarios išraiškos atitikimas neribojamas.
• Jei būsenų sąrašas nurodytas, reguliariai išraiškai bus leidžiama atitikti tik tuomet, jei leksinis analizatorius bus vienoje iš nurodytų būsenų.
• Būsenų vardai turi būti unikalūs – tam tikslui patartina juos pradėti didžiaja raide.
27
JLex direktyvos (5)
• JavaCUP – sintaksinio analizatoriaus generatorius Java kalbai buvo sukurtas Scott Hudson iš Georgia Tech universiteto, ir išvystytas Frank Flannery, Dan Wang, ir C. Scott Ananian pastangomis. – Smulkiau apie šį įrankį:
http://www.cs.princeton.edu/~appel/modern/java/CUP/
• Suderinamumas su JavaCUP aktyvuojamas sekančia JLex direktyva
%cup
28
Simbolių ir eilučių skaičiavimo direktyvos
• Kartais naudinga žinoti kur tekste yra tokenas. Tokeno padėtis nusakoma jo eilutės ir jo pirmojo simbolio eilės numeriu tekste.
• Simbolių skaičiavimas aktyvuojamas direktyva “%char”– Sukuriamas kintamasis yychar (leks.
analizatoriaus);– jo reikšmė – pirmojo atitikusioje šabloną leksemoje
simbolio eilės numeris.• Eilučių skaičiavimas aktyvuojamas direktyva “%line”
– Sukuriamas kintamasis yyline;– jo reikšmė – atitikusios šabloną simbolinės eilutės
pirmosios eilutės eilės numeris.• Pavyzdys: “int” { return (new
Yytoken(4,yytext(),yyline,yychar,yychar+3)); }
29
Reguliarių išraiškų taisyklės
• Jei nuskaitymo metu simbolinei eilutei atitinka keletas taisyklių, jos skaidymas į tokenus vyksta pagal tą taisyklę, kuri atitinka ilgiausią tokeną.
• Jei keletas taisyklių atitinka to pačio ilgio simbolinę eilutę, pasirenkama ta taisyklė, kuri JLex aprašyme yra pirmoji (ankstesnės taisyklės turi aukštesnį prioritetą).
• JLex aprašyme reikia numatyti reguliarias taisykles visiems galimiems atvejams. – Jei leksinio analizatoriaus įvestis neatitiks jokiai
aprašytai taisyklei, leksinis analizatorius nutrauks darbą.
– Galima apsidrausti, JLex aprašymo pabaigoje patalpinus sekančią taisyklę:
. { java.lang.System.out.println("Unmatched input: " + yytext()); }– Čia taškas (.) nurodo atitikimą bet kokiai įvesčiai,
išskyrus naują eilutę (newline).
30
Reguliarios išraiškos (1)
• JLex alfabetas yra ASCII simboliai nuo 0 iki 127 imtinai.– Šie simboliai yra reguliarios išraiškos patys
sau.• Reguliariose išraiškose negalima tiesiogiai
naudoti tarpų - tarpas laikomas reguliarios išraiškos pabaiga. – Jei tuščias tarpas reikalingas reguliarioje
išraiškoje, jį reikia nurodyti tarp dvigubų kabučių: " "
• Sekantys simboliai yra meta simboliai, turintys JLex reguliariose išraiškose specialią reikšmę:
? * + | ( ) ^ $ . [ ] { } " \
31
Reguliarios išraiškos (2)
• ef viena po kitos einančios reguliarios išraiškos reiškia jų konkatenaciją.
• e|f vertikalus brūkšnys | nurodo kad atitikti gali arba išraiška e arba f.
• \b reiškia Backspace • \n reiškia newline • \t reiškia Tab • \f reiškia Formfeed • \r reiškia Carriage return • \^C reiškia Control character • \c - bet koks simbolis po \ reiškia save patį.
32
Reguliarios išraiškos (3)
• $ žymi eilutės pabaigą. – Jei reguliari išraiška baigiasi $ jos atitikimas
tikrinamas tik eilutės pabaigoje (t.y. iš kito galo).• . atitinka bet kokį simbolį išskyrus naują eilutę.
– Taigi, ši išraiška ekvivalenti [^\n]. • "..." metasimboliai tampa paprastais simboliais
dvigubose kabutėse. – Pažymėsime, kad \" reiškia simbolį "
• {vardas} nurodo čia bus išskleistas makrosas su nurodytu vardu.
• * pažymi Kleene uždarinį nurodantį nulį ar daugiau reguliarios išraiškos pasikartojimų.
• + nurodo vieną ar daugiau reguliarios išraiškos pasikartojimų.Taigi e+ yra ekvivalenti ee*
• ? nurodo, kad reguliari išraiška gali būti taikoma arba ne.
33
Reguliarios išraiškos (4)
• (...) skliaustai naudojami reguliarių išraiškų grupavimui.
• [...] pažymi simbolių klasę - reguliari išraiška atitinka visiems klasės simboliams.– Jei pirmasis simbolis [...] yra (^), tai nurodo
kad reguliari išraiška atitinka visiems simboliams išskyrus nurodytus skliaustuose.
• Pavyzdžiui, – [a-z] atitinka visas mažasias raides, – [^0-9] atitinka viską išskyrus skaičius, – [\-\\] atitinka -\, – ["A-Z"] atitinka tris simbolius: A-Z, – [+-] ir [-+] atitinka + ir -.
34
Pavyzdžiai
• “a|b” atitinka a|b bet ne a arba b• ^main atitiks leksemą “main” tik tuomet kai
ji bus eilutės pradžioje.• main$ atitiks leksemą “main” tik tuomet kai
ji bus eilutės pabaigoje.• [a bc] yra ekvivalentus a|" "|b|c
35
Susieta veika (action)
• Ši veika atliekama kai nurodytas šablonas yra atpažystamas
• Veika, tai Java sakiniai (standartiniai) grąžinantys tokenus.
36
Leksinis analizatorius
• JLex taisyklingai parašytą aprašą transformuoja į java programą vardu Yylex.
• Ši klasė turi du konstruktorius turinčius vieną argumentą - įvesties srautą (input stream) kuri reikia išskaidyti į tokenus. – Įvesties srautas gali būti atitinkamai arba
java.io.InputStream, arba java.io.Reader (pavyzdžiui StringReader).
– Konstruktorius java.io.Reader naudojamas jei įvesties sraute gali būti unicode simbolių.
• Sekantį tokeną iš įvesties srauto grąžina leksinio analizatoriaus metodas Yylex.yylex(). – Grąžinamos reikšmės tipas yra Yytoken.
37
Specialūs JLex kintamieji/metodai
• yytext() – grąžina simbolinę eilutę kuriai leksinis analizatorius rado atitikmenį (galima nustatyti šios simbolinės eilutės semantinę reikšmę)– t.y. tai tokeno, kurį grąžina yylex() leksema
• yylength() – grąžina atitiktos simbolinės eilutės ilgį
• yychar – saugo atitiktos eilutės (matched string) pradžios padėtį faile– tokeno pradžia: yychar– tokeno pabaiga: yychar + yylength()
• yylex() paprastai grąžina Yytoken klasės egzempliorių, nors galima deklaruoti ir kitą tipą direktyva %type
38
Tokenai
• Paprastai kiekvienas tokenas yra klasės Symbol iš paketo - java_cup.runtime elementas.
• Šį paketą eksportuoja analizatorius JavaCUP• Symbol klasėje nustatomi sveiki skaičiai
atitinkantys kiekvienam tokenui.• Pavyzdžiui, čia aprašytos sveiko tipo
konstantos, tokios kaip sym.ID, sym.IF
39
Reguliarios išraiškos pavyzdys
reguliariIšraiška{ veika }
• Pavyzdys:{IDENTIFIER}{ System.out.println("ID is ..."
+ yytext());}• Prasmė:
– Pirmuosiuose skliaustuose šablonas, kurio atitikmens ieškome;
– antruosiuose skliaustuose kodas, kuris bus vykdomas jei atitikmuo bus rastas.
40
Makrosų pavyzdžiai
• Apibrėžimas (antroji JLex aprašymo dalis):IDENTIFIER = [a-zA-z_][a-zA-Z0-9_]*LETTER=[A-Za-z_] DIGIT=[0-9]WHITESPACE= [ \t\n]ALPHA_NUMERIC={LETTER}|{DIGIT}
• Panaudojimas (trečioji JLex aprašymo dalis):{IDENTIFIER} {return new Token(ID,
yytext());
41
Būsenų deklaracijos (1)
• Kai kurioms simbolinėms eilutėms atitikmuo turi būti ieškomas su skirtingomis reguliariomis išraiškomis.
• Taigi reikia turėti galimybę pervesti leksinį analizatorių į įvairias būsenas, kuriose jis funkcionuotų skirtingai, t.y. naudotų kitas reguliarias išraiškas.
• Pradėdamas darbą leksinis analizatorius yra būsenoje YYINITIAL.
• Jei norime naudoti savo būsenas, aprašome jas antrojoje JLex aprašo dalyje.– Pavyzdžiui: %state COMMENTS
• Perėjimui tarp būsenų naudojamas metodas yybegin().
42
Būsenų deklaracijos (2)
• Pavyzdys:<YYINITIAL> "//" {yybegin(COMMENTS);}<COMMENTS> [^\n] {}<COMMENTS> [\n] {yybegin(YYINITIAL);}• Jei Yylex yra pradinėje YYINITIAL būsenoje ir
atpažysta //, tuomet jis pereina į būseną COMMENTS
• Jei jis yra būsenoje COMMENTS ir atpažysta bet kokį simbolį išskyrus \n, tuomet jokia veika neatliekama
• Jei jis yra būsenoje COMMENTS ir atpažysta \n, tuomet jis grįžta atgal į būseną YYINITIAL
43
minimal.lex
package Example;import java_cup.runtime.Symbol;%%%cup%%";" { return new Symbol(sym.SEMI); }"+" { return new Symbol(sym.PLUS); }"*" { return new Symbol(sym.TIMES); }"(" { return new Symbol(sym.LPAREN); }")" { return new Symbol(sym.RPAREN); }[0-9]+ { return new Symbol(sym.NUMBER, new
Integer(yytext())); }[ \t\r\n\f] { /* ignore white space. */ }. { System.err.println("Illegal character: "+yytext()); }
44
import java_cup.runtime.Symbol;
• Ši eilutė importuoja klasę Symbol.• Kai sintaksinis analizatorius iškviečia Yylex
sekančiam tokenui, Yylex objektas grąžina Symbol klasės egzempliorių.
45
Symbol klasė
• Symbol klasės egzempliorius turi keletą konstruktorių.
• "+" { return new Symbol(sym.PLUS); }– Paprasčiausias naudoja tik tokeno eilės numerį
(t.y. vieną iš sugeneruotos sym klasės konstantų)– Šioms konstantoms vardus duodame mes patys,
o reikšmes priskiria JavaCUP generuodamas sym.java failą.
• [0-9]+ { return new Symbol(sym.NUMBER, new Integer(yytext())); }
– Sudėtingesnis konstruktorius dar naudoja leksinę reikšmę kurią apibrėžia tam skirtas objektas• čia naudojamas Java objektas • gali būti naudojamas ir mūsų sukurtos klasės
objektas
46
minimal.lex tokenams atitinkantys sveiki skaičiai
//----------------------------------------------------// The following code was generated by CUP v0.10k// Sat Oct 02 09:56:56 EEST 2004//----------------------------------------------------
package Example;
/** CUP generated class containing symbol constants. */public class sym { /* terminals */ public static final int RPAREN = 6; public static final int error = 1; public static final int PLUS = 3; public static final int NUMBER = 7; public static final int SEMI = 2; public static final int LPAREN = 5; public static final int TIMES = 4; public static final int EOF = 0;}
47
Įvesties failo panaudojimas JLex/CUP
48
Anglų kalbos gramatika
A sentence is a nounphrase, a verb, and anoun phrase.
A noun phrase is anarticle and a noun.
A verb is…
An article is…
A noun is...
<S> ::= <NP> <V> <NP>
<NP> ::= <A> <N>
<V> ::= loves | hates | eats
<A> ::= a | the
<N> ::= dog | cat | rat
49
Gramatikos taikymas
• Gramatika yra taisyklių rinkinys nusakantis kaip sukonstruoti sintaksinį medį – a parse tree
• Medžio šaknyje (root) patalpiname aukščiausią hierarchijos elementą – t.y. <S>
• Gramatikos taisyklės nusako kaip toliau yra pridedami sekantys mazgai - vaikai. – Pavyzdžiui, taisyklė <S> ::= <NP> <V> <NP>
parodo kokia tvarka pridedami mazgai <NP>, <V> ir <NP> – t.y. <S> vaikai.
50
<S> ::= <NP> <V> <NP>
<NP> ::= <A> <N>
<V> ::= loves | hates|eats
<A> ::= a | the
<N> ::= dog | cat | rat
tokens
non-terminalsymbols
start symbol
a production
51
A Parse Tree
<S>
<NP> <V> <NP>
<A> <N><A> <N>
the dog the cat
loves
52
Kontekstiškai laisva gramatika(Context free grammar)
• Programavimo kalbų sintaksė aprašoma kontekstiškai laisvomis gramatikomis (CFG).
• CFG taisyklės yra daugumoje rekursyvios.• Sintaksinis analizatorius tikrina, ar programa
tenkina CFG taisykles ar ne. Jei tenkina, sintaksinis analizatorius kuria programai sintaksinį medį (parse tree)
• CFG aprašomos (Backus Naur Form), pavyzdžiui:assignment -> identifier := expressionexpression -> identifierexpression -> numberexpression -> expression + expression
• T.y. sintaksinis analizatorius tikrina, ar programą galima išvesti naudojantis nustatytos gramatikos BNF
53
Kontekstiškai laisvos gramatikos
Kontekstiškai laisvą gramatiką sudaro• V: baigtinis neterminalų skaičius• Σ: baigtinis terminalų skaičius• R: Baigtinis taisyklių skaičius, kurių bendra forma neterminalas -> {neterminalas, terminalas}*• S: pradinis neterminalas
Dauguma programavimo kalbų yra kontekstiškai laisvos
54
Kontekstiškai laisvos gramatikos komponentai
1. Tokenai (terminalai)2. Neterminalai3. Produkcijos4. Pradinis neterminalas
Gramatika nusakomaišvardinant jos produkcijas.
Tokenų eilutės pagimdytospradinio simbolio sudarogramatika apibrėžtą kalbą.
Pavyzdys:list → list + digit | list – digit | digitdigit → 0|1|2|3|4|5|6|7|8|9
55
BNF gramatikos apibrėžimas (1)
• BNF gramatiką sudaro keturios dalys:– Tokenų rinkinys– Non-terminal symbols rinkinys– Pradinis simbolis (start symbol)– Produkcijų (productions) rinkinys
• Tokenai yra mažiausi (atominiai) sintaksės vienetai.
• Neterminalai atstovauja stambesnius sintaksės vienetus– Vaizduojami nelygybių skliaustuose– gramatikos taisyklės apibrėžia kaip jie yra
išskleidžiami į tokenų literalus.
56
BNF gramatikos apibrėžimas (2)
• Satrtinis simbolis yra išskirtinis neterminalas sudarantis atitnkamos gramatikos parse tree šaknį.
• Produkcijos yra medžio konstravimo taisyklės.• Kiekviena turi kairiąją pusę, skirtuką
(separator) ::= ir dešiniąją pusę.– Kairioji pusė yra vienas neterminalas– Dešiniąją pusę gali sudaryti seka kurioje gali
būti kaip terminalai, taip ir neterminalai– Dešinės pusės narių seka nusako kokia
tvarka turi būti konstruojamas parse tree.
57
Hierarchinės programos struktūros išreiškimo rekursinėmis taisyklėmis
pavyzdys
• Kiekvienas identifikatorius (identifier) yra išraiška (expression).
• Kiekvienas skaičius (number) yra išraiška (expression).
• Jei expression1 ir expression2 yra išraiškos, tai išraiškomis yra ir expression1 + expression2 expression1 * expression2 (expression1).
58
Programavimo Kalbos Gramatika
• Išaiška gali būti (arba)– dviejų išraiškų suma– dviejų išraiškų sandauga– apskliausta išraiška– vienu iš kintamųjų a, b arba c
<exp> ::= <exp> + <exp> | <exp> * <exp> | ( <exp> ) | a | b | c
59
Pavyzdys
Šioje gramatikoje ištiesų yra šešios produkcijos.T.y. viena ši produkcija yra ekvivalenti šioms šešioms:
<exp> ::= <exp> + <exp> | <exp> * <exp> | ( <exp> ) | a | b | c
<exp> ::= <exp> + <exp><exp> ::= <exp> * <exp><exp> ::= ( <exp> )<exp> ::= a<exp> ::= b<exp> ::= c
60
BNF Realiems skaičiams
<real-number> ::= <integer-part> . <fraction><integer-part> ::= <digit> | <integer-part> <digit> <fraction> ::= <digit> | <digit> <fraction> <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Realaus skaičiaus 21.89 išvedimas: rašome išraiškų seką kurioseneterminalai žingsnis po žingsnio keičiami keičiami terminalais;
real-number integer-part . fraction integer-part digit . fraction digit digit . fraction 2 digit . fraction 2 1 . fraction 2 1 . digit fraction 2 1 . 8 fraction 2 1 . 8 digit 2 1 . 8 9
61
Empty
• Specialus neterminalas <empty> naudojamas kai numanoma, kad gramatika toliau nebeturi nieko daugiau generuoti.
• Šio neterminalo panaudojimo pavyzdys: if-then konstrukcija su galima else dalimi:
<if-stmt> ::= if <expr> then <stmt> <else-part>
<else-part> ::= else <stmt> | <empty>
62
Sukonstruojame gramatikąJava primityvių tipų deklaracijoms
float a;boolean a,b,c;int a=1, b, c=1+2;
<var-dec> ::= <type-name> <declarator-list> ;
<type-name> ::= boolean | byte | short | int | long | char | float | double
<declarator-list> ::= <declarator> | <declarator> , <declarator-list>
<declarator> ::= <variable-name> | <variable-name> = <expr>
63
EBNF
• papildoma sintaksė supaprastinanti produkcijų žymėjimą:– {x} reiškia nulį ar daugiau x pasikartojimų– [x] reiškia, kad x yra papildomas (t.y. x |
<empty>)– () naudojamas grupavimui– | reiškia pasirinkimą tarp alternatyvų– Šie meta simboliai kabutėse reiškia
atitinkamus tokenus
64
EBNF pavyzdžiai
<stmt-list> ::= {<stmt> ;}
<if-stmt> ::= if <expr> then <stmt> [else <stmt>]
<thing-list> ::= { (<stmt> | <declaration>) ;}
65
Sintaksinės analizės medis(Parse Tree)
• Kodėl reikalingas sintaksinės analizės medis? – Sintaksinio medžio struktūra išreiškia jo
generuojamos išraiškos semantiką.• Konstruojant parse tree pradinis simbolis
talpinamas medžio šaknyje• Pridedant neterminalams vaikų mazgus sekama
tam neterminalui skirtomis gramatikos konstrukcijomis
• Medis baigtas, kai visi jo lapai yra tokenai• Medžio lapai skaitomi iš kairės į dešinę – tai
sakinys kuriam sukonstruotas medis
66
Pavyzdys
<exp>
<exp> + <exp>
( <exp> )
<exp> * <exp>
( <exp> )
a b
((a+b)*c)
c
67
Sintaksinės analizės medis
assignment operator
=
expressionexpression
expression
+
identifier
position
identifier
initial
expression
identifier
rate
expression+
number
60
Sintaksiniame medyjeyra daug nereikalingos
informacijos
Sintaksinis analizatorius (parser) programai kuria tamtikrą sintaksinę struktūrą – sintaksinį medį (parse tree).
• Sintaksiniame medyje visiterminalai yra lapuose.• Kontekstiškai laisvoje gramatikoje(context free grammar) visi vidiniaimazgai yra neterminalai.
position = initial + rate + 60
68
Pavyzdys: gramatika generuoja du medžius
<exp>
<exp> + <exp>
c
<mulexp>
<exp> + <exp>
b
<mulexp> a
<mulexp>
<exp>
<exp> + <exp>
b
<mulexp>
<exp> + <exp>
a
<mulexp> c
<mulexp>
Deja pirmasis medis neatitinka įprasto sudėtiesoperatoriaus asociatyvumo
a + b + c
<exp> ::= <exp> + <exp> | <mulexp><mulexp> ::= <mulexp> * <mulexp>| (<exp>) | a | b | c
69
Asociatyvumo problema ištaisoma modifikuojant gramatiką taip, kad sudėčiai
medis augtų kairėn ir žemyn
<exp> ::= <exp> + <mulexp> | <mulexp><mulexp> ::= <mulexp> * <rootexp> | <rootexp><rootexp> ::= (<exp>)| a | b | c<exp>
<exp> + <mulexp>
b
<rootexp>
<exp> + <mulexp>
<mulexp> c
<rootexp>
a
<rootexp>
Ši gramatikaišraiškaia+b+c generuojateisingąmedį
70
Abstraktus sintaksinis medis
• Programavimo kalbos paprastai saugo supaprastintą sintaksinio medžio versiją vadinamą abstrakčiu sintaksiniu medžiu (abstract syntax tree).
• Tokiame medyje kiekvienam operatoriui skiriamas atskiras mazgas, o kiekvienam operandui skiriamas submedis.
71
<exp>
<exp> + <mulexp>
b
<rootexp>
<exp> + <mulexp>
<mulexp> c
<rootexp>
a
<rootexp> +
c
a
+
b
sintaksinis medis
abstraktus sintaksinis medis
72
JavaCUP (Construct Useful Parser)YACC analogas
• JavaCUP tai Java kalba parašytas generatorius (Bottom up Parser) sukuriantis Java kalba parašytą sintaksinį analizatorių.
JavaCUP Specifikacija(cup failas)
JavaCUPSintaksinis
analizatorius
73
CUP aprašymo struktūra
1. package ir import specifikacija (papildoma)
2. Vartotojo kodo komponentai (papildomi, leidžia vartotojui deklaruoti kodą, kuris bus įtrauktas į sugeneruotą sintaksinį analizatorių)
3. Simbolių sąrašai (terminalai ir neterminalai)4. Prioritetų deklaracijos (papildoma)5. Gramatika
74
package ir import specifikacija
• Specifikacija prasideda papildomomis package ir import deklaracijomis.
• Deklaracijos turi tą pačią sintaksę ir vaidina tą patį vaidmenį kaip ir Java kalboje.
• Deklaracija package nurodo kuriame pakete (ir kataloge) bus patalpintos CUP sukurtos sym ir parser klasės.
• Deklaracijoje import nurodyti paketai bus deklaruoti parser klasėje, tuo pačiu tais paketais galės naudotis CUP specifikacijoje patalpintas vartotojo Java kodas.
75
Vartotojo kodo komponentai (1)
• action code{: ... :} Leidžia įtraukti vartotojo kodą į CUP$actions klasę ir kuris savo ruožtu bus naudojamas gramatikoje esančiu kodu.– Ši dalis dažniausiai skiriama darbui su
simbolių lentele.
• parser code{: ... :} Ši dalis panaši į action code, tačiau kodas bus įtrauktas į klasę parser, todėl juo galės naudotis šios klasės metodai (pavyzdžiui, scan()).– Ši dalis dažnai skiriama standartinei
įvesčiai aptarnauti.
76
parser code{: ... :} pavyzdyje minimal.cup
parser code {: public static void main(String args[]) throws Exception { new parser(new Yylex(System.in)).parse(); }:}
77
Vartotojo kodo komponentai (2)
• init with{: ... :} Šioje dalyje pateikiamas kodas, kuris bus atliekamas pats pirmas, dar prieš analizatoriui užklausiant pirmojo tokeno.– Paprastai naudojama analizatoriui,
įvairioms lentelėms ir struktūroms inicijuoti
• scan with{: ... :} Nurodo kaip sintaksinis analizatorius turi užklausti leksinį analizatorių sekančio tokeno. – Šioje dalyje esantis kodas grąžinimui turi
naudoti tipą java_cup.runtime.Symbol
78
Simbolių sąrašai
• Šioje dalyje deklaruojamas kiekvienas naudojamos gramatikos terminalas ir neterminalas.terminal name1, name2, ...;terminal classname name1, name2, ...; non terminal name1, name2, ...;non terminal classname name1, name2, ...;
• Klasės naudojamos terminalų deklaracijose turi būti klasės java_cup.runtime.token poklasės.
• Klasės naudojamos neterminalų deklaracijose turi būti klasės java_cup.runtime.symbol poklasės.
79
Terminalų ir neterminalų vardams negalima naudoti CUP rezervuotus
žodžius:
"code", "action", "parser", "terminal", "non",
"nonterminal", "init", "scan", "with", "start",
"precedence", "left", "right", "nonassoc",
"import", "package"
80
Prioritetų deklaracijos (1)
• Yra trys prioriteto/asociatyvumo deklaracijų tipai:
precedence left terminal[, terminal...];precedence right terminal[, terminal...];precedence nonassoc terminal[, terminal...];
• Deklaracijų sąraše prioritetai auga iš viršaus į apačią
• Pavyzdžiui, jei nebus prioritetų, analizatorius nežinos kuria tvarka apskaičiuoti išraišką 3 + 4 * 8
• Jei terminalo nėra prioritetų deklaracijų sąraše, jo prioritetas laikomas mažiausiu.
81
Prioritetų deklaracijos (2)
• Produkcijos prioritetas yra laikomas lygiu žemiausio jos terminalų prioritetui.
• Jei produkcija neturi terminalų, jos prioritetas yra žemiausias.
• Pavyzdžiui, produkcijos expr ::= expr TIMES expr
prioritetas lygus TIMES prioritetui• Jei produkcijų prioritetai lygūs, toliau išraiškų
vertinimo tvarką nustato terminalų asociatyvumas.
82
Prioritetų deklaracijos (3)
• Yra trys asociatyvumo tipai: left, right ir nonassoc.
• Pavyzdžiui, jei PLUS asociatyvumas nustatytas left, tuomet 3 + 4 + 5 + 6 + 7 bus vertinama iš kairės į dešinę (pradedant 3 + 4); jei PLUS asociatyvumas nustatytas right bus vertinama iš dešinės į kairę (pradedant 6 + 7)
• Jei terminalas deklaruotas kaip nonassoc, tuomet sekantys vienas po kito jo taikymai su vienodu prioritetu bus interpretuojami kaip klaida.
• Pavyzdžiui, jei == asociatyvumas nustatytas nonassoc, tuomet 6 == 7 == 8 == 9 reikš klaidą.
83
Gramatikos produkcijos (1)
non_terminal symbol::= { actions, terminal symbols, non_terminal symbols } ;
• actions įterpiamos tarp skirtukų {: ... :}– action vykdoma tuomet, kai analizatorius
atpažįsta jai atitinkančią produkcijos dalį.• Kaip terminalai, taip ir neterminalai sąraše gali
būti pažymėti vardu atskirtu dvitaškiu. – Šis vardas atstovauja kode jo pažymėtą
objektą.
84
Gramatikos produkcijos (2)
• Jei neterminalas turi keletą produkcijų, jos deklaruojamos kartu ir atskiriamos iš dešinės (|) simboliu.
• Galima nurodyti analizatoriui kuriuo neterminalu jis turi pradėti darbą:
start with non_terminal;– Jei tokios deklaracijos nėra, analizatorius
pradeda darbą pirmąja produkcija.
85
Aritmetinių išraiškų su sveikais skaičiais interpretatorius
• Interpretatorius turi nuskaityti aritmetines išraiškas (besibaigiančias kabliataškiu) iš standartinės įvesties (standard input).
• Interpretatorius turi apskaičiuoti išraiškas ir resultatą išvesti į standartinę išvestį (standard output).
86
Aritmetinių išraiškų su sveikais skaičiais gramatika
expr_list::= expr_list expr_part | expr_part
expr_part ::= expr ';'
expr ::= expr '+' expr | expr '-' expr | expr '*' expr
| expr '/' expr | expr '%' expr | '(' expr ')' | '-' expr | number
87
Terminalų ir neterminalų deklaracija
• Pirmasis žingsnis aprašant sintaksinį analizatorių (kuriant jo specifikacijų failą) yra deklaruoti naudojamus terminalus ir neterminalus.
• Neterminalai:– expr_list, expr_part ir expr
• Terminalai:– SEMI, PLUS, MINUS, TIMES, DIVIDE, MOD,
NUMBER, LPAREN, ir RPAREN• Šiuos terminalų vardus mes pasirinkome
patys JLex specifikacijų faile.– Jei terminalui nenurodome tipo, tai jis negali
įgyti reikšmės.
88
Terminalų ir neterminalų deklaracijapavyzdyje minimal.cup
• terminal SEMI, PLUS, TIMES, LPAREN, RPAREN;• terminal Integer NUMBER;
• non terminal expr_list, expr_part;• non terminal Integer expr;
89
Operatorių prioritetai ir asociatyvumas
• Mūsų naudojama gramatika nėra vienareikšmė.• Gramatika taps vienareikšme nustačius
operatorių prioritetus ir asociatyvumo taisykles.• Pavyzdžiui:
• Sakinių prioritetai auga iš viršaus į apačią.
precedence left PLUS, MINUS;precedence left TIMES, DIVIDE, MOD;precedence left UMINUS;
90
Leksinio analizatoriaus kuriamos klasės
• sym saugo terminalams skirtas konstantas• parser klasė realizuoja analizatorių
– ji yra java_cup.runtime.lr_parser poklasė• CUP$action klasė skirta kaip gramatikoje
nurodyto vartotojo kodo veikoms, taip ir veikų kodui deklaracijose.
91
Inerpretatoriaus kūrimas
• Apskaičiuoti išraiškas ir išvesti rezultatą galima tik su atitinkamu Java kodu.
• Šis Java kodas turi būti atitinkamose vietose patalpintas tarp žymių {: ir :}
expr:l PLUS expr:r{: RESULT=new Integer(l.intValue() + r.intValue()); :}
– Pirmasis neterminalas pažymėtas l, o antrasis r.
• Kiekvienos produkcijos kairioji pusė visuomet žymima identifikatoriumi RESULT– ši reikšmė (RESULT) priskiriama
analizatoriaus grąžinamam objektui Symbol.
92
min
imal
.cu
ppackage Example;import java_cup.runtime.*;parser code {:public static void main(String args[]) throws Exception {
new parser(new Yylex(System.in)).parse();}
:}terminal SEMI, PLUS, TIMES, LPAREN, RPAREN;terminal Integer NUMBER;non terminal expr_list, expr_part;non terminal Integer expr;precedence left PLUS;precedence left TIMES;expr_list ::= expr_list expr_part | expr_part;expr_part ::= expr:e {: System.out.println(" = "+e+";"); :} SEMI;expr ::= NUMBER:n {: RESULT=n; :}
| expr:l PLUS expr:r{: RESULT=new Integer(l.intValue() + r.intValue()); :}
| expr:l TIMES expr:r{: RESULT=new Integer(l.intValue() * r.intValue()); :}
| LPAREN expr:e RPAREN {: RESULT=e; :};
93
minimal.lex
package Example;import java_cup.runtime.Symbol;%%%cup%%";" { return new Symbol(sym.SEMI); }"+" { return new Symbol(sym.PLUS); }"*" { return new Symbol(sym.TIMES); }"(" { return new Symbol(sym.LPAREN); }")" { return new Symbol(sym.RPAREN); }[0-9]+ { return new Symbol(sym.NUMBER, new Integer(yytext())); }[ \t\r\n\f] { /* ignore white space. */ }. { System.err.println("Illegal character: "+yytext()); }