nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 25.09.2021.

trejler_dokument Jezici: JavaScript

trejler_teg_narandzasti Težina: 7/10

JavaScript
regularni izrazi
regex
optimizacija
strukture podataka
lista
obrada teksta
niske
tutorijali
saveti
zanimljivosti

Tema: Syntax highlighter

Kako napraviti syntax highlighterTutorijal - Uklanjanje komentara iz programskog kodaSyntax highlighter - Regularni izrazi u JavaScript-u

Povezani članci

Postfiksna notacija - kako računari računaju?Shunting Yard - Implementacija - 1. deo - Prevođenje izraza iz infiksnog u postfiksni zapisRegularni izrazi - napredna pretraga tekstaUvod u PythonŠablonske niske u programskim jezicimaKlase složenosti algoritama (O notacija)Strukture podatakaIzbor prvog programskog jezikaASCII, Unicode i UTF - Predstavljanje znakova na računarimaUNIX Time - Predstavljanje datuma i vremena na računarimaGNU/Linux - 3. deo – Napredne komande
Svi članci
In order to understand recursion, one must first understand recursion
Anonymous

Tutorijal - Prepoznavanje algebarskih izraza u programskim kodovima

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Saveti

Uvod

U prošlom članku nagovestili smo da primena algoritma koji prepoznaje algebarske izraze predstavlja nepraktičan način za razvrstavanje algebarskih izraza i regularnih izraza (koji su zapisani preko regex literal sintakse u JavaScript-u), ali, sa druge strane, jasno je da su algoritmi za prepoznavanje algebarskih izraza prilično zanimljivi sami po sebi (i pri tom predstavljaju dobru vežbu za čitaoce koji u nekom trenutku planiraju da se upuste u samostalnu implementaciju prevodioca za određeni programski jezik).

U procesu tumačenja algebarskih izraza (pri prevođenju programskih kodova i sl), tipovi podataka igraju važnu ulogu. Na primer, u naredbi: a = b + c; - ukoliko promenljiva a nije brojčanog tipa - naredba se ne može izvršiti, a ako bilo koja od preostale dve promenljive takođe nije brojčanog tipa - izraz b + c ne može predstavljati algebarski izraz (već možda predstavlja naredbu za spajanje niski, ili (jednostavno), izraz koji ni u kom smislu nije pravilno formatiran). *

Međutim, u članku pred nama, nećemo se (ipak) baviti prevođenjem programskog koda u pravom smislu reči, odnosno, proveravaćemo samo da li su tokeni ispravni u sintaksnom smislu i - što je važnije - da li su poređani tako da tvore algebarske izraze (koji bi se 'inače' dali interpretirati, ukoliko bi tokeni koji označavaju promenljive bili povezani sa prikladnim vrednostima i sl).

Među tokenima lako možemo prepoznati identifikatore i brojčane konstante, ali, na samom početku, najveći izazov predstavljaće prepoznavanje poziva funkcija. **

Prepoznavanje (poziva) funkcija u programskom kodu, odnosno - razlikovanje identifikatora funkcija od identifikatora promenljivih (tj. operanada), *** može delovati relativno komplikovano, međutim, uz dobro poznavanje algoritma Shunting Yard i uz malo promišljanja, zadatak postaje znatno lakši ....

* Prvi "nivo provere" - ustanoviti da li je promenljiva sa leve strane izraza uopšte definisana i da li tip promenljive dozvoljava zapis brojčanih vrednosti - zadatak je za parser (što je tema kojoj ćemo, više nego verovatno, u nekom trenutku posvetiti članak).

Međutim, u ovom članku (kao što smo već nagovestili), smatraćemo da su promenljive uredno definisane, odnosno, obratićemo pažnju samo na format izraza u opštem smislu.

** Zadatak parsera takođe bi bio ('inače'): prepoznati da li je funkcija pravilno definisana (definisana uopšte i definisana tako da vraća brojčane vrednosti).

*** Prikazaćemo postupak prepoznavanja identifikatora promenljivih i funkcija, a prepoznavanje brojčanih konstanti je nešto što ćemo ostaviti čitaocima, kao svojevrstan "lakmus test" koji može pokazati nečiju spremnost da se uhvati ukoštac sa nešto ozbiljnijim zadacima.

Algoritam

