Имплементиране командно-линијске опције, додате функције за препознавање типова бројева
This commit is contained in:
parent
4dc9a4e888
commit
d24fa3945d
5
Makefile
5
Makefile
|
@ -8,7 +8,8 @@ PREFIX = /usr/local
|
||||||
LIBPREFIX = $(PREFIX)/lib
|
LIBPREFIX = $(PREFIX)/lib
|
||||||
|
|
||||||
# флегови за C компајлер и линкер
|
# флегови за C компајлер и линкер
|
||||||
CPPFLAGS = -D_POSIX_C_SOURCE=200200L -DDESTDIR=\"$(DESTDIR)\"
|
CPPFLAGS = -D_POSIX_C_SOURCE=200200L -DDESTDIR=\"$(DESTDIR)\" \
|
||||||
|
-DVERSION=\"$(VERSION)\"
|
||||||
CFLAGS = -g -std=c99 -pedantic -Wall -O0
|
CFLAGS = -g -std=c99 -pedantic -Wall -O0
|
||||||
# CFLAGS = -std=c99 -pedantic -Wall -O3
|
# CFLAGS = -std=c99 -pedantic -Wall -O3
|
||||||
LDFLAGS = -lm -lc
|
LDFLAGS = -lm -lc
|
||||||
|
@ -25,7 +26,7 @@ all: cirilisp $(L_SRC)
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
|
$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
|
||||||
|
|
||||||
$(OBJ): $(INC) $(L_SRC)
|
$(OBJ): $(INC)
|
||||||
|
|
||||||
cirilisp: $(OBJ)
|
cirilisp: $(OBJ)
|
||||||
$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
||||||
|
|
66
cirilisp.c
66
cirilisp.c
|
@ -1,4 +1,5 @@
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -16,14 +17,17 @@ int load(char *pathname)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
object exp;
|
object exp;
|
||||||
while (TYPE(exp = eval(read("", stream), globalEnv)) != EOFObject)
|
while (TYPE(exp = Eval(Read("", stream), globalEnv)) != EOFObject)
|
||||||
{
|
{
|
||||||
if (TYPE(exp) == errorObject)
|
if (TYPE(exp) == errorObject)
|
||||||
{
|
{
|
||||||
print(exp);
|
Print(exp);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
deleteObject(exp);
|
deleteObject(exp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
eofStatus = 0;
|
eofStatus = 0;
|
||||||
fclose(stream);
|
fclose(stream);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -45,6 +49,8 @@ void init()
|
||||||
addSymbolInternal("-", &subtractInt, 0);
|
addSymbolInternal("-", &subtractInt, 0);
|
||||||
addSymbolInternal("*", &multiplyInt, 0);
|
addSymbolInternal("*", &multiplyInt, 0);
|
||||||
addSymbolInternal("/", ÷Int, 0);
|
addSymbolInternal("/", ÷Int, 0);
|
||||||
|
addSymbolInternal("бројилац", &numeratorInt, 0);
|
||||||
|
addSymbolInternal("именилац", &denominatorInt, 0);
|
||||||
addSymbolInternal("навод", "eInt, 1);
|
addSymbolInternal("навод", "eInt, 1);
|
||||||
addSymbolInternal("опиши", &defineInt, 1);
|
addSymbolInternal("опиши", &defineInt, 1);
|
||||||
addSymbolInternal("опиши-складњу", &defineMacroInt, 1);
|
addSymbolInternal("опиши-складњу", &defineMacroInt, 1);
|
||||||
|
@ -58,6 +64,8 @@ void init()
|
||||||
addSymbolInternal("нил?", &nilQInt, 0);
|
addSymbolInternal("нил?", &nilQInt, 0);
|
||||||
addSymbolInternal("конс?", &consQInt, 0);
|
addSymbolInternal("конс?", &consQInt, 0);
|
||||||
addSymbolInternal("број?", &numberQInt, 0);
|
addSymbolInternal("број?", &numberQInt, 0);
|
||||||
|
addSymbolInternal("разломак?", &fractionQInt, 0);
|
||||||
|
addSymbolInternal("реалан?", &realQInt, 0);
|
||||||
addSymbolInternal("симбол?", &symbolQInt, 0);
|
addSymbolInternal("симбол?", &symbolQInt, 0);
|
||||||
addSymbolInternal("процедура?", &procedureQInt, 0);
|
addSymbolInternal("процедура?", &procedureQInt, 0);
|
||||||
addSymbolInternal("булски?", &boolQInt, 0);
|
addSymbolInternal("булски?", &boolQInt, 0);
|
||||||
|
@ -81,14 +89,64 @@ void init()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Није пронађена стандардна ЋИРЛИСП библиотека\
|
fprintf(stderr, "Није пронађена стандардна ЋИРЛИСП библиотека\
|
||||||
\nПрограм се није могао правилно покренути\n");
|
\nПрограм се није могао правилно покренути\n");
|
||||||
exit(0);
|
exit(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *help =
|
||||||
|
"\
|
||||||
|
Команда: cirilisp [-q|-v|-h] [<име фајла>]*\n\
|
||||||
|
Опције:\n\
|
||||||
|
-q Не започињи ЧПШП (\"Читај, процени, штампај\" петљу) након\n\
|
||||||
|
евалуирања командних аргумената\n\
|
||||||
|
-h Одштампај овај кратки помоћник и затвори програм\n\
|
||||||
|
-v Одштампај верзију програма и затвори програм\n\
|
||||||
|
";
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
while (print(eval(read("ШКЉ> ", stdin), globalEnv)))
|
|
||||||
|
int quitFlag = 0, opt;
|
||||||
|
while ((opt = getopt(argc, argv, "qvh")) != -1)
|
||||||
|
{
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'q':
|
||||||
|
quitFlag = 1;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("Верзија: " VERSION "\n");
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
printf(help);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Непозната командна опција");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (argv[optind] != NULL)
|
||||||
|
{
|
||||||
|
if (!load(argv[optind]))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Није било могуће отворити фајл %s.\n\
|
||||||
|
Проверите да ли дати фајл заиста постоји\n", argv[optind]);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
++optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quitFlag)
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Добродошли у ЋИРИЛИСП ЧПШП окружење, верзија: " VERSION "\n");
|
||||||
|
while (Print(Eval(Read("ШКЉ> ", stdin), globalEnv)))
|
||||||
;
|
;
|
||||||
printf("\nДостигнут крај улазног тока.\nЗбогом и дођите нам опет!\n");
|
printf("\nДостигнут крај улазног тока.\nЗбогом и дођите нам опет!\n");
|
||||||
|
|
||||||
|
|
10
eval.c
10
eval.c
|
@ -11,7 +11,7 @@ int maxRecursionDepth = 10000;
|
||||||
|
|
||||||
object apply(object function, object parameters, env currentEnv);
|
object apply(object function, object parameters, env currentEnv);
|
||||||
|
|
||||||
object eval(object input, env currentEnv)
|
object Eval(object input, env currentEnv)
|
||||||
{
|
{
|
||||||
object result;
|
object result;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ object eval(object input, env currentEnv)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int regularEvalOrder = 1;
|
int regularEvalOrder = 1;
|
||||||
CAR(input) = eval(CAR(input), currentEnv);
|
CAR(input) = Eval(CAR(input), currentEnv);
|
||||||
if (TYPE(CAR(input)) == errorObject)
|
if (TYPE(CAR(input)) == errorObject)
|
||||||
{
|
{
|
||||||
result = copyObject(CAR(input));
|
result = copyObject(CAR(input));
|
||||||
|
@ -50,7 +50,7 @@ object eval(object input, env currentEnv)
|
||||||
while (TYPE(*currentCell) != nilObject)
|
while (TYPE(*currentCell) != nilObject)
|
||||||
{
|
{
|
||||||
CAR(*currentCell) =
|
CAR(*currentCell) =
|
||||||
eval(CAR(*currentCell),
|
Eval(CAR(*currentCell),
|
||||||
currentEnv);
|
currentEnv);
|
||||||
|
|
||||||
if (TYPE(CAR(*currentCell)) ==
|
if (TYPE(CAR(*currentCell)) ==
|
||||||
|
@ -77,7 +77,7 @@ object eval(object input, env currentEnv)
|
||||||
PROC_SPECIAL(CAR(input)) &&
|
PROC_SPECIAL(CAR(input)) &&
|
||||||
PROC_TYPE(CAR(input)) == compoundProc)
|
PROC_TYPE(CAR(input)) == compoundProc)
|
||||||
{
|
{
|
||||||
result = eval(result, currentEnv);
|
result = Eval(result, currentEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ object apply(object procedure, object parameters, env currentEnv)
|
||||||
object *currentSubProc = &body;
|
object *currentSubProc = &body;
|
||||||
while (TYPE(*currentSubProc) != nilObject)
|
while (TYPE(*currentSubProc) != nilObject)
|
||||||
{
|
{
|
||||||
CAR(*currentSubProc) = eval(CAR(*currentSubProc), procEnv);
|
CAR(*currentSubProc) = Eval(CAR(*currentSubProc), procEnv);
|
||||||
if (TYPE(CDR(*currentSubProc)) == nilObject)
|
if (TYPE(CDR(*currentSubProc)) == nilObject)
|
||||||
{
|
{
|
||||||
result = copyObject(CAR(*currentSubProc));
|
result = copyObject(CAR(*currentSubProc));
|
||||||
|
|
2
eval.h
2
eval.h
|
@ -1,4 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
object eval(object input, env currentEnv);
|
object Eval(object input, env currentEnv);
|
||||||
object apply(object function, object parameters, env currentEnv);
|
object apply(object function, object parameters, env currentEnv);
|
||||||
|
|
65
internals.c
65
internals.c
|
@ -198,6 +198,36 @@ object inexactToExactInt(object parameters)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object fractionPart(object parameters, int part)
|
||||||
|
{
|
||||||
|
if (listLength(parameters) != 1)
|
||||||
|
{
|
||||||
|
SIGERR(argumentNumberError);
|
||||||
|
}
|
||||||
|
if (TYPE(CAR(parameters)) != numberObject ||
|
||||||
|
NUM_TYPE(CAR(parameters)) != fractionNum)
|
||||||
|
{
|
||||||
|
SIGERR(typeError);
|
||||||
|
}
|
||||||
|
object result;
|
||||||
|
TYPE(result) = numberObject;
|
||||||
|
NUM_TYPE(result) = fractionNum;
|
||||||
|
NUM_DENOM(result) = 1;
|
||||||
|
NUM_NUMER(result) = !part ? NUM_NUMER(CAR(parameters)) :
|
||||||
|
NUM_DENOM(CAR(parameters));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
object numeratorInt(object parameters)
|
||||||
|
{
|
||||||
|
return fractionPart(parameters, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
object denominatorInt(object parameters)
|
||||||
|
{
|
||||||
|
return fractionPart(parameters, 1);
|
||||||
|
}
|
||||||
|
|
||||||
object quoteInt(object parameters)
|
object quoteInt(object parameters)
|
||||||
{
|
{
|
||||||
object result;
|
object result;
|
||||||
|
@ -286,7 +316,7 @@ object defineInt(object parameters, env currentEnv)
|
||||||
if (listLength(parameters) == 2)
|
if (listLength(parameters) == 2)
|
||||||
{
|
{
|
||||||
result = copyObject(CAR(parameters));
|
result = copyObject(CAR(parameters));
|
||||||
value = eval(copyObject(CAR(CDR(parameters))),
|
value = Eval(copyObject(CAR(CDR(parameters))),
|
||||||
currentEnv);
|
currentEnv);
|
||||||
if (TYPE(value) == errorObject)
|
if (TYPE(value) == errorObject)
|
||||||
{
|
{
|
||||||
|
@ -420,7 +450,7 @@ object ifInt(object parameters, env currentEnv)
|
||||||
switch (listLength(parameters))
|
switch (listLength(parameters))
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
predicate = eval(copyObject(CAR(parameters)), currentEnv);
|
predicate = Eval(copyObject(CAR(parameters)), currentEnv);
|
||||||
if (TYPE(predicate) == errorObject)
|
if (TYPE(predicate) == errorObject)
|
||||||
{
|
{
|
||||||
CPYERR(ERR(predicate));
|
CPYERR(ERR(predicate));
|
||||||
|
@ -432,7 +462,7 @@ object ifInt(object parameters, env currentEnv)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = eval(copyObject(CAR(CDR(parameters))),
|
result = Eval(copyObject(CAR(CDR(parameters))),
|
||||||
currentEnv);
|
currentEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +472,7 @@ object ifInt(object parameters, env currentEnv)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
predicate = eval(copyObject(CAR(parameters)), currentEnv);
|
predicate = Eval(copyObject(CAR(parameters)), currentEnv);
|
||||||
if (TYPE(predicate) == errorObject)
|
if (TYPE(predicate) == errorObject)
|
||||||
{
|
{
|
||||||
CPYERR(ERR(predicate));
|
CPYERR(ERR(predicate));
|
||||||
|
@ -450,12 +480,12 @@ object ifInt(object parameters, env currentEnv)
|
||||||
|
|
||||||
if (TYPE(predicate) == boolObject && BOOL(predicate) == 0)
|
if (TYPE(predicate) == boolObject && BOOL(predicate) == 0)
|
||||||
{
|
{
|
||||||
result = eval(copyObject(CAR(CDR(CDR(parameters)))),
|
result = Eval(copyObject(CAR(CDR(CDR(parameters)))),
|
||||||
currentEnv);
|
currentEnv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = eval(copyObject(CAR(CDR(parameters))),
|
result = Eval(copyObject(CAR(CDR(parameters))),
|
||||||
currentEnv);
|
currentEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +528,29 @@ object numberQInt(object parameters)
|
||||||
return checkType(parameters, numberObject);
|
return checkType(parameters, numberObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object checkNumType(object parameters, numType type)
|
||||||
|
{
|
||||||
|
if (listLength(parameters) != 1)
|
||||||
|
{
|
||||||
|
SIGERR(argumentNumberError);
|
||||||
|
}
|
||||||
|
object result;
|
||||||
|
TYPE(result) = boolObject;
|
||||||
|
BOOL(result) = TYPE(CAR(parameters)) == numberObject &&
|
||||||
|
(NUM_TYPE(CAR(parameters)) == type ? 1 : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
object fractionQInt(object parameters)
|
||||||
|
{
|
||||||
|
return checkNumType(parameters, fractionNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
object realQInt(object parameters)
|
||||||
|
{
|
||||||
|
return checkNumType(parameters, realNum);
|
||||||
|
}
|
||||||
|
|
||||||
object symbolQInt(object parameters)
|
object symbolQInt(object parameters)
|
||||||
{
|
{
|
||||||
return checkType(parameters, symbolObject);
|
return checkType(parameters, symbolObject);
|
||||||
|
|
|
@ -7,6 +7,8 @@ object multiplyInt(object parameters);
|
||||||
object divideInt(object parameters);
|
object divideInt(object parameters);
|
||||||
object exactToInexactInt(object parameters);
|
object exactToInexactInt(object parameters);
|
||||||
object inexactToExactInt(object parameters);
|
object inexactToExactInt(object parameters);
|
||||||
|
object numeratorInt(object parameters);
|
||||||
|
object denominatorInt(object parameters);
|
||||||
object quoteInt(object parameters);
|
object quoteInt(object parameters);
|
||||||
object lambdaInt(object parameters, env currentEnv);
|
object lambdaInt(object parameters, env currentEnv);
|
||||||
object defineInt(object parameters, env currentEnv);
|
object defineInt(object parameters, env currentEnv);
|
||||||
|
@ -18,6 +20,8 @@ object ifInt(object parameters, env currentEnv);
|
||||||
object nilQInt(object parameters);
|
object nilQInt(object parameters);
|
||||||
object consQInt(object parameters);
|
object consQInt(object parameters);
|
||||||
object numberQInt(object parameters);
|
object numberQInt(object parameters);
|
||||||
|
object fractionQInt(object parameters);
|
||||||
|
object realQInt(object parameters);
|
||||||
object symbolQInt(object parameters);
|
object symbolQInt(object parameters);
|
||||||
object procedureQInt(object parameters);
|
object procedureQInt(object parameters);
|
||||||
object boolQInt(object parameters);
|
object boolQInt(object parameters);
|
||||||
|
|
2
print.c
2
print.c
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
void printValue(object input);
|
void printValue(object input);
|
||||||
|
|
||||||
int print(object input)
|
int Print(object input)
|
||||||
{
|
{
|
||||||
if (TYPE(input) == errorObject)
|
if (TYPE(input) == errorObject)
|
||||||
{
|
{
|
||||||
|
|
2
print.h
2
print.h
|
@ -1,4 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
int print(object input);
|
int Print(object input);
|
||||||
void printValue(object input);
|
void printValue(object input);
|
||||||
|
|
18
read.c
18
read.c
|
@ -19,7 +19,7 @@ wint_t unscanwc(wint_t c, FILE *stream);
|
||||||
object getToken();
|
object getToken();
|
||||||
object macroFunction(wchar_t m, FILE *stream);
|
object macroFunction(wchar_t m, FILE *stream);
|
||||||
|
|
||||||
object read(char *prompt, FILE *stream)
|
object Read(char *prompt, FILE *stream)
|
||||||
{
|
{
|
||||||
printf("%s", prompt);
|
printf("%s", prompt);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ object read(char *prompt, FILE *stream)
|
||||||
|
|
||||||
if (TYPE(result) == unspecifiedObject)
|
if (TYPE(result) == unspecifiedObject)
|
||||||
{
|
{
|
||||||
return read("", stream);
|
return Read("", stream);
|
||||||
/* уколико улаз функције није прави објекат (на пример уколико је учитан
|
/* уколико улаз функције није прави објекат (на пример уколико је учитан
|
||||||
* коментар) покушавамо прочитати опет */
|
* коментар) покушавамо прочитати опет */
|
||||||
}
|
}
|
||||||
|
@ -327,6 +327,16 @@ object dispatchedChar(wint_t c, FILE *stream)
|
||||||
}
|
}
|
||||||
TYPE(result) = unspecifiedObject;
|
TYPE(result) = unspecifiedObject;
|
||||||
break;
|
break;
|
||||||
|
case L'!':
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if ((c = scanwc(stream)) == L'\n' || c == WEOF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TYPE(result) = unspecifiedObject;
|
||||||
|
break;
|
||||||
case WEOF:
|
case WEOF:
|
||||||
SIGERR(unexpectedEOFError);
|
SIGERR(unexpectedEOFError);
|
||||||
default:
|
default:
|
||||||
|
@ -351,7 +361,7 @@ object macroFunction(wchar_t m, FILE *stream)
|
||||||
listCurrent = &result;
|
listCurrent = &result;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
object currentObject = read("", stream);
|
object currentObject = Read("", stream);
|
||||||
if (TYPE(currentObject) == errorObject &&
|
if (TYPE(currentObject) == errorObject &&
|
||||||
!strcmp(ERR(currentObject),
|
!strcmp(ERR(currentObject),
|
||||||
commonErrs[unmatchedParenError]))
|
commonErrs[unmatchedParenError]))
|
||||||
|
@ -441,7 +451,7 @@ object macroFunction(wchar_t m, FILE *stream)
|
||||||
break;
|
break;
|
||||||
case L'\'':
|
case L'\'':
|
||||||
case L'`':
|
case L'`':
|
||||||
expression = read("", stream);
|
expression = Read("", stream);
|
||||||
if (TYPE(expression) == errorObject)
|
if (TYPE(expression) == errorObject)
|
||||||
{
|
{
|
||||||
CPYERR(ERR(expression));
|
CPYERR(ERR(expression));
|
||||||
|
|
2
read.h
2
read.h
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
object read(char *prompt, FILE *stream);
|
object Read(char *prompt, FILE *stream);
|
||||||
|
|
5
инит.ћ
5
инит.ћ
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
(опиши нил ())
|
(опиши нил ())
|
||||||
|
|
||||||
|
(опиши (новиред) (прикажи #\новиред))
|
||||||
|
|
||||||
(опиши (није предикат)
|
(опиши (није предикат)
|
||||||
(ако предикат #л #и))
|
(ако предикат #л #и))
|
||||||
|
|
||||||
|
@ -58,3 +60,6 @@
|
||||||
(ако (нил? (сдр клаузе))
|
(ако (нил? (сдр клаузе))
|
||||||
()
|
()
|
||||||
(примени услов (сдр клаузе)))))))
|
(примени услов (сдр клаузе)))))))
|
||||||
|
|
||||||
|
(опиши (цеоброј? џ)
|
||||||
|
(= (именилац (нетачно->тачно џ)) 1))
|
||||||
|
|
Loading…
Reference in a new issue