diff --git a/cirilisp.c b/cirilisp.c index 9bdeb5f..8be0d45 100644 --- a/cirilisp.c +++ b/cirilisp.c @@ -62,6 +62,7 @@ void init() addSymbolInternal("дужина-ниске", &strLengthInt, 0); addSymbolInternal("именилац", &denominatorInt, 0); addSymbolInternal("конс", &consInt, 0); + addSymbolInternal("карактер", &charInt, 0); addSymbolInternal("карактер?", &charQInt, 0); addSymbolInternal("конс?", &consQInt, 0); addSymbolInternal("ламбда", &lambdaInt, 1); @@ -82,6 +83,7 @@ void init() addSymbolInternal("реалан?", &realQInt, 0); addSymbolInternal("тачно->нетачно", &exactToInexactInt, 0); addSymbolInternal("сдр", &cdrInt, 0); + addSymbolInternal("свежи-ниске", &catInt, 0); addSymbolInternal("симбол?", &symbolQInt, 0); addSymbolInternal("читај", &readInt, 0); addSymbolInternal("штампај", &printInt, 0); @@ -147,7 +149,7 @@ int main(int argc, char **argv) } printf("Добродошли у ЋИРИЛИСП ЧПШП окружење, верзија: " VERSION "\n"); - while (Print(Eval(Read("ШКЉ> ", stdin), globalEnv))) + while (Print(Eval(Read("Ћ> ", stdin), globalEnv))) ; printf("\nДостигнут крај улазног тока.\nЗбогом и дођите нам опет!\n"); diff --git a/doc/rad.tex b/doc/rad.tex index 477a23d..f75747d 100644 --- a/doc/rad.tex +++ b/doc/rad.tex @@ -97,7 +97,25 @@ Лиспова синтакса је хомоиконична, другим речима он није само способан да прерађује листе, већ је и сам његов код сачињен од листа. Ово чини саму синтаксу језика довољно једноставном да се опише једним примером позива функцијем, који у Лиспу увек бивају следећег облика: \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} штампала дати број на екрану или терминалу. +Један од централних појмова у свакој имплементацији Лиспа јесте такозвани ,,Read, Evaluate, Print Loop'', у овој имплементацији познато као ЧПШП. ЧПШП је најосновнији начин интеракције са већином Лисп система, у питању је командно-линијски интерфејс у којем корисник текстуално уноси програм као низ симболичких израза, који у ,,бесконачној'' петљи учитава симболичке изразе а затим их евалуира (процењује им коначну вредност), и на крају их штампа. На пример горепоменути израз: +\begin{verbatim} +(/ 1 (+ 3 (* 4 5))) +\end{verbatim} +би био учитан кроз \textit{read} функцију, која би га парсирала и претворила у листу, коју би потом \textit{eval} функција проценила да има резултат +\begin{verbatim} +1/23 +\end{verbatim} +(већина имплементација природно подржава разломачку аритметику), након чега би \textit{print} штампала дати број на екрану или терминалу. + +Будући да \texttt{eval} функција покушава да евалуира сваку дату непразну листу као позив на функцију која је описана првим чланом листе, а сваки симбол као референцу на неку променљиву, веома често је пожељно назначити \texttt{eval} функцији да дати израз не желимо да она евалуира, ово се у већини дијалеката чини помоћу \texttt{quote} специјалне форме. На пример уколико напишемо израз као \texttt{(1 2 3)}, највероватније желимо да на неки начин користимо листу са члановима 1, 2 и 3, а не да позовемо функцију \texttt{1} са аргументима \texttt{2} и \texttt{3}, дакле написаћемо га као: +\begin{verbatim} +(quote (1 2 3)) +\end{verbatim} +Пошто се ова форма изузетно често користи чешће се виђа скраћени облик исте: +\begin{verbatim} +'(1 2 3) +'simbol +\end{verbatim} \subsection{Функције} Лисп није нужно функционалан језик и његове функције углавном могу да стварају ,,нуспроизводе'' над остатком програма. Међутим многи дијалекти фаворизују функционалан стил. Функције се врло често дефинишу путем ,,ламбда'' израза, који су у већини дијалеката форме: @@ -134,7 +152,7 @@ \end{verbatim} \section{Кратак увод у Ћирилисп} -Ћирилисп је минималистички, претежно функционални дијалекат Лиспа, који је дизајниран и имплементиран у сврху овог матурског рада. Његово главно дефинишуће својство јесте што за конституентне карактере токена, уместо енглеских латиничнихслова из ASCII табеле користи Уникод UTF-8 карактере који описују слова која припадају српској ћирилици. +Ћирилисп је минималистички, претежно функционални дијалекат Лиспа, који је дизајниран и имплементиран у сврху овог матурског рада. Његово главно дефинишуће својство јесте што за конституентне карактере токена, уместо енглеских латиничних слова из ASCII табеле користи Уникод UTF-8 карактере који описују слова која припадају српској ћирилици. Интерпретер који служи као имплементација овог језика се може покренути на већ постојећем фајлу или унутар ЧПШ петље, описане у под-одељку \ref{REPL}, начин на који се интерпретер може покренути на оба описана начина је објашњен у одељку Увод. @@ -194,9 +212,60 @@ Read функција (кориснику Ћирилиспа доступна к \end{verbatim} \end{enumerate} -\newpage -\section{Стандардне процедуре језика} -Већ су поменуте опиши и ламбда специјалне форме, којима се граде и везују имена за процедуре које корисник описује, међутим језик би био бескористан да не поседује сопствене, предефинисане функције за различите сврхе: +\subsection{Стандардне процедуре језика} +Већ су поменуте \texttt{опиши} и \texttt{ламбда} специјалне форме, којима се граде и везују имена за процедуре које корисник описује, међутим језик би био бескористан да не поседује сопствене, предефинисане функције за различите сврхе. + +\subsubsection{Типови} +Неке од најбитнијих врста јесу типски предикати, другим речима функције које враћају булеан \texttt{\#и} или \texttt{\#л} у зависности од тога да ли је једини аргумент дат тип или не, језик пружа следеће предикате типова: \texttt{листа?}, \texttt{број?}, \texttt{конс?}, \texttt{нил?}, \texttt{ниска?}, \texttt{реалан?}, \texttt{разломак?}, \texttt{процедура?}, \texttt{симбол?}, \texttt{цео-број?}. +\begin{verbatim} +Ћ> (листа? 5) +#л +Ћ> (цео-број? 5) +#и +\end{verbatim} + +\subsubsection{Аритметика} +Постоји неколико функција које омогућавају извршавање рачуна над бројевима, све основне аритметичке операције су понуђене: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}. +\begin{verbatim} +Ћ> (+ (* 3 5 4) 9) +69 +Ћ> (/ (+ 6 7) (- 150 100)) +13/50 +Ћ> (+ (* 20 20) 20 0,666) +420,666 +\end{verbatim} + +Такође постоје и функције за преобраћивање рационалног (тачног) броја, у реални (нетачни), и обратно. +\begin{verbatim} +Ћ> (тачно->нетачно 555/333) +1,666667 +Ћ> (нетачно->тачно 12,250) +49/4 +\end{verbatim} + +На рационалним бројевима се такође могу применити функције \texttt{бројилац} и \texttt{именилац}. +\begin{verbatim} +Ћ> (бројилац 88/14) ; разломци се скраћују, улаз је 44/7 +44 +Ћ> (именилац 1312) ; сваки цео број је само разломак са имениоцем 1 +1 +\end{verbatim} + +Коначно, постоје функције за добијање маскимума и минимума из листе бројева. +\begin{verbatim} +Ћ> (макс 3 5 2 512 3) +512 +Ћ> (мин 85 99 105 110 110) +85 +\end{verbatim} + +\subsubsection{Прерада листа} +Као и сваки дијалекат Лиспа, Ћирилисп поседује процедуре у сврси прераде листа и конс ћелија. \texttt{сар} (садржај адресног дела броја регистра) и \texttt{сдр} (садржај декрементног дела броја регистра) су важне процедуре за анализирање ових структура. +\begin{verbatim} +Ћ> (сар '(ČĆ)) +"упркос" +\end{verbatim} +% proba.txt \begin{comment} Следе типови карактера које \texttt{read} функција распознаје: diff --git a/internals.c b/internals.c index 1a335af..fb74b3e 100644 --- a/internals.c +++ b/internals.c @@ -24,6 +24,20 @@ int allNums(object list) return 1; } +int allStrings(object list) +{ + object *currentCell = &list; + while (TYPE(*currentCell) != nilObject) + { + if (TYPE(CAR(*currentCell)) != stringObject) + { + return 0; + } + currentCell = &CDR(*currentCell); + } + return 1; +} + int allSyms(object list) /* проверава да ли је дати објекат симбол, или листа (правилна или крња), чији * је сваки члан симбол */ @@ -875,6 +889,31 @@ object makeStrInt(object parameters) return result; } +object charInt(object parameters) +{ + object result; + if (listLength(parameters) != 2) + { + SIGERR(argumentNumberError); + } + if (TYPE(CAR(parameters)) != stringObject || + TYPE(CAR(CDR(parameters))) != numberObject || + !integer(CAR(CDR(parameters)))) + { + SIGERR(typeError); + } + + TYPE(result) = charObject; + char *mbs = STR(CAR(parameters)); + int index = NUM_NUMER(CAR(CDR(parameters))), current = 0; + for (current = 0; current < index && *mbs != '\0'; + mbs += mblen(mbs, MB_CUR_MAX), ++current) + ; + mbtowc(&CHR(result), mbs, MB_CUR_MAX); + + return result; +} + object strLengthInt(object parameters) { object result; @@ -905,3 +944,32 @@ object strLengthInt(object parameters) return result; } + +object catInt(object parameters) +{ + if (!allStrings(parameters)) + { + SIGERR(typeError); + } + + object result; + TYPE(result) = stringObject; + int stringLength = 0; + object *current = ¶meters; + while (TYPE(*current) != nilObject) + { + stringLength += strlen(STR(CAR(*current))); + current = &CDR(*current); + } + STR(result) = malloc((stringLength + 1) * sizeof(char)); + STR(result)[0] = '\0'; + + current = ¶meters; + while (TYPE(*current) != nilObject) + { + strcat(STR(result), STR(CAR(*current))); + current = &CDR(*current); + } + + return result; +} diff --git a/internals.h b/internals.h index 6604466..a08b7a3 100644 --- a/internals.h +++ b/internals.h @@ -40,4 +40,6 @@ object readInt(object parameters); object beginInt(object parameters); object throwInt(object parameters); object makeStrInt(object parameters); +object charInt(object parameters); object strLengthInt(object parameters); +object catInt(object parameters); diff --git a/print.c b/print.c index cf9d225..b6298d6 100644 --- a/print.c +++ b/print.c @@ -90,6 +90,8 @@ void printValue(object input) case L'\t': printf("табулар"); break; + case L'\0': + printf("нул"); default: printf("%lc", CHR(input)); } diff --git a/read.c b/read.c index 05a2874..1098673 100644 --- a/read.c +++ b/read.c @@ -300,6 +300,10 @@ object dispatchedChar(wint_t c, FILE *stream) { CHR(result) = L'\t'; } + else if (!wcscmp(buffer, L"нул")) + { + CHR(result) = L'\0'; + } else { SIGERR(invalidHashSequenceError);