LINUX.ORG.RU

Facebook платит за устранение багов в реализации языка программирования D

 ,


1

5

На данный момент размер вознаграждения за исправление багов в общей сложности насчитывает 1500$. Со слов Александреску, они будут внимательно смотреть, как это скажется на сообществе.

Одно из определений языка D: «D — это то, чем должен был быть С++». Вокруг языка сломалось уже много копий, но несмотря на это язык продолжает жить и развиваться, демонстрируя свои замечательные возможности и расширяя свое сообщество. Все больше разработчиков из мира С++/Java пристально следят за развитием языка и стараются держать руку на пульсе. Должен отметить, что сообщество D не является ортодоксальным и фундаменталистким (что бы это ни значило), и нередко в ньюсгруппах можно увидеть, что в ответ на вопрос, можно ли использовать D для решения определенной задачи, члены сообщества рекомендуют задавшему вопрос использовать другой язык, отличный от D. Так что в лице сообщества D любой найдет грамотных специалистов своего дела, готовых ответить на нужный вопрос кратко и по существу. Все это делает развитие языка неизбежным и неотвратимым.

Список багов с ценами за их устранение

>>> Оригинал новости

★★

Проверено: Shaman007 ()
Последнее исправление: cetjs2 (всего исправлений: 6)

Ответ на: комментарий от DarkEld3r

То есть берём «минимально подходящий» тип

Тут аналог не C++, а Haskell или Agda.

Если я пишу функцию с сигнатурой T test(T a, T b), то я обязан гарантировать, что она работает для _всех_ T.

Ошибка как раз показывает контр-примеры. А сам +:

> +
- : (Number * -> Number) ...

monk ★★★★★
()
Ответ на: комментарий от DarkEld3r

берём «минимально подходящий» тип к которому могут привестись оба аргумента без потерь

Они гарантированно приведутся к Any. И толку?

Понимаешь, тут нет типов у переменных. Есть только типы у значений. Любое значение принадлежит типу Any, лююое число — типу Number и т.д.

monk ★★★★★
()

Не знаю насчет багов, но я на базе исходников компилятора D пилю тут компилятор своего языка программирования... так вот, код написан прямо скажем так себе. Си с минимумом С++, какая-то своя библиотека структур данных, та часть которая отвечает за генерацию кода - там вообще черт ногу сломит. Да даже лексический анализатор - я в свое время гораздо красивее и компактнее написал. А там - огромные фукнции с кучей if'ов внутри. Рефакторю понемногу. Были и баги какие-то явные, уж не помню.

NeoCode
()
Ответ на: комментарий от DarkEld3r

Могу только предположить, что ты залогинен на этом «зерочане».

Вовсе нет, в гугле картинку нашел. Может быть этот зерочан уже в черном списке?

Deleted
()
Ответ на: комментарий от wota

man Qt
в том числе там, где «переносимой» жабки нет

это какой язык получится? вы так излагаете, как будто С++ где в своей нише тихонько сидит, а на деле куда ни плюнь - везде си или с++, включая половину стандартной библиотеки питона.

anonymous
()
Ответ на: комментарий от eao197

Что это за языки-то такие, да еще в большом количестве?

если брать топовые, то, например, Java, C#, Objective C, Python, JavaScript, Lua, ну и вероятно я не совсем корректно сформулировал, имелись ввиду как новые ЯП, так и относительно старые, но получившие развитие и популярность

wota ★★
()
Ответ на: комментарий от DarkEld3r

берём «минимально подходящий» тип

Ах да. Математически минимально подходящий тип для твоей функции будет U {a} {b}. В смысле, множество из двух элементов. И такой тип существует для любых a и b.

monk ★★★★★
()
Ответ на: комментарий от wota

JavaScript

вот это очень годно. это такой родной сын си.

остальное - как раз нишевое. раньше большие компании писали свои библиотечки, теперь пишут свои язычки с модулями и тредингом. теперь ещё и язычки которые в джаваскрипт компилируются.

anonymous
()
Ответ на: комментарий от anonymous

вот это очень годно. это такой родной сын си.