Za razliku od prethodnog članka, u kome smo prvo razmatrali različite neefikasne algoritme (pre nego što smo se usmerili na efikasni postupak za prepoznavanje regularnih izraza), ovoga puta ćemo se odmah usmeriti na efikasni postupak (bez "rešavanja niza zagonetki od kojih je svaka komplikovanija od prethodne"), to jest, usmerićemo se na jedan od efikasnih postupaka. *

U najprostijem smislu, ideja je sledeća: ako se određeni izraz (tj. izdvojeni deo koda koji se proverava), posle pretvaranja u postfiksni oblik može interpretirati tako da se dobije korektan rezultat - može se smatrati da je izraz pravilno formatiran, odnosno - može se smatrati da niz tokena predstavlja algebarski izraz.

* Mnogo šta moglo bi se izvesti uz direktno korišćenje ideja za proveru ispravnosti infiksnih i postfiksnih izraza, u toku prevođenja infiksnog izraza u postfiksni oblik (o čemu smo već diskutovali), ali, ovoga puta, s obzirom na to da se ionako bavimo zanimljivim algoritmima na pomalo eklektičan način, želimo da razradimo nekoliko novih ideja.

Pre nego što se 'bacimo na posao', podsetićemo se ukratko na drugu etapu algoritma Shunting Yard (nešto kasnije, podsetićemo se i na prvu etapu).

Koristićemo kao primer postfiksni izraz ab- (koji je nastao prevođenjem infiksnog izraza a - b), pri čemu će identifikatori a i b biti zamenjeni vrednostima promenljivih (smatraćemo da važi a = 2 i b =1):

Prepoznavanje algebarskih izraza 01
Slika 1. - Shunting Yard - Računanje vrednosti izraza - korak #1.

Prva dva koraka podrazumevaju da se obe vrednosti smeste na stek (jedna za drugom):

Prepoznavanje algebarskih izraza 02
Slika 2. - Shunting Yard - Računanje vrednosti izraza - korak #2.

Pri pojavi operatora -, dva operanda sa vrha steka - uklanjaju se sa steka, i smeštaju se na pomoćne memorijske lokacije ....

Prepoznavanje algebarskih izraza 03
Slika 3. - Shunting Yard - Računanje vrednosti izraza - korak #3.

.... računa se vrednost izraza ....

Prepoznavanje algebarskih izraza 04
Slika 4. - Shunting Yard - Računanje vrednosti izraza - korak #4.

.... i potom se izračunata vrednost vraća nazad na stek:

Prepoznavanje algebarskih izraza 05
Slika 5. - Shunting Yard - Računanje vrednosti izraza - korak #5.

Na kraju, pošto su potrošeni svi tokeni iz izraza, proverava se da li se na steku nalazi tačno jedan operand, i - ako jeste tako (u primeru koji razmatramo, jeste) - izraz je pravilno formatiran, a vrednost izraza se nalazi na vrhu steka.

Međutim, bitno je primetiti sledeće: postupak je u idejnom smislu isti - bez obzira na tip operacije.

Naravno, u slučaju operacije deljenja ('inače', u tipičnim situacijama), mora postoji mehanizam koji sprečava pokušaj deljenja nulom, ali, takvu proveru - u kontekstu ovog članka (s obzirom na to da nećemo zapravo računati vrednosti izraza) - možemo zanemariti.

U bilo kom od četiri slučaja:

  • sa vrha steka se preuzimaju dva operanda
  • obavlja se operacija
  • na stek se vraća jedan operand

Shodno navedenom, i budući da drugu etapu algoritma Shunting Yard koristimo samo da bismo ustanovili "da li se vrednost izraza može izračunati", a ne - da bi vrednost zapravo bila izračunata (to jest, budući da je samo potrebno ustanoviti da li je niz tokena poređan tako da tvori 'potencijalni algebarski izraz') - u praktičnom smislu se može zanemariti sadržaj operanada i tip operacije - i potom se svi tokeni mogu svesti na jedan od dva moguća tokena:

  • token a - koji predstavlja apstrakciju za (bilo koji/svaki) "operand"
  • token + - koji predstavlja apstrakciju za (bilo koji/svaki) "operator"

Da ponovimo još jednom: u apstraktnom/opštem smislu (u situaciji kada je potrebno ustanoviti samo da li je izraz pravilno formatiran), bitno je samo da li - pri pojavi svakog operatora - sa steka mogu biti preuzeta dva operanda (posle čega se na stek "vraća" * samo jedan operand), i da li na kraju - pošto se postupak ponovi za sve operatore - na steku ostaje samo jedan operand.

