Додате процедуре за прераду ниски

This commit is contained in:
kappa 2019-02-28 23:43:33 +01:00
parent eaf7c19617
commit 3c05965a77
6 changed files with 153 additions and 6 deletions

View file

@ -62,6 +62,7 @@ void init()
addSymbolInternal("дужина-ниске", &strLengthInt, 0); addSymbolInternal("дужина-ниске", &strLengthInt, 0);
addSymbolInternal("именилац", &denominatorInt, 0); addSymbolInternal("именилац", &denominatorInt, 0);
addSymbolInternal("конс", &consInt, 0); addSymbolInternal("конс", &consInt, 0);
addSymbolInternal("карактер", &charInt, 0);
addSymbolInternal("карактер?", &charQInt, 0); addSymbolInternal("карактер?", &charQInt, 0);
addSymbolInternal("конс?", &consQInt, 0); addSymbolInternal("конс?", &consQInt, 0);
addSymbolInternal("ламбда", &lambdaInt, 1); addSymbolInternal("ламбда", &lambdaInt, 1);
@ -82,6 +83,7 @@ void init()
addSymbolInternal("реалан?", &realQInt, 0); addSymbolInternal("реалан?", &realQInt, 0);
addSymbolInternal("тачно->нетачно", &exactToInexactInt, 0); addSymbolInternal("тачно->нетачно", &exactToInexactInt, 0);
addSymbolInternal("сдр", &cdrInt, 0); addSymbolInternal("сдр", &cdrInt, 0);
addSymbolInternal("свежи-ниске", &catInt, 0);
addSymbolInternal("симбол?", &symbolQInt, 0); addSymbolInternal("симбол?", &symbolQInt, 0);
addSymbolInternal("читај", &readInt, 0); addSymbolInternal("читај", &readInt, 0);
addSymbolInternal("штампај", &printInt, 0); addSymbolInternal("штампај", &printInt, 0);
@ -147,7 +149,7 @@ int main(int argc, char **argv)
} }
printf("Добродошли у ЋИРИЛИСП ЧПШП окружење, верзија: " VERSION "\n"); printf("Добродошли у ЋИРИЛИСП ЧПШП окружење, верзија: " VERSION "\n");
while (Print(Eval(Read("ШКЉ> ", stdin), globalEnv))) while (Print(Eval(Read("Ћ> ", stdin), globalEnv)))
; ;
printf("\nДостигнут крај улазног тока.\nЗбогом и дођите нам опет!\n"); printf("\nДостигнут крај улазног тока.\nЗбогом и дођите нам опет!\n");

View file

