Sintaksa (programski jezici)
U računarstvu, sintaksa programskog jezika predstavlja niz pravila koja definišu kombinaciju simbola za koje se smatra da daju ispravno struktuiran dokument ili fragment u traženom programskom jeziku. Ovo se odnosi na programske jezike, gde dokument predstavlja izvorni kod, ali i na jezike za obeležavanje, gde dokument predstavlja podatak. Sintaksa programskog jezika definiše svoju površinsku formu.[1] Tekstualno bazirani programski jezici su bazirani na rasporedu karaktera, dok su vizuelni programski jezici bazirani na prostornom rasporedu i vezi između simbola (koje mogu biti tekstualne ili grafičke prirode) apstraktno. Za dokument koji ima neispravnu sintaksu kaže se da ima sintaksnu grešku.
Sintaksa (forma) predstavlja suprotnost semantici. Kod obrade programskih jezika, obrada semantike generalno dolazi nakon obrade sintakse, ali u nekim slučajevima je neophodna prvo obrada semantike za obradu sintakse. U komapjleru, sintaksna analiza obuhvata prednji kraj, dok semantička analiza obuhvata zadnji kraj (i srednji kraj, ako predstavlja karakterističan deo).
Nivoi sintakse
[uredi | uredi izvor]Sintakse programskih jezika su generalno podeljene u tri nivoa:
- Reči – leksički nivo, određuje kako će izgledati forma znakova;
- Fraze – gramatički nivo, generalno gledano, određuje fraznu formu znakova;
- Sadržaj – određuje šta objekti ili promenljive rade, takođe određuje da li su one ispravne, itd.
Ovakvo razlikovanje dovodi do modulariteta, dozvoljavajući da se svaki nivo obradi odvojeno, i uglavnom nezavisno. Prvo lekser pretvara linearan raspored karaktera u linearni raspored znakova; ova procedura je poznata kao "leksička analiza". Zatim parser pretvara linearni raspored znakova u hijerarhijsko sintaksno stablo; ova procedura je poznata kao "raščlanjivanje" u generalnom smislu. Zatim kontekstualna analiza otkriva imena i proverava tipove. Ovakav modularitet je ponekad moguć, ali u mnogim programskim jezicima raniji korak zavisi od kasnijeg koraka, npr. lekser hak u C-u zato što organizacija znakova zavisi od konteksta. Čak i u ovakvim situacijama, sintaksna analiza je često viđena kao aproksimacija za ovakav idealni model.
Raščlanjivanje se može podeliti u dva dela: drvo izvođenja ili "konkretno sintaksno stablo", deo koji je određen gramatikom, ali je generalno gledano previše kompleksan za praktičnu upotrebu, i apstraktno sintaksno drvo (ASD), koje uprošćava ovo u upotrebljivu formu. ASD koraci i koraci semantičke analize se mogu smatrati formom semantičkke analize, kao što je dodavanje značenja i interpetacije sintaksi, ili alternativno kao neforamlna, manuelna implementacija sintaksnih pravila koja bi se teško ili neprijatno opisala tj. implementirala.
Nivoi uglavnom odgovaraju nivoima u hijerarhiji Čomskog. Reči su u regularnim jezicima, navedene u leksičkoj gramatici, koja je gramatika "Tipa-3", date kao regularni izrazi. Fraze su u kontekstualno slobodnim jezicima (KSJ) , ili generalno gledano u deterministički kontekstno slobodnim jezicima (DKSJ), navedene u frazama strukture gramatike, koja je "Tipa-2", date kao pravila u Bakus-Naurovoj formi (BNF). Fraze gramatike su češće specifirane u mnogim ograničenim gramatika nego u kontekstno slobodnoj gramatici, kako bi se lakše analizirale; dok se LR raščlanjivač može analizirazi DFCL u linearnom vremenu, jednostavni LALR raščlanjivač i čak jednostavniji LL raščlanjivač su više efikasniji od LR-a, ali mogu jedino analizirazi gramatiku koja ima ograničena pravila. Kontekstualna struktura se može opisati uz pomoć kontekstno-senzitivne gramatike, i automatski analizirati pomoću atributa gramatike (iako se ovaj korak generalno gledano obavlja ručno, preko pravila imena rezolucije i proveravanja tipa) i implementirati preko simboličke tabele koja sadrži imena i tipove.
Alati su napisali da je lekser automatski generisan iz leksičke specifikacije u regularni izraz a raščlanjivač iz fraze gramatike napisan u BNF-u: ovo omogućava korišćenje deklarativnog programiranja, umesto proceduralnog ili funkcionalnog programiranja. Značajan primer je Leks-Yacc par. Ovaj par automatski produkuje konkretno sintaksno stablo; autor raščlanjivača mora ručno da napiše kod i da objasni kako ga je pretvorio u apstraktno sintaksno stablo. Kontekstualna analiza je uglavnom implementirana ručno. Uprkos postojanju ovih automatskih alata, raščlanjavanje se obično unosi ručno, iz nekoliko razloga - možda frazna struktura nije kontekstualno slobodna, ili alternativna implementacija poboljšava perforamnse ili postoji neka greška, ili se gramatika vrlo lako može izmeniti. Raščlanjivači su često pisani u funkcionalnim programskim jezicima, kao što je Haskel, u jezicima za skriptovanje, kao što je Pajton ili Perl, ili u C ili C++.
Primeri grešaka
[uredi | uredi izvor]Kao primer, uzećemo, (add 1 1)
koji je sintatički ispravan Lisp-ov program (pretpostavljajući da 'add' funkcija postoji, inače bi program bio neispravan), koji dodaje 1 i 1. Međutim, sledeći kod je neispavan:
(_ 1 1) lexical error: '_' is not valid (add 1 1 parsing error: missing closing ')' (add 1 x) name error: 'x' is not bound
Primetno je da lekser ne može da identifikuje grešku - sve što on zna je da nakon produkovanja znaka LEVE_ZAGRADE, '(' ostatak programa je neispravan, jer ne postoji reč koja kreće sa '_'. U ovoj fazi raščlanjivanja, raščlanjivač indetifikuje "listu" pravila proizvodnje zbog '(' znaka, i na taj način se može dobiti poruka koja ukazuje na grešku; generalno gledano, ona može biti dvosmislena. U fazi sadržaja, simbol 'h' postoji u sintaksnom stablu, ali nije definisan, pa saimim tim analiza konteksta može dati specifičnu grešku.
U strogo tipskim progrgamskim jezicima, tipske greške su takođe forme sintaksnih grešaka koje se obično određuju u fazi kontekstualne analize, i ovo predstavlja snagu snažnog kucanja. Na primer, sledeći primer ima neispravnu sintaksu Pajtonovog koda (zato što je ovo štamparska greška, tj. tip koji se može utvrditi u fazi raščlanjivanja):
'a' + 1
... jer sabira string sa brojem. Ovo se može detektovati u fazi analize ako postoje odvojena pravila za "string + string" i "broj + broj", ali ovo će češće biti analizirano od strane generalnog pravila kao što je "Književno ili indetifikovano + književno ili identifikovano" i na taj način će greška biti detektovana u fazi kontekstualne analize, gde se nalazi tipska provera. U nekim slučajevima provera nije moguća, i ovakve sintaksne greške se mogu otkriti samo pri pokretanju programa
U slabim tipskim jezicima, gde se tip može odrediti u periodu pokretanja programa, javljaju se tipske greške umesto semantičkih grešaka, i kao što je rečeno, mogu se otkriti tek pri pokretanju programa. Sledeći Pajtonov kod:
a + b
je dvosislen, i iako ovaj kod ima ispravnu sintaksu u periodu analize, validnost sintakse se može utvrditi samo pri pokretanju koda, zato što promenljive nemaju tip u Pajtonu, već ih samo vrednosti imaju.
Definicija sintakse
[uredi | uredi izvor]Sintaksa tekstualnih programskih jezika se obinčno definiše korišćenjem kombinacije regularnih izraza (za leksičku strukturu) i Bakus-Naurove forme (za gramatičku strukturu) za iduktivno određivanje sintaksnih kategorija (neterminali) i terminalskih simbola. Sintaksne kategorije su definisane pravilima koja se nazivaju produkcije, koje specifiraju vrednost koja pripada sintaksnoj kategoriji.[1] Terminalski simboli su konkretni karakteri ili stringovi karaktera (na primer ključna reč kao što je definiši, ako, dozvoli, ili poništiti) od kojih su sintaksno ispravni programi napravljeni.
Programski jezik mora imati više različitih i jednakih gramatika, kao što su regularni jednaki izrazi (na leksičkim nivoima), ili različita pravila analiziranja koja generišu isti jezik. Koristeći širu kategoriju gramatika, kao što je LR gramatika, dozvoljeno je poređenje manjih ili jednostavnijih gramatika sa više ograničenih kategorija, kao što je LL gramatika, koja zahteva dužu gramatiku sa više pravila. Različita ali jednaka fraza gramatike daje različita stabla analiziranja, iako je osnovni jezik (niz ispravnih dokumenata) isti.
Primer: Lisp
[uredi | uredi izvor]Ispod je prikazana jednostavna gramatika, definisana koristeći notaciju regularnih izraza i Bakus-Naurove forme. Ona opisuje Lisp-ovu sintaksu, koja definiše produkte sintatičkih kategorija izraza (expression), atoma (atom), broja (number), simbola (symbol), i liste (list):
expression ::= atom | list
atom ::= number | symbol
number ::= [+-]?['0'-'9']+
symbol ::= ['A'-'Z''a'-'z'].*
list ::= '(' expression* ')'
Ova gramatika definiše sledeće:
- izraz je ili atom ili lista;
- atom je broj ili simbol;
- broj predstavlja jedinstven niz od jedne ili više digitalnih jedinica, obično sledi nakon operacija kao što su plus ili minus;
- simbol predstavlja pismo od nula ili nekoliko karaktera (bez spejs prostora); i
- lista predstavlja identični par zagrada, koja sadrži nekoliko izraza u sebi, mada može biti i prazna.
Decimalni brojevi, karakteri malih i velikih slova, i zagrade predstavljaju simbole terminala.
Slede primeri dobro formiranih znakova u ovoj gramatici : '12345
', '()
', '(a b c232 (1))
'
Kompleksna gramatika
[uredi | uredi izvor]Gramatika mora specifikovati programski jezik koji se može klasifikovati prema svojoj poziciji u hijerarhiji Čomskog. Frazna gramatika većine programskih jezika se može specifikovati koristeći "Tip-2" gramatiku, tj. oni su kontekstno slobodne gramatike,[2] iako je celokupni sadržaj sintakse osetljiv (zbog deklaracije promenljivih i okvira programa), pa otuda sledi "Tip-1". Međutim, postoje izuzeci, pa je za neke programske jezike frazna gramatika "Tipa-0" (Tjuring-kompletni programi).
U nekim jezicima kao što su na primer Perl i Lisp, specifikaciju (ili implementaciju) programskog jezika omogućava konstrukcija koja se izvršava tokom faze raščlanjavanja. Osim toga, ovi programski jezici imaju konstruktore koji dozvoljavaju programeru da promni tok ponašanja rašlčanjivanja. Ova kombinacija efektivno zamagljuje razlike između raščlanjivanja i izvršavanja, i čini analizu sintaksne analize neodlučivim problemom u pomenutim programskim jezicima, misleći na to da se faza raščlanjavanja možda nije završila. Na pirmer, u Perlu je moguće izvršavati kod tokom faze raščlanjivanja koristeći BEGIN
funkciju, pa tako prototipovi Perlove funkcije mogu menjati sintaksu interpretacije, a nekada mogu menjati i celokupnu ispravnost sintakse celog koda.[3] Generalno gledano, ovo znači da "Samo Perl može analizirati Perl" (zato što kod mora da se izvrši tokom faze raščlanjavanja, i može da izmeni gramatiku), ili drugačije rečeno "čak i Perl ne može analizirati Perl" (zato što je nepredvidiv). Slično, Lisp makroi koji se uvode defmacro
sintaksom, koji se takođe izvršavaju tokom raščlanjivanja, misleći na to da Lisp kompajler mora imati predstavljen ceo Lisp-ov sistem. Suprotno, C makroi predstavljaju zamenu za stringove, i ne zahtevaju izvršavanje koda.[4][5]
Sintaksa u odnosu na semantiku
[uredi | uredi izvor]Sintaksa programskog jezika opisuje formu ispravnosti programa, ali ne daje nikakve informacije o značenju programa ili razultatima izvršavanja programa. Značenje date kombinacije simbola zavisi od semantike (bilo da je formalna i teško kodirana u referentnoj implementaciji), tj. semantika upravlja značenjem kombinacije simbola. Nisu svi sintatički ispravni programi i semantički ispravni. Mnogi sintatički ispravni programi su ipak loše formirani, i zavise od pravila programskog jezika; i mogu (u zavisnosti od specifikacije programa i vrste implementacije) da daju rezultat kao grešku u prevodu ili izvršavanju. U posebnim slučajevima, takvi programi mogu da ispolje nedefinisano ponašanje. Čak i kada je program dobro definisan i osmišljen u okviru jezika, ipak može imati suprotno značenje od onog značenja koje je programer hteo da program ima.
Korišćenjem prirodnog jezika kao jedan primer, možda neće biti moguće dodeliti značenje gramatički korektnim rečenicama ili rečenicama koje su možda neispravne:
- "Bezbojne zelene ideje brže spavaju" je gramatički dobro formirana rečenica ali nema neko posebno značenje.
- "Džon je oženjeni neženja." je gramatički ispravna rečenca ali ne može biti tačna.
Sledeći C kod je sintatički ispravan, ali obavlja operaciju koja nije semantički definisana (zato što je "p (p)" nulti pokazivač, a operacije "p->real" i "p->im" nemaju značenje):
complex *p = NULL;
complex abs_p = sqrt (p->real * p->real + p->im * p->im);
Još više pojednostavljeno:
int x;
printf("%d", x);
je sintatički ispravan, ali semantički neispravan kod, jer koristi neoznačenu promenljivu.
Vidi još
[uredi | uredi izvor]Brzo poređenje sintaksi u različitim programskim jezicima. Možete pogledati listu "Zdravo, svete!". Evo nekoliko primera:
- Sintaksa programskog jezika Perl
- PHP-ova sintaksa i semantika
- Sintaksa programskog jezika C
- Sintaksa programskog jezika C++
- Sintaksa programskog jezika Java
- Sintaksa programskog jezika JavaSkript
- Sintaksa i semantika programskog jezika Pajton
Reference
[uredi | uredi izvor]- ^ a b Friedman, Daniel P.; Mitchell Wand; Christopher T. Haynes (1992).
- ^ Michael Sipser (1997).
- ^ The following discussions give examples:
- ^ "An Introduction to Common Lisp Macros" Arhivirano na sajtu Wayback Machine (6. avgust 2013).
- ^ "The Common Lisp Cookbook - Macros and Backquote".
Spoljašnje veze
[uredi | uredi izvor]- Različite konstrukcije sintaksi korišćene u programskim jezicima Arhivirano na sajtu Wayback Machine (10. maj 2008)