уторак, 23 априла, 2024
Слободни професионалац

Језичка лабораторија у Ракету (1. део)

Аутор: Лука Хаџи-Ђокић

Прављење језика – зашто?

Програмских језика је много. Може се рећи да је због тога прављење нових језика бескорисно. Међутим, мало који од њих је замишљен као програмски језик опште употребе, него пре као алатка за решавање врло специјализованих проблема – као домен-специфични програмски језик. Они су нашли употребу у разним пољима – од дигиталног писања музике, преко веб-дизајна, до интеракције са роботима.

Да бисмо разумели како компјутерски језици раде, у овом серијалу ћемо покушати да направимо један мали програмски језик.

Шта је језик?

Језик је, у формалном смислу, скуп симбола (алфабет) и скуп правила за прављење речи од тих симбола (граматика). Као што видите, оваква дефиниција се не ограничава само на програмске језике. Пример граматике једног ограниченог дела српског језика би био следећи:

  • Реченица је субјекат, праћен предикатом и објектом и завршава се знаком интерпункције,
  • Субјекат је или “Милан” или “Драган”,
  • Предикат је један од “једе”, “прича”, “прави” и “носи”,
  • Објекат је један од “сарму”, “причу”, “програмски језик” и “ранац”,
  • Знак интерпункције је “.” или “!”

У овако дефинисаном језику, “Милан прави сарму.” је валидна реченица. Међутим, можемо направити и неке бесмислене реченице, као што је “Драган једе програмски језик!”. Ово може довести до забуне. Међутим, треба имати на уму да граматика одређује само синтаксу језика. Када причамо о смислу реченице, дотичемо се семантике, којом додељујемо значење знаковима, речима или целим реченицама.

Граматику програмских језика представљамо на сличан начин, у такозваној Бакус-Науровој форми (о којој ће бити више приче у следећим деловима), док је семантика оно чиме се одређује како се програм извршава.

Како ће радити преводилац

Као што сте можда до сада чули, преводилац или компајлер (енг. compiler) је програм који изворни код у неком програмском језику преводи у машински језик рачунара. Интерпретатори, слично томе, изворни код директно извршавају. Наш језик ћемо преводити у Ракет (енг. Racket), функционални програмски језик из ЛИСП фамилије.

Овај процес биће подељен у 2 дела (који ће бити даље подељени у каснијим текстовима):

1. Читач (енг. reader) ће трансформисати код нашег језика у С-изразе (енг. C-expressions), који су главни облик израза (кода који може да се изврши) у Ракету. Пример једног С-израза је:

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

Такође су врло блиски апстрактним синтаксним стаблима, која служе за представљање кода у општем случају, па се може представити и овако:

2. Експандер (енг. expander) претвара С-израз у валидне Ракет изразе, како би могли да их извршимо. Тако ће претходни С-израз бити трансформисан у

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

или у облику стабла:

које се може израчунати, и резултат је 42.

Мали увод у Ракет

Како би пратили наредне текстове, потребно је да инсталирате Ракет и упознате се са њим. Инсталацију можете наћи на следећем линку, или у у софтверским центрима ваше дистрибуције. Ова инсталација садржи

  • ракет (енг. racket) – преводилац,
  • рако (енг. raco), менаджер пакета за ракет, као и
  • ДрРакет (енг. DrRacket), интерактивно развојно окружење (енг. interactive development environment или IDE).

Окружење ДрРакет-а подељено је на 2 дела: текстуални едитор (горе) и такозвани РЕПЛ, петљу читања, евалуације и исписивања (енг. Read Eval Print Loop), интерактивни Ракет интерпретатор (доле). На почетку Ракет програма отвореног у текстуалном едитору стоји линија

[code]#lang racket[/code]

Она омогућава програмеру да одреди језик којим пише, тј. да одреди читач и експандер које ће ракет преводилац користити при превођењу програма. За почетак, можете се играти у РЕПЛ-у:

[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
#<procedure>
> (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]

У наредним текстовима ћемо, док правимо наш језик, наићи на неке занимљивости Ракета, али и функционалних програмских језика уопште. Али ако не можете да чекате, кратак увод можете наћи овде.

Наставак