* Videćemo kasnije da se procedura "računanja vrednosti" može dodatno pojednostaviti u implementaciji.

Razmotrićemo i jedan konkretan primer.

Shodno svemu što smo do sada naveli, sledeći infiksni izraz ....

		
(a + b) * c / (d - e)
		
	
Slika 6. - Primer izraza u infiksnoj notaciji.

.... može se prvo svesti na odgovarajući postfiksni oblik ....

		
ab+c*de-/
		
	
Slika 7. - Izraz sa slike #6 - pretvoren u postfiksni oblik.

.... nakon čega se može svesti na 'apstraktni' oblik:

		
aa+a+aa++
		
	
Slika 8. - Izraz sa slike #7 - sveden na apstraktni oblik (operandi su zamenjeni apstraktnim tokenom "a", a operatori, apstraktnim tokenom "+").

Na kraju, 'apstraktni postfiksni izraz' može se (u četiri koraka) svesti na jedan token "a" ....

		
=> a a + a + a a + +
=> a a + a a + +
=> a a a + +
=> a a +
=> a
		
	
Slika 9. - Rešavanje početnog izraza (koji je preveden u "apstraktni" oblik).

.... što ukazuje na to da je početni izraz (bio) pravilno formatiran.

Apstraktni oblik izraza se (naravno) ne može više koristiti za računanje vrednosti prvobitnog izraza, ali, sasvim dobro ukazuje na redosled izvršavanja operacija i (na kraju) na ispravnost izraza - a to je zapravo jedino što smo želeli da ustanovimo preko prikazanog postupka.

Prepoznavanje različitih identifikatora u izrazu

Prethodna situacija u kojoj smo kao tokene koristili obične znakove (jedan znak - jedan token), bila je jednostavna za razmatranje, međutim, pravi izrazi su komplikovaniji:

		
let a = b1 * c1.v / (f1("+", d, e) + 19.58);
		
	
Slika 10. - Naredba koja sadrži izraz koji će biti korišćen kao glavni primer u članku.

Prikazani izraz (koji ćemo koristiti kao novi/glavni primer), sam po sebi nije posebno zanimljiv, ali, sadrži sve elemente koje treba uzeti u obzir:

  • identifikatore promenljivih - b1
  • objekte sa pripadajućim poljima - c1.v
  • pozive funkcija - f1("+", d, e)
  • brojčane konstante - 19.58

Primetili ste da u naslovu poglavlja stoji termin "identifikator" (a ne "token"), što znači da vama prepuštamo da sami implementirate algoritam za prepoznavanje operatora i brojčanih konstanti.

Međutim, da vas ne bismo "ostavili na cedilu", daćemo na kraju članka nekoliko korisnih napomena koje vam u svemu navedenom mogu pomoći.

Pre svega, smatraćemo da su čitaoci (koje zanima tema članka), u stanju da iz cele naredbe izdvoje deo koji (potencijalno) predstavlja izraz ....

		
b * c1.v / (f1("+", c, d) + 19.58)
		
	
Slika 11. - Izraz koji je izdvojen iz naredbe sa prethodne slike.

.... i da podele izraz na tokene. *

Implementacija zavisi od toga da li (već) radite sa tokenima (ako je izraz prošao kroz lekser), ili ("baš") sa tekstom.

U drugom slučaju, regularni izraz koji traži razmake, tabove, znake operacija, zagrade i zareze, poslužiće sasvim dobro.

* Dakle, prilično je lako prepoznati 'potencijalni' algebarski izraz (posle čega ostaju provere o kojima diskutujemo u većem delu članka).

U svakom slučaju, "ulazna vrednost" (u nastavku, u glavnom algoritmu koji proučavamo), biće sledeća lista tokena:

		
/* ----- */
b
/* ----- */
*
/* ----- */
c1.v
/* ----- */
/
/* ----- */
(
/* ----- */
f1
/* ----- */
(
/* ----- */
"+"
/* ----- */
,
/* ----- */
c
/* ----- */
,
/* ----- */
d
/* ----- */
)
/* ----- */
+
/* ----- */
19.58
/* ----- */
)
/* ----- */
		
	
Slika 12. - Lista tokena koja je nastala tokenizacijom izraza sa slike #11.

Identifikatori počinju znakovima abecede ili podvučenim crtama, brojevi počinju ciframa ili tačkom (po čemu ih možemo prepoznavati), a ostali tokeni su samostalni.

