Kreirano 2024-09-30 Mon 13:42, pritisni ESC za mapu, m za meni, Ctrl+Shift+F za pretragu
Osnovne klase alata:
Posmatrajmo sledeći jednostavan iskaz:
if a > 5:
print("Veće od 5!")
Ulazni string čine karakteri ovog programskog koda: i
, f
, <prazno>
, a
, <prazno>
,
>
, itd. Skeniranje će grupisati pojedinačne karaktere u tokene. Svaki token ima
svoje ime (ili tip, npr. identifikator
, operacija
, varijabla
), i vrednost (npr.
if
, a
, >
, 5
), odnosno deo ulaza koji je prepoznat kao ovaj tip tokena. Proces
tokenizacije će za ulazni string da proizvede nisku tokena koja će služiti kao
ulaz u proces parsiranja. Za primer sa prethodnog listinga izlaz skenera će
imati sledeći oblik (pod pretpostavkom da ignorišemo prazne karaktere):
[Keyword(if), Variable(a), Operator(>), IntLiteral(5),...]
array [1..10] of integer
1..10
se može tokenizovati kao konstanta 1.
tipa real
iza koje sledi
konstanta .10
istog tipa, ili kao interval od 1 do 10. Tek ukoliko imamo
kontekst u kome vršimo skeniranje možemo da razlučimo ove dve situacije.
a * b
Na programskom jeziku C ovaj iskaz može da se tumači kao množenje varijabli a
i
b
, ali i kao deklaracija varijable b
čiji je tip pokazivač na a
u zavisnosti od
semantike.
Stablo parsiranja za ulazni string -(4-1)*5/(2+4.67)
-(4-1)*5/(2+4.67)
-(4-1)/5/(2+4.67)
možemo u postfiksnoj notaciji (obrnuta
poljska notacija) zapisati kao 4 1 - 5 / 2 4.67 + / -
. Ovo će rezultovati
različitim stablima parsiranja ali je suština izraza ista i rezultovaće
istim apstraktnim sintaksnim stablima.Formalna gramatika je \(G = (N, \Sigma, P, S)\) gde je:
+
, -
, …), oznake
interpunkcije (zagrade, zarez, tačka…) - terminalni simboli gramatikeSkup produkcija:
\(A \rightarrow \alpha_1, A \rightarrow \alpha_2, A \rightarrow \alpha_3\)
može se pisati kao:
\(A \rightarrow \alpha_1 | \alpha_2 | \alpha_3\)
Gde \(\alpha_1, \alpha_2, \alpha_3\) nazivamo alternativama produkcije \(A\).
Formalne gramatike se mogu klasifikovati prema hijerarhijskoj klasifikaciji Noama Čomskog1. Prema ovoj klasifikaciji gramatike mogu biti:
G = ({S}, {a, b}, P, S)
gde je skup produkcionih pravila P
dat kao:
S → a S a
S → b S b
S → ε
Ako je \(G\) formalna gramatika tada sa \(L(G)\) obeležavamo skup svih rečenica koje \(G\) može da generiše. Ovaj skup zovemo jezikom koji \(G\) generiše ili definiše.
\(S \overset{1}{\rightarrow} aSa \overset{1}{\rightarrow} aaSaa \overset{2}{\rightarrow} aabSbaa \overset{3}{\rightarrow} aabbaa\)
Korak derivacije je element oblika:
\(\gamma A \delta \Rightarrow \gamma \alpha \delta\)
gde: \(\gamma, \delta \in (N \cup T)*\)
i \(A \rightarrow \alpha\) je pravilo gramatike.
Izvođenje rečenične forme \(\tau\) iz rečenične forme \(\sigma\) je sekvenca koraka izvođenja:
\(\sigma \Rightarrow \beta_1 \Rightarrow \beta_2 \Rightarrow ... \Rightarrow \beta_{n-1} \Rightarrow \tau\)
Takođe možemo pisati i \(\sigma \overset{*}{\Rightarrow} \tau\), ukoliko imamo 0 ili više koraka, ili \(\sigma \overset{n}{\Rightarrow} \tau\) ukoliko imamo tačno \(n\) koraka, ili, ukoliko je \(n>0\) možemo pisati \(\sigma \overset{+}{\Rightarrow} \tau\).
Bilo koja niska terminala i neterminala koja se može dobiti primenom produkcionih pravila počevši od početnog simbola naziva se rečeničnom formom (Sentential Form).
\(( x + S ) * S - S * S / ( S + S )\)
Ukoliko se rečenična forma sastoji samo od terminala onda je to rečenica (Sentence).
\(( x + y ) * x - z * y / ( x + x )\)
Odnosno možemo pisati da je rečenična forma niska \(\alpha\) za koju važi \(S \overset{*}{\Rightarrow} \alpha\). A ukoliko važi i \(\alpha \in T*\) onda je \(\alpha\) rečenica.
1. S → x 2. S → y 3. S → z 4. S → S + S 5. S → S - S 6. S → S * S 7. S → S / S 8. S → ( S )
S (startni simbol) → S - S (pravilo 5) → S * S - S (pravilo 6, primenjeno na levi neterminal S) → S * S - S / S (pravilo 7, primenjeno na desni neterminal S) → ( S ) * S - S / S (pravilo 8, primenjeno na levi S) → ( S ) * S - S / ( S ) (pravilo 8, primenjeno na desni S) → ( S + S ) * S - S / ( S ) (itd.) → ( S + S ) * S - S * S / ( S ) → ( S + S ) * S - S * S / ( S + S ) → ( x + S ) * S - S * S / ( S + S ) → ( x + y ) * S - S * S / ( S + S ) → ( x + y ) * x - S * y / ( S + S ) → ( x + y ) * x - S * y / ( x + S ) → ( x + y ) * x - z * y / ( x + S ) → ( x + y ) * x - z * y / ( x + x )
\(S \xrightarrow{1} E \xrightarrow{2} E + E \xrightarrow{7} 3 + E\\ \xrightarrow{4} 3 + E * E \xrightarrow{7} 3 + 4 * E \\ \xrightarrow{6} 3 + 4 * (E) \\ \xrightarrow{3} 3 + 4 * (E - E) \\ \xrightarrow{7} 3 + 4 * (7 - E) \\ \xrightarrow{7} 3 + 4 * (7 - 2)\)
3 + 4 * (7 - 2)
U produkciji:
IF -> 'if' COND 'then' STMT 'else' STMT
| 'if' COND 'then' STMT
definisan je if
iskaz nekog jezika:
if stmt1 then if stmt2 then stmt3 else stmt4
Ova konstrukcija može da se tumači na dva načina. Kod prvog tumačenja je else
deo spoljnjeg if
iskaza, odnosno:
if stmt1 then (if stmt2 then stmt3) else stmt4
dok je kod drugog tumačenja else
deo iskaza u sastavu unutrašnjeg if
iskaza,
odnosno:
if stmt1 then (if stmt2 then stmt3 else stmt4)
if-else
klauzuli može se dodati ključna
reč endif
.
Stablo koje oslikava prioritet i asocijativnost operacija
S → E
E → E '+' E
E → E '-' E
E → E '*' E
E → E '/' E
E → '(' E ')'
E → broj
Ulaz: \(3+4*(7-2)\)
A trebalo bi:
Jedna od tehnika razrešavanja višeznačnosti kroz enkodovanje prioriteta i asocijativnosti operacija na nivou gramatike.
S → E
E → E + T | E - T | T
T → T * F | T / F | F
F → ( E ) | id
E
definiše operacije sabiranja i oduzimanja i referencira
pravilo T
koje definiše operacije množenja i deljenja višeg prioriteta.Na primer:
Something: INT | FLOAT;
terminals
INT: /\d+/;
FLOAT: /\d+(\.\d+)?/;
INT
i FLOAT
mogu da prepoznaju isti deo
ulaza ukoliko u ulaznom broju nemamo decimalnu tačku i cifre iza nje. Ovaj
problem nastaje zbog preklapanja na početnom delu definicije (\d+
)."3"
oba pravila mogu da ga prepoznaju što
znači da imamo dva moguća rešenja.Određene vrste parsera ne smeju da imaju levo rekurzivne produkcije jer to dovodi do beskonačne rekurzije gde parser primenjuje stalno iste produkcije bez konzumiranja tokena sa ulaza.
Na primer:
\(A \rightarrow A a | \epsilon\)
Prepoznaje stringove oblika \(a*\) ali kod parsera koji ne dozvoljavaju levu rekurziju će doći do beskonačne rekurzije u pokušaju da se prepozna \(A\).
\(recognize(A) \overset{Aa}{\rightarrow} recognize(A) \ldots\)
Pravilo \(A \rightarrow Aa | B\) postaje \(A \rightarrow Ba*\)
Primer:
\(expr \rightarrow expr \; "+" \; term \; | \; number\)
postaje:
\(expr \rightarrow number ("+" \; term)*\)
S → E
E → E '+' E
E → E '-' E
E → E '*' E
E → E '/' E
E → '(' E ')'
E → broj
S → E
E → T E'
E' → '+' T E'
E' → '-' T E'
E' → Ɛ
T → F T'
T' → '*' F T'
T' → '/' F T'
T' → Ɛ
F → ( E )
F → broj
ili kompaktnije:
S → E
E → T (('+' | '-') T)*
T → F (('*' | '/') F)*
F → '(' E ')' | broj
Refaktorisana gramatika nije ekvivalentna polaznoj jer definiše prioritet i asocijativnost operacija dok kod nerefaktorisane gramatike pored leve rekurzije imamo višeznačnost jer prioriteti i asocijativnost operatora nisu definisani.
Takođe, stabla parsiranja do kojih dolazimo upotrebom refaktorisane gramatike nisu intuitivna kao kod polazne i zahtevaju dodatni posao prilikom analize izraza.
Za ulaz 3 + 4 * ( 7 - 2 )
dobijamo stablo na desnoj strani.
A za dalju obradu daleko bi bilo pogodnije ovakvo stablo koje oslikava smisao ulaznog stringa.
letter = "A" | "B" | "C" | "D" | "E" | "F" | "G"
| "H" | "I" | "J" | "K" | "L" | "M" | "N"
| "O" | "P" | "Q" | "R" | "S" | "T" | "U"
| "V" | "W" | "X" | "Y" | "Z" ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
symbol = "[" | "]" | "{" | "}" | "(" | ")" | "<" | ">"
| "'" | '"' | "=" | "|" | "." | "," | ";" ;
character = letter | digit | symbol | "_" ;
identifier = letter , { letter | digit | "_" } ;
terminal = "'" , character , { character } , "'"
| '"' , character , { character } , '"' ;
lhs = identifier ;
rhs = identifier
| terminal
| "[" , rhs , "]"
| "{" , rhs , "}"
| "(" , rhs , ")"
| rhs , "|" , rhs
| rhs , "," , rhs ;
rule = lhs , "=" , rhs , ";" ;
grammar = { rule } ;
Gramatika: S → E E → T + E E → T T → int Ulaz: int + int + int Production Input Action --------------------------------------------------------- S int + int + int Predict S -> E E int + int + int Predict E -> T + E T + E int + int + int Predict T -> int int + E int + int + int Match int + E + int + int Match + E int + int Predict E -> T + E T + E int + int Predict T -> int int + E int + int Match int + E + int Match + E int Predict E -> T T int Predict T -> int int int Match int Accept
http://stackoverflow.com/questions/5975741/what-is-the-difference-between-ll-and-lr-parsing
/
umesto sa |
da bi se naglasila uređenost.
Kako enkodovati pravila prioriteta i eliminisati levu rekurziju?
Gramatike izraza za parsiranje se sastoje od:
Skup terminalnih simbola (terminala) predstavlja alfabet jezika. Neterminalni simboli (neterminali) su varijable koje predstavljaju nisku sastavljenu od terminala i/ili drugih neterminala.
Pravila parsiranja su oblika \(A \leftarrow e\) gde je \(A\) neterminal a \(e\) predstavlja izraz za parsiranje. Izraz za parsiranje može biti prost i u tom slučaju može biti:
Ukoliko su \(e\), \(e_1\) i \(e_2\) izrazi za parsiranje tada se složeni izrazi za parsiranje mogu kreirati upotrebom sledećih operatora:
\(e_1 e_2\) (sekvenca) | redom se prepoznaju izrazi \(e_1\) i \(e_2\) |
\(e_1 / e_2\) (uređeni izbor) | prepoznaje se \(e_1\) ili \(e_2\) u navedenom redosledu |
\(e?\) (opciono prep.) | prepoznaje se izraz \(e\) opciono |
\(e+\) (jedan ili više) | sukcesivno se vrši prepoznavanje izraza \(e\) dok god uspeva. Izraz uspeva ako je bar jedno prepoznavanje uspešno |
\(e*\) (nula ili više) | sukcesivno se vrši prepoznavanje izraza \(e\) dok god uspeva. Izraz uvek uspeva |
PEG poseduje i posebnu klasu operatora – sintaksne predikate. Oni omogućavaju definisanje dodatnih informacija o kontekstu u kome se nalazi izraz koji pokušavamo da prepoznamo bez konzumiranja ulaznog stringa. Sintaksni predikati su sledeći:
Sintaksni predikati omogućavaju definisanje izraza za parsiranje oblika \(e_1 \&e_2\) – biće prepoznat izraz \(e_1\) samo ukoliko iza njega sledi tekst koji se može prepoznati izrazom \(e_2\), pri čemu deo ulaznog teksta prepoznatog izrazom \(e_2\) neće biti uklonjen sa ulaza (neće biti konzumiran).