petak, 27 decembra, 2024
Slobodni profesionalac

Jezička laboratorija u Raketu (1. deo)

Autor: Luka Hadži-Đokić

Pravljenje jezika – zašto?

Programskih jezika je mnogo. Može se reći da je zbog toga pravljenje novih jezika beskorisno. Međutim, malo koji od njih je zamišljen kao programski jezik opšte upotrebe, nego pre kao alatka za rešavanje vrlo specijalizovanih problema – kao domen-specifični programski jezik. Oni su našli upotrebu u raznim poljima – od digitalnog pisanja muzike, preko veb-dizajna, do interakcije sa robotima.

Da bismo razumeli kako kompjuterski jezici rade, u ovom serijalu ćemo pokušati da napravimo jedan mali programski jezik.

Šta je jezik?

Jezik je, u formalnom smislu, skup simbola (alfabet) i skup pravila za pravljenje reči od tih simbola (gramatika). Kao što vidite, ovakva definicija se ne ograničava samo na programske jezike. Primer gramatike jednog ograničenog dela srpskog jezika bi bio sledeći:

  • Rečenica je subjekat, praćen predikatom i objektom i završava se znakom interpunkcije,
  • Subjekat je ili “Milan” ili “Dragan”,
  • Predikat je jedan od “jede”, “priča”, “pravi” i “nosi”,
  • Objekat je jedan od “sarmu”, “priču”, “programski jezik” i “ranac”,
  • Znak interpunkcije je “.” ili “!”

U ovako definisanom jeziku, “Milan pravi sarmu.” je validna rečenica. Međutim, možemo napraviti i neke besmislene rečenice, kao što je “Dragan jede programski jezik!”. Ovo može dovesti do zabune. Međutim, treba imati na umu da gramatika određuje samo sintaksu jezika. Kada pričamo o smislu rečenice, dotičemo se semantike, kojom dodeljujemo značenje znakovima, rečima ili celim rečenicama.

Gramatiku programskih jezika predstavljamo na sličan način, u takozvanoj Bakus-Naurovoj formi (o kojoj će biti više priče u sledećim delovima), dok je semantika ono čime se određuje kako se program izvršava.

Kako će raditi prevodilac

Kao što ste možda do sada čuli, prevodilac ili kompajler (eng. compiler) je program koji izvorni kod u nekom programskom jeziku prevodi u mašinski jezik računara. Interpretatori, slično tome, izvorni kod direktno izvršavaju. Naš jezik ćemo prevoditi u Raket (eng. Racket), funkcionalni programski jezik iz LISP familije.

Ovaj proces biće podeljen u 2 dela (koji će biti dalje podeljeni u kasnijim tekstovima):

1. Čitač (eng. reader) će transformisati kod našeg jezika u S-izraze (eng. C-expressions), koji su glavni oblik izraza (koda koji može da se izvrši) u Raketu. Primer jednog S-izraza je:

[code](saberi (pomnozi 4 10) 2)[/code]

Takođe su vrlo bliski apstraktnim sintaksnim stablima, koja služe za predstavljanje koda u opštem slučaju, pa se može predstaviti i ovako:

2. Ekspander (eng. expander) pretvara S-izraz u validne Raket izraze, kako bi mogli da ih izvršimo. Tako će prethodni S-izraz biti transformisan u

[code](+ (* 4 10) 2)[/code]

ili u obliku stabla:

koje se može izračunati, i rezultat je 42.

Mali uvod u Raket

Kako bi pratili naredne tekstove, potrebno je da instalirate Raket i upoznate se sa njim. Instalaciju možete naći na sledećem linku, ili u u softverskim centrima vaše distribucije. Ova instalacija sadrži

  • raket (eng. racket) – prevodilac,
  • rako (eng. raco), menadžer paketa za raket, kao i
  • DrRaket (eng. DrRacket), interaktivno razvojno okruženje (eng. interactive development environment ili IDE).

Okruženje DrRaket-a podeljeno je na 2 dela: tekstualni editor (gore) i takozvani REPL, petlju čitanja, evaluacije i ispisivanja (eng. Read Eval Print Loop), interaktivni Raket interpretator (dole). Na početku Raket programa otvorenog u tekstualnom editoru stoji linija

[code]#lang racket[/code]

Ona omogućava programeru da odredi jezik kojim piše, tj. da odredi čitač i ekspander koje će raket prevodilac koristiti pri prevođenju programa. Za početak, možete se igrati u REPL-u:

[code]> (+ 1 1) ; komentari pocinju sa ";", izrazi su u zagradama
2
> ‘(+ 1 1) ; izrazi su u zagradama, za liste stavljamo ‘ ispred
‘(+ 1 1)
> (define dva 2)
> dva
2
> (let ([dva "Zdravo svete!"]) ; dva ce u okviru
(printf dva)) ; let izraza biti ovaj string
Zdravo svete!
> dva
2
> (lambda (x) (+ x 40)) ; lambda izrazi predstavljaju funkcije
#
> (define funkcija1 ; definisati kao i promenljive
(lambda (x)
(+ x 40)))
> (funkcija1 2)
42
> (define (funkcija2 x) (+ x 40)) ; ekvivalentno gornjoj definiciji
> (funkcija2 2)
42
> (map funkcija2 ‘(1 2 3 4)) ; liste su bitne, pa imaju dosta korisnih funkcija
‘(41 42 43 44)
> (filter even? ‘(1 2 3 4))
‘(2 4)
> (if #t ; #t je true ili tacno, #f je false ili netacno
"tacno" ; sve sto nije #f, smatra se tacnim u kondicionalima
"netacno")
> (cond [(> 2 dva) (error "netacno!")]
[(< 2 dva) (error "opet netacno!")]
[else "tacno!"]) ; kao if else u drugim programskim jezicima
"tacno!"
> (struct boja (crvena zelena plava)) ; strukture
> (define zuta
(boja 0 255 255))
> (boja-plava zuta)
255[/code]

U narednim tekstovima ćemo, dok pravimo naš jezik, naići na neke zanimljivosti Raketa, ali i funkcionalnih programskih jezika uopšte. Ali ako ne možete da čekate, kratak uvod možete naći ovde.

Nastavak