Konačno, sve se svodi na pravilno tumačenje zagrada.

Razlikovanje zagrada koje pripadaju funkcijama od ostalih zagrada

U osnovnom tehničkom smislu (to jest, u smislu pojavnog oblika), token zagrade koja pripada funkciji (zagrada sa parametrima ili argumentima), nije naravno različit od tokena (slobodne/samostalne) zagrade koja se inače koristi u izrazima, što znači da je glavni zadatak - ustanoviti kriterijum po kome se prepoznaje kontekst u kome se zagrade pojavljuju.

U prošlom članku, kada smo govorili o prepoznavanju regularnih izraza u naredbama, ustanovili smo da se regularni izrazi ne mogu pojavljivati posle identifikatora.

Ovoga puta situacija je "obrnuta", to jest: zagrade koje nas (više) zanimaju - one koje pripadaju funkcijama - pojavljuju se upravo posle identifikatora.

Pri interpretaciji izraza potrebno je voditi računa o tipu prethodnog tokena, i - ako je pri pojavi otvorene zagrade prethodni token bio identifikator - može se smatrati da zagrada pripada funkciji, zbog čega treba 'podići' kontekst čitanja i zanemarivati sve tokene, sve dok se ne naiđe na odgovarajuću zatvorenu zagradu.

Kao što znamo, zanemaruju se (naravno) i same zagrade.

Preko navedenog postupka početni izraz se svodi na sledeći oblik:

		
b * c1.v / (f1 + 19.58)
		
	
Slika 13. - Izraz koji proučavamo, sveden na 'relevantne' tokene (tj. tokene koji striktno predstavljaju jednu od sledeće tri kategorije tokena: operatore, identifikatore promenljivih, ili brojeve).

Otvorena zagrada, u paru zagrada koje su ostale, pojavljuje se posle operatora, i zato je ostala u izrazu (baš onako 'kako i treba da bude').

Postupak preko koga smo (ceo) poziv funkcije sveli na jedan token, može zbuniti neke od čitalaca (verujemo da je tako), i stoga ćemo dodatno pojasniti o čemu se radi (nakon čega ćemo detaljno proći kroz primer tumačenja celog početnog izraza).

U "pravom" algoritmu za računanje vrednosti izraza, pojava identifikatora promenljivih nije ništa drugo nego poziv da se vrednosti promenljivih pronađu i uvrste u izraz umesto identifikatora, kao na primer u sledećem izrazu, u kome poslednja naredba ....

		
let a = 10, b = 15, c;
c = a + b;
		
	
Slika 14. - Primer jednostavne naredbe dodele.

.... praktično postaje ....

		
c = 10 + 15;
		
	
Slika 15. - Primer izvršavanja naredbe dodele sa prethodne slike.

Nije onda teško razumeti da poziv funkcije (u različitim izrazima), praktično pokreće izvršavanje funkcije, posle čega se pojava funkcije u izrazu - menja vrednošću, koja se dobija izvršavanjem funkcije.

Ako se privremeno usmerimo na drugi primer, možemo primetiti da poziv funkcije sabiranje u sledećem kodu ....

		
function sabiranje(a, b) {
	return a + b;
}

let x = 10, y = 15;
let r = sabiranje(x, y);
		
	
Slika 16. - Primer jednostavne funkcije.

.... praktično postaje ....

		
let r = sabiranje(10, 15)
		
	
Slika 17. - Primer izvršavanja funkcije 'sabiranje' (sa prethodne slike).

.... odnosno, posle izvršavanja naredbe return a + b u funkciji sabiranje: *

		
let r = 25;
		
	
Slika 18. - Rezultat izvršavanja funkcije 'sabiranje'.

* Promenljivama a i b prethodno su dodeljene vrednosti 10 i 15.

U apstraktnom smislu, izvršavanje funkcije f1 (u glavnom primeru u članku), može se svesti: prvo na identifikator f1, a zatim na token "a" (što je konvencija koju smo usvojili za označavanje "operanda ili vrednosti").

Pogledajmo kompletan primer koji ilustruje pretvaranje početnog izraza.

Primer tumačenja izraza

Pri tumačenju izraza, za početak se koristi prva etapa algoritma Shunting Yard - prebacivanje infiksne notacije u postfiksni oblik, što (kao što je poznato od ranije), podrazumeva upotrebu pomoćnog steka i reda za čekanje (u koji se smeštaju obrađeni tokeni).