@ -97,7 +97,25 @@
Лиспова синтакса је хомоиконична, другим речима он није само способан да прерађује листе, већ је и сам његов код сачињен од листа. Ово чини саму синтаксу језика довољно једноставном да се опише једним примером позива функцијем, који у Лиспу увек бивају следећег облика: \texttt{(функција арг1 арг2 арг3 ...)}, овако изгледа готово свака операција или функција извршена у самом језику, то наравно значи да су неки, нама познати изрази као \texttt{1 / (3 + 4 * 5)}, претворени у, на први изглед, необичне \texttt{(/ 1 (+ 3 (* 4 5)))}. Међутим, управо та чудна униформност читаве синтаксе која није присутна у већини других програмских језика је управо оно што Лиспу омогућава толику изражајну моћ. Лиспова синтакса је хомоиконична, другим речима он није само способан да прерађује листе, већ је и сам његов код сачињен од листа. Ово чини саму синтаксу језика довољно једноставном да се опише једним примером позива функцијем, који у Лиспу увек бивају следећег облика: \texttt{(функција арг1 арг2 арг3 ...)}, овако изгледа готово свака операција или функција извршена у самом језику, то наравно значи да су неки, нама познати изрази као \texttt{1 / (3 + 4 * 5)}, претворени у, на први изглед, необичне \texttt{(/ 1 (+ 3 (* 4 5)))}. Међутим, управо та чудна униформност читаве синтаксе која није присутна у већини других програмских језика је управо оно што Лиспу омогућава толику изражајну моћ.
\subsection{REPL\label{REPL}} \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{Функције} \subsection{Функције}
Лисп није нужно функционалан језик и његове функције углавном могу да стварају ,,нуспроизводе'' над остатком програма. Међутим многи дијалекти фаворизују функционалан стил. Функције се врло често дефинишу путем ,,ламбда'' израза, који су у већини дијалеката форме: Лисп није нужно функционалан језик и његове функције углавном могу да стварају ,,нуспроизводе'' над остатком програма. Међутим многи дијалекти фаворизују функционалан стил. Функције се врло често дефинишу путем ,,ламбда'' израза, који су у већини дијалеката форме:
@ -134,7 +152,7 @@
\end{verbatim} \end{verbatim}
\section{Кратак увод у Ћирилисп} \section{Кратак увод у Ћирилисп}
Ћирилисп је минималистички, претежно функционални дијалекат Лиспа, који је дизајниран и имплементиран у сврху овог матурског рада. Његово главно дефинишуће својство јесте што за конституентне карактере токена, уместо енглеских латиничнихслова из ASCII табеле користи Уникод UTF-8 карактере који описују слова која припадају српској ћирилици. Ћирилисп је минималистички, претежно функционални дијалекат Лиспа, који је дизајниран и имплементиран у сврху овог матурског рада. Његово главно дефинишуће својство јесте што за конституентне карактере токена, уместо енглеских латиничних слова из ASCII табеле користи Уникод UTF-8 карактере који описују слова која припадају српској ћирилици.
Интерпретер који служи као имплементација овог језика се може покренути на већ постојећем фајлу или унутар ЧПШ петље, описане у под-одељку \ref{REPL}, начин на који се интерпретер може покренути на оба описана начина је објашњен у одељку Увод. Интерпретер који служи као имплементација овог језика се може покренути на већ постојећем фајлу или унутар ЧПШ петље, описане у под-одељку \ref{REPL}, начин на који се интерпретер може покренути на оба описана начина је објашњен у одељку Увод.
@ -194,9 +212,60 @@ Read функција (кориснику Ћирилиспа доступна к
\end{verbatim} \end{verbatim}
\end{enumerate} \end{enumerate}
\newpage \subsection{Стандардне процедуре језика}
\section{Стандардне процедуре језика} Већ су поменуте \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} \begin{comment}
Следе типови карактера које \texttt{read} функција распознаје: Следе типови карактера које \texttt{read} функција распознаје:

View file

@ -24,6 +24,20 @@ int allNums(object list)
return 1; 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) int allSyms(object list)
/* проверава да ли је дати објекат симбол, или листа (правилна или крња), чији /* проверава да ли је дати објекат симбол, или листа (правилна или крња), чији
* је сваки члан симбол */ * је сваки члан симбол */
@ -875,6 +889,31 @@ object makeStrInt(object parameters)
return result; 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 strLengthInt(object parameters)
{ {
object result; object result;
@ -905,3 +944,32 @@ object strLengthInt(object parameters)
return result; return result;
} }
object catInt(object parameters)
{
if (!allStrings(parameters))
{
SIGERR(typeError);
}
object result;
TYPE(result) = stringObject;
int stringLength = 0;
object *current = &parameters;
while (TYPE(*current) != nilObject)
{
stringLength += strlen(STR(CAR(*current)));
current = &CDR(*current);
}
STR(result) = malloc((stringLength + 1) * sizeof(char));
STR(result)[0] = '\0';
current = &parameters;
while (TYPE(*current) != nilObject)
{
strcat(STR(result), STR(CAR(*current)));
current = &CDR(*current);
}
return result;
}

View file

@ -40,4 +40,6 @@ object readInt(object parameters);
object beginInt(object parameters); object beginInt(object parameters);
object throwInt(object parameters); object throwInt(object parameters);
object makeStrInt(object parameters); object makeStrInt(object parameters);
object charInt(object parameters);
object strLengthInt(object parameters); object strLengthInt(object parameters);
object catInt(object parameters);

View file

@ -90,6 +90,8 @@ void printValue(object input)
case L'\t': case L'\t':
printf("табулар"); printf("табулар");
break; break;
case L'\0':
printf("нул");
default: default:
printf("%lc", CHR(input)); printf("%lc", CHR(input));
} }

4
read.c
View file

@ -300,6 +300,10 @@ object dispatchedChar(wint_t c, FILE *stream)
{ {
CHR(result) = L'\t'; CHR(result) = L'\t';
} }
else if (!wcscmp(buffer, L"нул"))
{
CHR(result) = L'\0';
}
else else
{ {
SIGERR(invalidHashSequenceError); SIGERR(invalidHashSequenceError);