и тем не менее сейчас на JS скриптуют то, что раньше писалось на компилируемых ЯП, как пример тот же Qt с QML

wota ★★
()
Ответ на: комментарий от monk

Увы. В scheme не могут. Это же не defgeneric из CLOS. Если функция определена, то доопределить её нельзя (разве что через set!).

Жаль. Ну тогда понятно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от wota

потому это и родной сын си, что язык универсален.

anonymous
()
Ответ на: комментарий от wota

если брать топовые, то, например, Java, C#, Objective C, Python, JavaScript, Lua, ну и вероятно я не совсем корректно сформулировал, имелись ввиду как новые ЯП, так и относительно старые, но получившие развитие и популярность

Из этого списка реально у C++ что-то забирали разве что Java (которая родилась намного раньше, чем D и к появлению D уже была вполне себе мейнстримом) и C#. Вот С# действительно появился и выстрелил тогда, когда разработчики D кормили всех обещаниями о «лучшем C++».

Objective C появился давным-давно. И не так уж, чтобы что-то у C++ забрал, поскольку всегда был языком одной платформы. Была платформа в жопе, там же и ObjC был. Стала платформа популярной, стал и ObjC популярным.

Так что, по факту, только C# и появился.

Можно было бы попробовать сюда же приплести OCaml с Haskell-ем. Но и они постарше D будут. А так же еще вопрос, забирают ли они у кого-нибудь что-нибудь. И если забирают, то у C++ ли, или у Java/C# и пр.

Данную придирку делаю лишь для того, чтобы подчеркнуть, что новые, получающие широкое распространение инструменты, появляются совсем не часто. И причин для того, чтобы D стал таковым, я сейчас сам уже не вижу.

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от monk

Тут аналог не C++, а Haskell или Agda.
Если я пишу функцию с сигнатурой T test(T a, T b), то я обязан гарантировать, что она работает для _всех_ T.

Ну идею я (кажется) понял. Но всё-таки возможность создавать обобшённые функции (как в С++) мне кажется удобной. Такого тут нет? Или и не сильно нужно?

Смущает, что если использовать не типизированный вариант, то оно как бы работало (с сообщением об ошибке в рантайме), если была бы перегрузка функций.

Шаблонов в смысле C++? Когда тело функции зависит от типа параметра? Нельзя. Для этого есть классы.

В С++ всё-таки благодаря перегрузке функций и тело может быть одинаковое.

DarkEld3r ★★★★★
()
Ответ на: комментарий от quiet_readonly

Кого волнует заказчик? Если ему не нравится - он может себе других программистов выбрать, а я вполне могу писать на том, что мне нравится, а не на том, про что заказчик в модном журнале прочитал.

ForwardToMars
()
Ответ на: комментарий от Deleted

Вовсе нет, в гугле картинку нашел. Может быть этот зерочан уже в черном списке?

Ну у меня вместо картинки отображется следующее «Hosted on zerochan.net. Use the sharred links instead.». То есть они не хотят, чтобы их картинки просто так постили, а использовали их механизм.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

И не так уж, чтобы что-то у C++ забрал, поскольку всегда был языком одной платформ

и таки на той самой платформе когда-то рулил CodeWarrior и C++

Данную придирку делаю лишь для того, чтобы подчеркнуть, что новые, получающие широкое распространение инструменты, появляются совсем не часто.

но не так уж и редко, сейчас «рулят» ЯП, которым и 20 лет нет, если не считать старичков С и С++

wota ★★
()
Ответ на: комментарий от monk

Понимаешь, тут нет типов у переменных. Есть только типы у значений. Любое значение принадлежит типу Any, лююое число — типу Number и т.д.

Похоже, что не понимаю:

(define: byte : Byte 255)
По моему переменная «byte» имеет тип «Byte» и значение «255».

Они гарантированно приведутся к Any. И толку?

Ну если бы перегрузка функций всё-таки была, то функция «+» очевидно не принимала бы Any. А Number и мой тип - принимала бы. Соответственно, передача чего-то чисельного работало бы, как и передача моих типов. Но так как перегрузки нет, то тут спорить не о чем.

DarkEld3r ★★★★★
()
Ответ на: комментарий от wota

но не так уж и редко, сейчас «рулят» ЯП, которым и 20 лет нет, если не считать старичков С и С++

О как, 20 лет. Чё так мало-то? Почему не 25 или 30? Или в индустрии полно разработчиков со стажем в 20 лет?

О возрасте языков.

  • Java начали делать в 1991-м.
  • Objective-C в начале 1980-х (в 1982-1983).
  • Python начали делать в 1989-м.
  • Ruby начали делать в 1993-м.
  • Haskell в конце 1980-х (первая версия определена в 1990).
  • PHP начали делать в 1994-м.
  • JavaScript вышел в свет в 1995-м (т.е. делать его начали еще раньше).
  • Lua в 1993-м.

Так что из тех языков, которые «рулят» сейчас совсем немногие отстоят далеко от 20-ти летнего рубежа. Остальные либо его уже перешагнули, либо вот-вот это сделают.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

начали делать
начали делать
начали делать

то что начинали делать - не является даже первой версией ЯП, от которой логично вести отсчет

wota ★★
()

гос. измену за помощь иностранным спецслужбам за такое ни пришьют?

anonymous
()

Facebook платит за устранение багов в реализации языка программирования D

Ещё не раз заплатит :3

anonymous
()
Ответ на: комментарий от wota

то что начинали делать - не является даже первой версией ЯП, от которой логично вести отсчет

Тогда D вообще в пролете. Поскольку официальный релиз D 1.000 состоялся в январе 2007-го года. Когда уже C# был самым что ни есть мейнстримом. И никто уже особо от C++ не отъедал.

А официального релиза D2 еще вообще не было.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

производительнось, допустим, не свойство языка,

бугага

рассмотрим язык, где любой тип является полноценным классом, в том числе и Character (символ юникода) является классом, т.е. от него можно наследовать, добавляя виртуальные функции, причем прямо в рантайме — тормоза и расход памяти неизбежны, т.к. в 3 байта такой Character не поместится (а еще лучше, когда язык не ооп, а prototype-based, и юзеры могут это юзать в рантайме)

тут придется с каждым Character таскать еще и vtbl

конечно, тут можно попытаться повыжимать производительность в тех случаях, когда юзеры используют нерасширенный Character; шансы есть, но если стандарт *языка* не требует оптимизации для этого случая, то никто заморачиваться не будет

если же ты считаешь, что язык «выше» конкретного класса Character, то все равно тормозность — это свойство языка; например, отсутствие или наличие *гарантии* оптимизации, для примера — http://en.wikipedia.org/wiki/Return_value_optimization

з.ы. наверно я продолжу дискуссию про неявный return

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от eao197

Тогда D вообще в пролете.

ну это то понятно :)

А официального релиза D2 еще вообще не было

тогда и говорить нечего, раз даже авторы считают его неготовым, разве что обсуждать его теоретические преимущества в перспективе, но и тут «нишу» D уже начал занимать Rust

wota ★★
()
Ответ на: комментарий от monk

Поэтому утверждение «эти две переменные с разными значениями имеют один тип» вообще бессмысленно.

ыыы!

слишком толсто

да, компилятору надо подыскивать разумное понимание слова «тип», но это вполне реально

Если тип = «множество из одного значения», то всегда ответ «нет»,

а тут ты просто неправ — ответ иногда «да», и есть *практически* важный случай, когда именно это надо:

например, скалярное произведение двух векторов x и y имеет смысл только тогда, когда размерности их совпадают, т.е. type(x.dimension)==type(y.dimension) настолько, что даже x.dimension==y.dimension

если тип = Any, то всегда «да»...

... но у Any почти нет методов, и поэтому (почти всегда) тайпчекнуть код невозможно, поэтому *этот* ответ «да» компилятор не устроит

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

для примера — http://en.wikipedia.org/wiki/Return_value_optimization

Дык, для С++ как раз этой гарантии нет. Всё что делает, в данном случае, стандарт - «разрешает» (не требует) данную оптимизацию. И то просто потому что это может влиять на «наблюдаемое поведение».