Nakon prve etape, sledi ispitivanje postfiksnog izraza (što će biti prikazano u zasebnom odeljku, nakon prikaza postupka za formiranje sadržaja reda za čekanje).

Kreiranje postfiksnog zapisa

U izrazu koji razmatramo ....

		
b * c1.v / (f1("+", c, d) + 19.58)
		
	
Slika 19. - Izraz koji koristimo kao primer.

.... prvi token (b) ....

		
b  *  c1.v  /  (  f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ ]
STEK:     [ ]
KONTEKST: [ 0 ]
		
	
Slika 20. - Tumačenje izraza preko algoritma Shunting Yard - korak #1.

.... prepoznaje se kao operand i smešta se u red ....

		
*  c1.v  /  (  f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" ]
STEK:     [ ]
KONTEKST: [ 0 ]
		
	
Slika 21. - Tumačenje izraza preko algoritma Shunting Yard - korak #2.

Drugi token (*), prepoznaje se kao operator i smešta se na stek ....

		
c1.v  /  (  f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" ]
STEK:     [ "*" ]
KONTEKST: [ 0 ]
		
	
Slika 22. - Tumačenje izraza preko algoritma Shunting Yard - korak #3.

Treći token unapred je prepoznat kao c1.v, ali, u red se prenosi (samo) "c1":

		
/ (  f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" , "c1" ]
STEK:     [ "*" ]
KONTEKST: [ 0 ]
		
	
Slika 23. - Tumačenje izraza preko algoritma Shunting Yard - korak #4.

Ukoliko je (u prikazu koda/"inače"), potrebno označiti operator pristupa poljima objekta (.) i polje (v1) - a najverovatnije ćete takav postupak primenjivati u praktičnim situacijama (van konteksta "školskih" članaka), potrebno je predvideti da se, pri podeli tokena, znak tačka (.), tretira kao specijalni token, i u tom slučaju biće izdvojena tri tokena: c1, . i v (umesto jednog tokena: c1.v).

U kontekstu provere algebarskog izraza, dovoljno je samo da se zanemare svi tokeni osim prvog, s tim da bi najpraktičnije bilo da se odmah u red za čekanje premesti token a.

Ipak (zarad bolje preglednosti), do kraja članka će ostati "početni" tokeni, sve dok ne dođemo do ispitivanja postfiksnog izraza, a implementaciju u svakom slučaju ostavljamo čitaocima (kao samostalni mini-projekat), i nastavljamo sa tumačenjem izraza ....

Četvrti token / ima isti prioritet kao operator na vrhu steka (*), i stoga se prvo operator * premešta u red, a potom se operator / smešta na stek:

		
f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" , "c1" , "*" ]
STEK:     [ "/" ]
KONTEKST: [ 0 ]
		
	
Slika 24. - Tumačenje izraza preko algoritma Shunting Yard - korak #5.

Peti token (, prepoznaje se kao obična zagrada (zagradi prethodi operator), i stoga se zagrada direktno smešta na stek ....

		
f1  (  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" , "c1" , "*" ]
STEK:     [ "/" , "(" ]
KONTEKST: [ 0 ]
		
	
Slika 25. - Tumačenje izraza preko algoritma Shunting Yard - korak #6.

Šesti token (f1) prepoznaje se kao identifikator i smešta se u red.

		
(  \"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" , "c1" , "*" , "f1" ]
STEK:     [ "/" , "(" ]
KONTEKST: [ 0 ]
		
	
Slika 26. - Tumačenje izraza preko algoritma Shunting Yard - korak #7.

Sedmi token ( - otvorena zagrada koja pripada funkciji (što algoritam prepoznaje po tome što zagradi prethodi identifikator, a ne operator) - sama po sebi se odbacuje, i takođe uvodi čitač u režim odbacivanja tokena (sve dok se ne pojavi odgovarajuća zatvorena zagrada) ....

		
\"  +  \"  ,  c  ,  d  )  +  19.58  )
RED:      [ "b" , "c1" , "*" , "f1" ]
STEK:     [ "/" , "(" ]
KONTEKST: [ 0 , 3 ]
		
	
Slika 27. - Tumačenje izraza preko algoritma Shunting Yard - korak #8.

.... i stoga će tokeni: \", +, \", ,, c, ,, d i, na kraju, token ) - biti odbačeni, posle čega se kontekst * vraća na 0.

		
+  19.58  )
RED:      [ "b" , "c1" , "*" , "f1" ]
STEK:     [ "/" , "(" ]
KONTEKST: [ 0 ]
		
	
Slika 28. - Tumačenje izraza preko algoritma Shunting Yard - korak #9.

* Usput: promenljiva kontekst je u funkcionalnom smislu takođe stek (kao i ranije), ali, ovoga puta smo joj dali drugi naziv da bismo je razlikovali od glavnog steka, koji koristimo za algoritam Shunting Yard.

Sledeći token (+) prepoznaje se kao operator i smešta se na stek.

		
19.58  )
RED:      [ "b" , "c1" , "*" , "f1" ]
STEK:     [ "/" , "(" , "+" ]
KONTEKST: [ 0 ]
		
	
Slika 29. - Tumačenje izraza preko algoritma Shunting Yard - korak #10.

Na kraju, token 19.58 se smešta u red ....

		
)
RED:      [ "b" , "c1" , "*" , "f1" , "19.58" ]
STEK:     [ "/" , "(" , "+" ]
KONTEKST: [ 0 ]
		
	
Slika 30. - Tumačenje izraza preko algoritma Shunting Yard - korak #11.

.... a token ) - zatvorena zagrada, povlači pražnjenje steka u red, sve do prve otvorene zagrade:

		
RED:      [ "b" , "c1" , "*" , "f1" , "19.58" , "+" ]
STEK:     [ "/" ]
KONTEKST: [ 0 ]
		
	
Slika 31. - Tumačenje izraza preko algoritma Shunting Yard - korak #12.

Budući da u izrazu nema više tokena (zatvorena zagrada je ujedno bila i poslednji token u izrazu), pravila nalažu da se tokeni * iz steka premeste u red (jedan po jedan):

		
RED:      [ "b" , "c1" , "*" , "f1" , "19.58" , "+" , "/" ]
STEK:     [ ]
KONTEKST: [ 0 ]
		
	
Slika 32. - Tumačenje izraza preko algoritma Shunting Yard - korak #13.

Red je formiran, i može se pristupiti ispitivanju postfiksnog izraza. **

* U praktičnom smislu, premešten je samo jedan token (koji se 'zatekao' na steku).

** Ne možemo reći da će algoritam "računati vrednost", iako se praktično primenjuje postupak za računanje vrednosti. :)

Ispitivanje postfiksnog izraza

Za tumačenje izraza, koristićemo drugu etapu algoritma Shunting Yard.

Prema konvencijama koje smo utvrdili na početku, tokene početnog izraza (koji su smešteni u red za čekanje) ....

		
RED:  [ "b" , "c1" , "*" , "f1" , "19.58" , "+" , "/" ]
STEK: [ ]
		
	
Slika 33. - Izraz koji koristimo kao primer - sveden na 'relevantne' tokene (tj. tokene koji predstavljaju: operator, operand ili broj).

.... prvo treba svesti na apstraktni oblik:

		
RED:  [ "a" , "a" , "+", "a" , "a" , "+" , "+" ]
STEK: [ ]
		
	
Slika 34. - Izraz koji koristimo kao primer - prethodno sveden na 'relevantne' tokene, koji su dalje svedeni na 'apstraktne' tokene (token "a" simbolično predstavlja operand, a token "+" predstavlja operator).

Kao što smo takođe ranije nagovestili, u implementaciji je zapravo najpraktičnije da se regularni tokeni prebace u apstraktni oblik još tokom prve faze izvršavanja algoritma Shunting Yard (ali smatrali smo da dosadašnji primeri deluju preglednije ukoliko se 'apstraktni' i regularni tokeni ne mešaju).

Budući da je sve spremno, može se započeti sa tumačenjem izraza.

Sve do pojave operatora, tokeni iz reda se prebacuju na stek (jedan za drugim).

		
RED:  [ "+", "a" , "a" , "+" , "+" ]
STEK: [ "a" , "a" ]
		
	
Slika 35. - Tumačenje apstraktnog izraza - korak #1.

Kao što znamo od ranije, pojava operatora, povlači preuzimanje dva operanda sa steka ....

		
RED:  [ "a" , "a" , "+" , "+" ]
STEK: [ ]
OP1:  "a"
OP2:  "a"
		
	
Slika 36. - Tumačenje apstraktnog izraza - korak #2.

.... nakon čega se obavlja operacija, a rezultat se vraća na stek.

Međutim, u praktičnom smislu, budući da se sa steka preuzimaju dva tokena "a" i vraća jedan, u implementaciji je dovoljno proveriti da li na steku postoje (bar) dva operanda i - ako je navedeni uslov zadovoljen - jednostavno sa steka treba ukloniti jedan operand (2 - 1 = 1).

		
RED:  [ "a" , "a" , "+" , "+" ]
STEK: [ "a" ]
		
	
Slika 37. - Tumačenje apstraktnog izraza - korak #3.

U nastavku, ponovo se operandi sa leve strane reda 'slivaju' na stek (sve do pojave operatora) ....

		
RED:  [ "+" , "+" ]
STEK: [ "a" , "a" , "a" ]
		
	
Slika 38. - Tumačenje apstraktnog izraza - korak #4.

.... nakon čega ponovo sledi računanje:

		
RED:  [ "+" ]
STEK: [ "a" ]
OP1:  "a"
OP2:  "a"
		
	
Slika 39. - Tumačenje apstraktnog izraza - korak #5.

Ponovo se prvo proverava da li se operator može upariti sa dva operanda na steku, i - pošto je uslov zadovoljen - obavlja se "računanje" a rezultat se "vraća" na stek (ali, prema ranijem 'dogovoru': zapravo nema "računanja" i skripta će jednostavno ukloniti jedan operand sa steka) ....

		
RED:  [ "+" ]
STEK: [ "a" , "a" ]
		
	
Slika 40. - Tumačenje apstraktnog izraza - korak #6.

Na kraju, ceo postupak se ponavlja još jednom:

		
RED:  [ ]
STEK: [ "a" ]
		
	
Slika 41. - Tumačenje apstraktnog izraza - završetak.

Budući da su tokeni iz izraza (tj. iz reda za čekanje), potrošeni, i budući da se na steku nalazi tačno jedan operand (koji bi inače bio rezultat računanja), može se zaključiti da je izraz pravilno interpretiran, odnosno, može se zaključiti da je uspešno prepoznat algebarski izraz.

Ideje za dalje unapređenje

U članku smo "pretresli" dobar deo onoga što je potrebno obaviti, ali, nismo prikazali "sve" (jednostavno rečeno, želimo da potaknemo čitaoce da samostalno istražuju određene delove implementacije).

U kontekstu mogućih unapređenja, pomenućemo dve ideje:

  • prepoznavanje kompleksnijih operatora (++, /= i sl)
  • prepoznavanje brojčanih konstanti (ideja koju smo još ranije nagovestili)

Kao što rekosmo, nećemo otkrivati baš sve i ostavićemo vam da sami smislite postupak za prepoznavanje operatora koji se zapisuju uz upotrebu dva znaka (ili više znakova), kao i postupak za prepoznavanje brojčanih konstanti.

U svemu navedenom, razmislite o sledećem:

  • postoji li suštinska razlika u tumačenju unarnih i binarnih operatora (u smislu interakcije sa operandima)?
  • da li (možda) pravila za definisanje identifikatora promenljivih pomažu u prepoznavanju brojčanih konstanti? *

* Napomena: pomažu. :)

Na kraju, postavlja se i pitanje kako prepoznavati ternarni operator ? (možda smo mi "zaboravili" na ternarni operator, ali, "ternarni operator nije zaboravio nas").

Zanimljive teme za razmišljanje i razradu.

Za kraj ....

Ovim člankom zaključujemo mini-serijal o obradi teksta (odnosno, obradi programskog koda), koji smo pokrenuli člankom o samostalnoj implementaciji skripte za označavanje sintaksnih elemenata.

Ako želite da dodatno istražujete oblast tumačenja programskog koda, samostalna izrada parsera je i dalje osnovna ideja koju vredi pratiti.

Kao što smo već pominjali, projektovanje i implementacija parsera svakako nije "lagan" projekat, pogotovo ne za početnike (pri čemu, ovoga puta, pojam "početnik" vezujemo za nekoga ko do sada nije pisao programe kakve prikazujemo u poslednjih nekoliko članaka, a ne za nekoga ko doslovno tek počinje da se bavi programiranjem).

Međutim, projekat nije ni "najteži mogući" (ni iz daleka), pa - ako imate dovoljno slobodnog vremena i dobre volje :) ....

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > Tutorijal - Prepoznavanje algebarskih izraza u programskim kodovima
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt