Настављено писање рада

This commit is contained in:
kappa 2019-02-28 12:03:31 +01:00
parent f3d20686db
commit abc0b5c9b8

View file

@ -4,15 +4,17 @@
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[serbianc]{babel}
\usepackage{multicol}
\usepackage{textalpha}
\usepackage{textcase}
\usepackage{comment}
\usepackage{tocloft}
\usepackage{graphicx}
\usepackage[backend=biber]{biblatex}
\addbibresource{lit.bib}
\usepackage{setspace}
\author{Петар Каприш}
\title{Лисп интерпретер}
\title{Прављење LISP интерпретера у C-у}
\begin{document}
\begin{titlepage}
@ -29,7 +31,9 @@
\textbf{Матурски рад из програмирања}
\vspace*{0.5cm}
\begin{spacing}{0.8}
\MakeUppercase{\textbf{\@title}}
\end{spacing}
}
\vspace*{8.9cm}
@ -53,7 +57,9 @@
\section*{Предговор}
Овај рад се бави концепцијом и имплементацијом Ћирилисп програмског језика, минималистичког Лисп дијалекта осмишљеног да се користи на српском језику са ћириличком тастатуром.
Моја главна мотивација за овај рад јесте моје опште интересовање за дизајн и имплементацију програмских језика и наручито за дизајн дијалеката Лиспа. Као додатни својство језика одлучио сам да он буде дефинисан тако да су све његове стандардне форме (као и симболи генерално) описани на српској ћирилици, било ми је интересантно да се уз посао дизајна и имплементације програмског језика, такође (додуше не на претерано захтеван начин) бавим радом на локализацији софтвера.
Моја главна мотивација за овај рад јесте моје опште интересовање за дизајн и имплементацију програмских језика и нарочито за дизајн дијалеката Лиспа. Иако је ова тема помало преопширна за опсег матурског рада одлучио сам да радим на њој јер ми је такође пружала изазов.
Желео бих да се захвалим свом ментору Марку Савићу за све савете и препоруке којима ми је помогао у изради рада и дизајну програма, као и за детаљно прегледање свег кода и овог самог рада.
\newpage
\tableofcontents
@ -61,14 +67,24 @@
\section*{Увод\label{Uvod}}
\addcontentsline{toc}{section}{\protect\numberline{}Увод}
Ћирилисп је програмски језик, дијалекат Лиспа, осмишљен да се његов код пише на српској ћирилици. Има многа својства која би се очекивала од типичног Лисп дијалекта, укључујући типичне методе манипулације листама, комплетан (мада не хигијенски) макро систем, којим се може проширити синтакса језика за жељену употребу.
Ћирилисп је програмски језик, дијалекат Лиспа, осмишљен да се његов код пише на српској ћирилици. Има многа својства која би се очекивала од типичног Лисп дијалекта, укључујући типичне методе манипулације листама, лексички опсег променљивих, комплетан макро систем, којим се може проширити синтакса језика за жељену употребу, као и способност за разломачку аритметику.
У сврху овог матурског рада, направљена је имплементација овог језика, као интерпретер написан у C-у, који између осталог може покренути код у датом језику као извршиву датотеку у било којој Посикс сагласној Љусци.
Интерпретер захтева C компајлер сугласан са C99 стандардом, или више, као и стандардну бибилиотеку сагласну са POSIX стандардом 2001. или касније. До сада је једино тестиран са ГНУ C компајлером 6.3.0 на Девуан ГНУ/Линуксу. MinGW на виндоузу је потврђен да не садржи довољно комплетну C библиотеку да би компајлирао Ћирилисп.
Уколико имате копију сорс кода овог програма, са окружењем способним да га покрене, један \texttt{make install} би требало да компајлира и инсталира цео програм.
Са инсталираним интерпретером можете покренути Ћирилиспово ЧПШП окружење командом \texttt{cirilisp}. Уколико желите да интерпретирате код било којег већ написаног .ћ фајла, можете то учинити командом \texttt{cirilisp -q <име-фајла>}. За покретање оваквог Ћирилисп фајла попут извршивог програма, он мора да започне уз шабенг \texttt{\#!/usr/local/bin/cirilisp -q}, као и да има дозволе за извршавање (што му можете омогућити уз \texttt{chmod} команду), такав програм можете покренути уз просто \texttt{./<име-фајла>}.
Уколико желите да компајлирате овај \LaTeX{} документ, потребна вам је \texttt{texlive} инсталација уз \texttt{biber} и све пакете које документ користи (за шта можете прочитати његов сорс код).
(Напомена: \texttt{babel-serbianc} мора бити верзије строго веће од \texttt{2.2}, у супротном се дешава конфликт са \texttt{biblatex} пакетом)
\newpage
\section{Кратак опис Лисп програмског језика}
\section{Кратак увод и Лисп програмски језик}
Лисп је један од најстаријих програмских језика који је и данас у општој примени. Познат је по својој једноставној и једноличној синтакси, аутоматским системима за управљање меморијом, и моћним макроима који могу проширити његову већ изражајну синтаксу. Управо због своје екстензибилности, и изражајне моћи основа његовог дизајна, Лисп је један од ретких програмских језика који поседује сопствене дијалекте. Постоје стотине Лисп дијалеката, сваки са другачијом сврхом и философијом дизајна. Од више функционалних и декларативних, као Ским или Кложур, до практичнијих и опште-наменских, као Комон Лисп и, па и обласно-специфичних као Емакс-Лисп.
\subsection{Листе и функције}
\subsection{Листе и синтакса}
Лисп је акроним за List Processing (пређивање листа) што указује на најосновнију, и најбитнију сложену структуру података која је доступна у овом језику, наиме листа. Листа се гради помоћу конс ћелија, структура које поседују два показивача (или два сама објекта у зависности од имплементације) који могу показати на било какав податак. Ови показивачи се респективно зову CAR и CDR (Contents of the address part of the register number и Contents of the decrement part of the register number). Листа се уз њих дефинише као скуп конс ћелија где CAR сваке показује на одређени податак, члан листе, а CDR показује на следећу конс ћелију која чини исто као и претходна, све до задње, која показује на посебни тип објекта најчешће зван нил објекат.\parencite{recursive}
\begin{figure}[h!]
\includegraphics[width=\linewidth]{list.png}
@ -76,9 +92,135 @@
\label{list1}
\end{figure}
Слика \label{list1} приказује листу која би се текстуално могла представити као симболички израз \texttt{(1 . (2 . (3 . ())))} или у скраћеној нотацији симболичких израза, \texttt{(1 2 3)}.
Слика \ref{list1} приказује листу која би се текстуално могла представити као симболички израз \texttt{(1 . (2 . (3 . ())))} или у скраћеној нотацији симболичких израза, \texttt{(1 2 3)}. Овиме се може видети да \textit{car} и \textit{cdr} функције, када су примењене на валидну листу, враћају првог члана листе и остатак листе респективно.
Лиспова синтакса је хомоиконична, другим речима он није само способан да прерађује листе, већ је и сам његов код сачињен од листа. Ово чини саму синтаксу језика довољно једноставном да се опише једним примером позива функцијем, који у Лиспу увек бивају следећег облика: \texttt{(функција арг1 арг2 арг3 ...)}, овако изгледа готово свака операција или функција извршена у самом језику, то наравно значи да су неки, нама познати изрази као \texttt{1 / (3 + 4 * 5)}, претворени у, на први изглед, необичне \texttt{(/ 1 (+ 3 (* 4 5)))}. Међутим, управо та чудна униформност читаве синтаксе која није присутна у већини других програмских језика је управо оно што Лиспу омогућава толику изражајну моћ.
\subsection{REPL\label{REPL}}
Један од централних појмова у свакој имплементацији Лиспа јесте такозвани ,,Read, Evaluate, Print Loop'', у овој имплементацији познато као ЧПШП. ЧПШП је најосновнији начин интеракције са већином Лисп система, у питању је командно-линијски интерфејс у којем корисник текстуално уноси програм као низ симболичких израза, који у ,,бесконачној'' петљи учитава симболичке изразе а затим их евалуира (процењује им коначну вредност), и на крају их штампа. На пример горепоменути израз, \texttt{(/ 1 (+ 3 (* 4 5)))}, би био учитан кроз \textit{read} функцију, која би га парсирала и претворила у листу, коју би потом \textit{eval} функција проценила да има резултат \texttt{1/23} (већина имплементација природно подржава разломачку аритметику), након чега би \textit{print} штампала дати број на екрану или терминалу.
\subsection{Функције}
Лисп није нужно функционалан језик и његове функције углавном могу да стварају ,,нуспроизводе'' над остатком програма. Међутим многи дијалекти фаворизују функционалан стил. Функције се врло често дефинишу путем ,,ламбда'' израза, који су у већини дијалеката форме:
\begin{verbatim}
(lambda (arg1 arg2 ...) expr1 expr2 ...)
\end{verbatim}
Овакви ламбда изрази се потом евалуирају и као резултат стварају ,,функција објекат'', који се потом може користити у другим изразима и као коначну вредност враћа евалуирани задњи израз у својој дефиницији (\texttt{exprn}), на пример, израз који би означавао функцију која враћа квадрат унесеног броја би изгледала овако.
\begin{verbatim}
(lambda (x) (* x x))
\end{verbatim}
Док би израз који такву функцију заиста примењује на број (нпр. 5) изгледао овако.
\begin{verbatim}
((lambda (x) (* x x)) 5)
\end{verbatim}
Овај израз би унесен у REPL вратио вредност \texttt{25}.
Пошто мноштво анонимних функција није претерано лепо за гледати у коду, и овако дефинисане анонимне функције немају погодан начин да врше рекурзију већина функција у Лиспу се дефинише везивањем оваквог ламбда израза за има променљиве тј. симбол. Следећи изрази именују и примењују функцију за квадрирање бројева.
\begin{verbatim}
(defun sqr (x) (* x x))
(sqr 6)
\end{verbatim}
(Овај пример је записан у Комон Лиспу)
Да би се показала рекурзивна могућност овако дефинисаних функција, дат је пример Акерманове функције написан у Скиму. Ским користи благо другачију форму за дефинисање функција и других променљивих.
\begin{verbatim}
(define (ack m n)
(cond
((= m 0) (+ n 1))
((and (> m 0) (= n 0)) (ack (- m 1) 1))
((and (> m 0) (> n 0)) (ack (- m 1) (ack m (- n 1))))))
\end{verbatim}
\section{Кратак увод у Ћирилисп}
Ћирилисп је минималистички, претежно функционални дијалекат Лиспа, који је дизајниран и имплементиран у сврху овог матурског рада. Његово главно дефинишуће својство јесте што за конституентне карактере токена, уместо енглеских латиничнихслова из ASCII табеле користи Уникод UTF-8 карактере који описују слова која припадају српској ћирилици.
Интерпретер који служи као имплементација овог језика се може покренути на већ постојећем фајлу или унутар ЧПШ петље, описане у под-одељку \ref{REPL}, начин на који се интерпретер може покренути на оба описана начина је објашњен у одељку Увод.
Ћирилисп садржи неколико основних типова података које корисник може да користи у својим програмима: \texttt{нил}, \texttt{конс}, \texttt{ћелија}, \texttt{број} (реални и разломачки), \texttt{симбол}, \texttt{процедура}, \texttt{булски}, \texttt{ниска}, \texttt{карактер}, и \texttt{грешка}.
Такође су дефинисани ,,крај фајла'' и ,,неодређен'' типови објеката, међутим корисник нема приступ њима и искључиво их користи \texttt{read} функција да би сигнализирала различите случајеве читања израза.
\subsection{Променљиве}
У Ћирилиспу се променљиве дефинишу на ,,Скимолик'' начин уз процедуру \texttt{опиши}, такође су променљиве искључиво лексичког опсега, као и у Скиму. Ово је пример просте дефиниције променљиве:
\begin{verbatim}
(опиши џ (* 37 1))
\end{verbatim}
Током дефиниције, други аргумент \texttt{опиши} процедуре се евалуира, и коначна вредност се везује за променљиву са именом првом аргумента (џ) у локалном опсегу.
Процедуре се могу дефиницати путем ламбда израза на сличан начин. Међутим чешће је (и читљивије) користити другу форму \texttt{опиши}, израза:
\begin{verbatim}
(опиши (квадрат џ) (* џ џ))
\end{verbatim}
Овај израз сама \texttt{опиши} функција преобраћује у:
\begin{verbatim}
(опиши квадрат (ламбда (џ) (* џ џ)))
\end{verbatim}
\subsection{Синтакса описана Read функцијом}\label{readsyntax}
Read функција (кориснику Ћирилиспа доступна као \texttt{читај}) прихвата унесени Ћирилисп израз и парсира га према свом алгоритму учитавања. (Овај алгоритам је претежно дизајниран по узору на Комон Лиспов стандардни алгоритам за \texttt{read} функцију, онакав какав је описан у књизи \textcite{steele84}.
Као резултат следећи синтаксни елементи су уносиви од стране корисника у језик:
\begin{enumerate}
\item Бројеви, који се могу замислити као састављени од низа цифара, или два низа цифара, између којих је разломачка црта или запета:
\texttt{43}, \texttt{2/73}, \texttt{43,4938}, ...
\item Симболи, који се састоје од низа саставних карактера, или почињу са избегнутим карактером и садрже \underline{било какав} карактер до следећег избегнутог карактера:
\texttt{имесимбола}, \texttt{име-><симбола/\_\_?}, \texttt{|СимБОЛ са разМАКОМ и ВЕЛИКИМ и малим словима|}, \texttt{|latinični simbol sa razmakom|},\fontencoding{T1} \texttt{|σύμβολο|}\fontencoding{T2A}.
\item Коментари, који или почињу уз семиколон или шабенг\texttt{(\#!)}, и трају до краја датог реда или почињу уз, \texttt{\#|} секвенцу и трају док се не наиђе на супротну \texttt{|\#} секвенцу карактера:
\begin{verbatim}
; ово је линијски коментар
#! ово је такође линијски коментар
#|ово је вишелинијски
коментар|#
\end{verbatim}
\item Булски објекти: \texttt{\#и} и \texttt{\#л}
\item Карактери: \texttt{\#\textbackslashа}, \texttt{\#\textbackslashновиред}, ...
\item Ниске: \texttt{"\textbackslash"Ово\textbackslash" је ниска, два наводника у овој ниски су избегнути, тако да и наводници могу да се нађу у нискама."}
\item Листе, конс ћелије и ,,окрњене'' листе:
\begin{verbatim}
(ово је листа)
(конс . ћелија)
(ово је окрњена . листа)
\end{verbatim}
\item ,,навод'' оператер, који спречава евалуацију израза на који је примењен:
\begin{verbatim}
'(ова листа се неће евалуирати)
`(неће ни ова)
\end{verbatim}
\end{enumerate}
\newpage
\section{Стандардне процедуре језика}
Већ су поменуте опиши и ламбда специјалне форме, којима се граде и везују имена за процедуре које корисник описује, међутим језик би био бескористан да не поседује сопствене, предефинисане функције за различите сврхе:
\begin{comment}
Следе типови карактера које \texttt{read} функција распознаје:
Размаци: <space>, <form-feed>, <newline>, <carriage return>, <horizontal tab>, <vertical tab>.
Саставни карактери: а, б, в, г, д, ђ, е, ж, з, и, ј, к, л, љ, м, н, њ, о, п, р, с, т, ћ, у, ф, х, ц, ч, џ, ш, А, Б, В, Г, Д, Ђ, Е, Ж, З, И, Ј, К, Л, Љ, М, Н, Њ, О, П, Р, С, Т, Ћ, У, Ф, Х, Ц, Ч, Џ, Ш, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, !, \$, \&, *, +, -, ., /, <, =, >, ?, @, \^{}, \textbackslash, \_, \~{}.
Макро-карактери: ", \#, ', (, ), ;, `.
Избегнути карактер: |.
Алгоритам учитавања (типови карактера који се помињу у ово:
\begin{enumerate}
\item Учитај карактер, док је карактер ,,размак'', игнориши га и учитај следећи. Уколико није, одреди правилну радњу у зависности од типова карактера учитаног, она ће бити у једном од корака од \ref{firsttype} до \ref{lasttype}.
\item \label{firsttype} Ако је достигнут крај фајла, врати ,,крај фајла објекат'' као своју вредност.
\item Ако је карактер макро-карактер, изврши функцију макро-карактера. (функције макро-карактера могу да саме учитавају карактере са улаза, или чак да рекурзивно позивају \texttt{read} функцију).
\item \label{tokenchar}Ако је карактер саставан или избегнут, врати га назад на улаз и почни да учитаваш токен, скоком на корак \ref{begintoken}.
\item \label{lasttype}Ако карактер није ниједан од могућих типова, пријави грешку.
\item \label{begintoken}Сада се започиње учитавање токена, први учитан карактер (поменут у кораку \ref{tokenchar}) се додаје у токен, уколико је тај карактер саставан, започети обичан токен скоком на корак \ref{regtoken}, уколико је избегнут, започети проширени токен скоком на корак \ref{extoken}.
\item \label{regtoken}Сада се учитава обичан токен, уколико
\end{enumerate}
\end{comment}
\newpage
\renewcommand\refname{Литература}
\printbibliography