Или я тебя не так понял?

DarkEld3r ★★★★★
()
Ответ на: комментарий от wota

тогда и говорить нечего, раз даже авторы считают его неготовым, разве что обсуждать его теоретические преимущества в перспективе, но и тут «нишу» D уже начал занимать Rust

Фиг знает. На D можно смотреть с двух точек зрения:

1. Как на уникальный долгострой, который лишь показывает, что авторам D интересно делать D и все. Не было речи о создании стабильного инструмента, которым могли бы в течении многих лет пользоваться тысячи и тысячи прикладных разработчиков, совершенно не интересующихся проблемами языка. С этой точки зрения вся эта возня вокруг D2 ничего не меняет, а лишь подтверждает, что долгострой никогда не остановится.

2. Как новый язык D2, который вот-вот должен появится и оставаться стабильным в течении 3-4 лет, для того, чтобы на его основе можно было начать спокойно что-то делать. И вот тут главный вопрос в том, где будет прикладная ниша D. Если производительность не нужна или нужна постольку-поскольку, то Java/С# или даже скриптовые языки он не потеснит. Если нужна производительность для вытеснения C++, то D мешают и отсутствие инфраструктуры и инструментария, а так же наличие сборщика мусора. При том, что достоинств у D перед C++11/14 уже поменьше, чем в свое время перед C++03. И, конечно же, очень важный вопрос, когда же D2 зарелизят и объявят заморозку языка на несколько лет.

Ну а редкие примеры того, как какой-то новый язык успешно эпизодически где-то используется есть почти всегда. Это не показатель.

eao197 ★★★★★
()
Ответ на: комментарий от DarkEld3r

По моему переменная «byte» имеет тип «Byte» и значение «255».

Да, но не «значение 255 типа Byte». То, что переменная byte имеет тип приведёт к тому, что любая операция

(set! byte (foo ...))

будет проверять: действительно ли функция foo возвращает тип, являющийся подмножеством Byte. И если нет, то такая операция небезопасна.

Дело в том, что в Racket число типов несчётно (в математичексом смысле), а в С++ конечно.

В С++ я не могу сделать тип «число от 0 до 1000».

Недостаток такого подхода:

(:print-type +)
...
        (Byte Zero -> Byte)
        (Zero Byte -> Byte)
...
        (Byte Byte -> Index)
...

А значит

(define: byte : Byte (+ 0 1))
работает, а
(define: byte : Byte (+ 1 1))
Type Checker: Expected Byte, but got Positive-Index in: (+ 1 1)

Потому что в этот момент проверяются только типы, а не значения.

Собственно, твой вариант шаблона так и пишется:

(: test (All (A) (case-> (Number Number -> Number)
                         (String String -> String)
                         ((Listof A) (Listof A) -> (Listof A)))))

(define (test a b)
  (cond
    [(number? a) (+ a b)]
    [(string? a) (string-append a b)]
    [(list? a) (append a b)]))

Можно проверить

> (string-append (test "a" "b") "c")
- : String
"abc"
> (string-append (test 2 1) "c")
. Type Checker: Expected String, but got Number in: (test 2 1)

monk ★★★★★
()
Ответ на: комментарий от DarkEld3r

мой пример, действительно, отнюдь не самый лучший, но кое-что поясняет

Дык, для С++ как раз этой гарантии нет.

и это означает, что с++ не является *бескомпромиссно* быстрым; конечно, мой пример был бы куда лучше, если бы с++ требовал rvo

Всё что делает, в данном случае, стандарт - «разрешает» (не требует) данную оптимизацию. И то просто потому что это может влиять на «наблюдаемое поведение».

а это означает, что с++ все же может быть быстрее, чем язык, где в стандарте «наблюдаемое поведение» сделано таким, как как в с++ без rvo

плюс еще надо учесть, что «move semantics без rvo» может быть лучшим выбором, чем обязательное rvo — но речь, идет, напомню, о том, что «производительнось, допустим, не свойство языка»

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от DarkEld3r

Может у Вас одного так? В моем браузере моя же ссылка отображается за милую душу.

P.S. Все это очень странно.

Deleted
()
Ответ на: комментарий от www_linux_org_ru

например, скалярное произведение двух векторов x и y имеет смысл только тогда, когда размерности их совпадают, т.е. type(x.dimension)==type(y.dimension)

Именно числом параметризовать в Racket нельзя (типы векторов и списков параметризуются только типом элементов). Есть проверка на количество элементов, но явная. Можно сделать так:

(: scalar-product (case-> ((Array Number) (Array Number) -> Number)
                           ((Array Number Number) (Array Number Number) -> Number)
                           ((Array Number Number Number) (Array Number Number Number) -> Number))
... добавить строк сколько надо
))

monk ★★★★★
()
Ответ на: комментарий от Deleted

ыыы, вот и выросло поколение, которое не знает, что такое referer

у тех, у кого показывается не то, необходимо скопировать ссылку и ввести ее в адресную строку, а не переходить по ней (а еще может и нажать релоад)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от monk

Дело в том, что в Racket число типов несчётно (в математичексом смысле), а в С++ конечно.

чувак, прекращай вешать нам на уши лапшу

число типов в с++ счетно, а в Racket либо конечно, либо счетно (как, скажите пожалуста, там *изображается* элемент несчетного множества?)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от DarkEld3r

если бы перегрузка функций всё-таки была, то функция «+» очевидно не принимала бы Any

Как раз наоборот, очевидно принимала бы Any. Так как на стадии компиляции определить классы, которые будут реализованы невозможно.

Пример из SBCL:

* (describe 'make-instance)
...
  Derived type: (FUNCTION (T &REST T &KEY &ALLOW-OTHER-KEYS) *)
...

Обрати внимание, что тип везде t, так как не ограничен.

monk ★★★★★
()
Ответ на: комментарий от monk

Именно числом параметризовать в Racket нельзя (типы векторов и списков параметризуются только типом элементов).

в с++ можно параметризовать:

1. статически известным числом

2. неизвестным числом, адрес которого статически известен

нельзя параметризовать, или может и можно, но с ахренными костылями:

3. локальной переменной (т.е. неизвестным числом, адрес которого статически известен относительно определенного фрейма стека)

нельзя параметризовать:

4. неизвестным числом... в общем, нет в с++ path-dependent types как в scala

5. ну и полноценных dependent types тоже нет — все уже догадались, пишу только для полноты

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

(как, скажите пожалуста, там *изображается* элемент несчетного множества?)

Например, pi :-)

Ладно, убедил. На реальном компьютере с конечной памятью тип с несчётным множеством существовать не может.

в с++ счетно

Точно, про все виды «указатель на...» не подумал.

monk ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

неизвестным числом, адрес которого статически известен

Можно уточнить?

int n;

void f(int x[10])
{
...
}

void g()
{
  int y[n];
  f(y);
}

int main()
{
   n = ...;
   g();
}

как будет компилироваться? Или нормальный пример такой параметризации?

monk ★★★★★
()
Ответ на: комментарий от yetanother

Хотя я начинаю привыкать к (бес)содержательной части большинства постов на Лоре :).

Аналогично.

Ты хочешь сказать, чот Уолтер Брайт получит эти деньги? :-)

rtvd ★★★★★
()
Ответ на: комментарий от DarkEld3r

А про typed racket что скажешь?

Про Racket и Typed Racket я в другом треде уже всё сказал. Идея волшебна, реализация - чудовищна.

rtvd ★★★★★
()
Ответ на: комментарий от DarkEld3r

Но всё-таки возможность создавать обобшённые функции (как в С++) мне кажется удобной. Такого тут нет? Или и не сильно нужно?

По факту нет. Так как при выполнении типа уже нет. Если на классах и CLOS, то есть на Common Lisp: http://common-lisp.net/~frideau/lil-ilc2012/lil-ilc2012.html

monk ★★★★★
()
Ответ на: комментарий от monk

ах да, если не ошибаюсь, еще отдельно от этого имеется специальный костыль специальная фича, которая позволяет статически упасть, если количество аргументов в двух списках отличаются (т.е. емнип можно zip-овать variadic templates parameters packs; хотя если и нельзя, то можно придумать аналогичную шаблонную магию)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

число типов в с++ счетно

Там речь шла про числовые типы. Их в C++: (unsigned) int, (unsigned) long, (unsigned) long long, (unsigned) char, float, double, long double. Больше нету.

Поэтому их можно использовать как классы. А если у тебя есть специализация f(<U 2 3 4>) и f(<U 1 2 3>), то какую из них использовать для f(2)?

monk ★★★★★
()

Корпорация с миллиардами не может $15000?

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от monk

Чем тебе не хватило Quicklisp в качестве библиотеки и SBCL declare type в качестве типизации?

Quicklisp... ОК. Я хочу, например, элементарного. Реализацию trie. Или hashmap. Или работы с календарями.

Захочу на сайт, смотрю на «any of over 700 libraries» и клацаю на ссылку. Вижу длинную таблицу на весь экран с двумя колонками. В одной - названия вроде «3bmd-20130615-git», в другой - «3bmd-ext-code-blocks, 3bmd-ext-definition-lists, 3bmd-ext-wiki-links, 3bmd». Круто, чё.

Ну думаю, давай воспользуюсь поиском. Пишу на той же странице в строке поиска «trie» и жму search. Получаю зелёного монстра с флагом и надписью «Quickdocs.org is under maintainance. Sorry.».

Может мне нужно было поиск описывать в виде S-выражения?

Шутки-шутками, то quicklisp ещё в бете, да и библиотек там негусто. А что самое главное, статус этих библиотек, их содержание и качество - сомнительны.

Что до SBCL declare type, то где я могу почитать про нынешнее состояние дел с ним? И в частности, про поддерживаемые типы. Последний раз на него глядел более 7 лет назад. Там можно было сказать «тип integer от 3 до 9 (или типа того)», но: 1. проверки типов отключались без проблем 2. сами проверки были очень часто во время исполнения а не на этапе компиляции 3. не было элементарных вещей вроде discriminating unions 4. CLOS конечно силён, но поскольку он на макросах, сообщения компилятора были часто безумны

Ну или, если хочется именно в _стандартной библиотеке_, то бери Racket (или Typed Racket с типизацией): в стандартной библиотеке GUI, многопоточность, XML, SSL, JSON, ...

На Racket я недавно смотрел и забил. Это - не production ready язык. И никогда им не станет.

rtvd ★★★★★
()
Ответ на: комментарий от rtvd

Да вряд ли их Александреску выплатит Брайту, даже если он их пофиксит (в чем я сомневаюсь). Так что не светит ему. Кстати, первая выплата в размере 80$ уже осуществлена. :)

Андрей четко написал в своем посте, что они (FB) будут внимательно следить как их инициатива скажется на сообществе и он очень не хотел бы чтобы сообщество перестало развивать язык в ожидании вознаграждения. Там и суммы-то маленькие за исправления бага - от 30$ до 100$ (тролли, можете приступать кушать, ну или жрать, как там у вас принято, не знаю). Тут важнее что движения вокруг языка не останавливаются и продолжаются, чтобы не говорили тут на Лоре. Кому делать нечего буду поливать грязью и писать очередную хрень не в тему, кому реально интересно - посмотрят на язык и попробуют его. Обычно большинство высказавшихся тут даже не пробовали о чем говорят. Либо пробуют исключительно с целью доказать, что язык - говно. Они специально выбирают самые-самые говенные места(ну а жопа - она же есть у всех), откусывают оттуда шмат дерьма и потом кричат, что вот оно - говно! Я нашел его! И самое интересное - они счастливы в своих поисках дерьма! Да бог им судья. Я уверен, что есть люди которые ищут альтернативы уже известным им инструментам. Не потому что их инструменты плохие, а потому что им хочется большего. Такие люди даже не будут тратить время на обливание грязью - они просто пробуют и если им нравится, они пользуются этим. Если им не подошел инструмент, они им не пользуются. Логично, на мой взгляд.

yetanother ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.