Semantic IDE - www.sem-tech.net
От: LaptevVV Россия  
Дата: 27.12.12 07:19
Оценка:
Наш сайт: www.sem-tech.net

Введение


Для разработки программного обеспечения в настоящее время используются интегрированные среды (Integrated Development Environment, IDE), объединяющие в единую систему множество необходимых системных программ: редактор, компилятор (и/или интерпретатор), компоновщик, отладчик, профайлер, мейкер и другие. При этом взаимодействие пользователя-программиста с интегрированной средой осуществляется в редакторе кода, который и обеспечивает интерфейс.

Редактор кода в интегрированной среде представляет собой обычный текстовый редактор, в котором основные операции выполняются с символами, строками и блоками текста. Однако современные промышленные среды осуществляют поддержку языка разработки, обеспечивая некоторый набор операций для манипуляций конструкциями языка программирования и объектами программы. Результаты этих операций обычно отражаются в окне редактора. Поддержка языка программирования, как правило, заключается в следующем:
    1. Осуществляется цветовая подсветка синтаксиса – ключевых слов и объектов про-граммы; подсветка обычно настраивается в самом редакторе.
    2. Обеспечивается возможность закомментировать выделенный блок строк, и отменить комментарий.
    3. Предоставляется возможность сворачивать и разворачивать блоки кода: классы, моду-ли, условные операторы и операторы цикла, определения подпрограмм, пространства имен, регионы и т.п.
    4. Обычно проверяется парность разнообразных скобок, используемых в коде.
    5. Предоставляется набор так называемых сниппетов (snippets), обеспечивающих встав-ку конструкций языка по короткой комбинации клавиш или даже по одной горячей клавише; сниппеты тоже разрешается настраивать в редакторе.

В последние годы в некоторых средах (например, в Visual Studio.NET) выполняется интеллектуальный анализ кода (технология IntelliSense) уже при его создании (до компиляции) [1]. Подобный механизм обеспечивает более богатые возможности:
    1. При наборе кода непосредственно в окне редактора предоставляются интеллектуальные подсказки, позволяющие не набирать конструкцию (например, вызов методов класса стандартной библиотеки) до конца вручную, а выбрать нужную из списка, который появляется при наборе нескольких первых символов.
    2. Обеспечивается возможность получения краткой информации об объектах программы (например, информация о составе методов класса или о списке параметров метода) непосредственно в редакторе без обращения к системе помощи.
    3. Обеспечивается интеллектуальная навигация по коду, например, переход от вызова метода к его определению и обратно.
    4. Ошибочные конструкции помечаются в окне редактора в момент набора (без компиляции), и в отдельном окне выводятся сообщения о возможных ошибках.

В наиболее развитых средах осуществляется поддержка операций рефакторинга [1,2]. Подобные возможности позволяют значительно снизить количество ошибок при наборе кода, и существенно повышают производительность программиста. Тем не менее, подавляющее большинство операций, которые программист выполняет в редакторе, – это обычные операции с текстом. Отметим, что применение текстовых операций к коду программы приводит к тому, что регулярно нарушается синтаксис языковых конструкций. Это нередко приводит к возникновению мелких синтаксических ошибок и в итоге – к непроизводительным затратам времени.

Как правило, редактор интегрированной среды сохраняет код программы в текстовом виде. Этот текстовый файл можно открыть, просмотреть и изменить вне интегрированной среды, например, в Блокноте. Текстовое представление кода является входным для компилятора, что приводит к необходимости иметь в компиляторе фазы лексического и синтаксического анализа.

Б. Страуструп в своей книге [3] отмечал, что главным препятствием на пути развития языка С++ являются символьно-ориентированные инструменты. Наиболее перспективный и интересный подход – отказаться от традиционного текстового представления и реализовать инструментарий на основе семантических понятий языка программирования. В этом случае синтаксис языка представляет собой интерфейс между языком и пользователем. И как всякий интерфейс, его можно изменять, не изменяя базовой семантики языка.
В данной статье рассматривается концепции и описывается реализация интегрированной среды Semantic IDE на основе базовой семантики языка программирования.

Концепции разработки


Анализ, выполненный в работе [4], позволил сформулировать требования к обучающей среде. В частности, обучающая среда должна включать исполняющую подсистему, в которой реализуются программы на учебном языке программирования [5]. Среда должна обеспечивать возможности создания и редактирования кода программы, а также обеспечивать возможность выполнения программы. Поскольку объем кода в учебных программах очень небольшой, и учебные программы в большинстве своем весьма просты, то было принято решение реализовать сначала чистый интерпретатор. Реализация интерпретатора основана на немногих следующих концепциях:

Более важными являются концепции реализации редактора кода и интегрированной среды, так как именно редактор кода определяет интерфейс с пользователем:

В настоящее время практически все эти концепции реализованы в рамках интегрирован-ной среды Semantic IDE.

Среда Semantic IDE и семантический редактор


Внешний вид среды Semantic IDE показан на рис. 1. Центральное окно – это окно редактора кода. В нем пользователь набирает код программ и пишет текст документов. Для каждого документа создается отдельная вкладка. Над центральным окном расположены главное меню и лента выбранного пункта главного меню.
Справа расположено окно проектов. В нем отображается текущий проект, с которым работает пользователь в данный момент. Внизу – окно сообщений об ошибках, и окно консоли. Сообщения об ошибках появляются при редактировании кода программы – для этого не требуется запускать программу на трансляцию. При выполнении программы среда переключается в окно консоли, в котором программист задает входные данные и в которое выводятся результаты работы программы. При работе в среде не открывается никаких дополнительных окон со стороны операционной системы.

Программисту часто приходится выполнять поиск различных имен в проекте. Например, требуется найти все вызовы конкретной функции, или от вызова функции перейти к определению, чтобы уточнить список параметров и их типы. Для выполнения подобных операций предназначено отдельное окно, в котором выводятся результаты поиска имен.

Среда Semantic IDE предназначена в первую очередь для обучения начинающих программистов. Поэтому среда должна отслеживать все действия пользователя, собирать и обрабатывать статистику по операциям, совершаемым пользователем. Для вывода действий пользователя предназначено окно Команды (вкладка слева).
Все окна, кроме центрального окна редактора, являются плавающими и их можно перемещать и закреплять как вкладки в центральном окне.


Рис. 1. Внешний вид Semantic IDE

Проекты


Работа в интегрированной среде начинается с создания проекта. Проект – это набор файлов, созданных в редакторе кода и сохраненных на диск. При создании проекта на диске создается папка, в которую заносится файл проекта. В этой же папке хранятся все файлы проекта. Проект создается даже в том случае, если включает единственный файл.

Любой файл, созданный в редакторе кода, может содержать программный модуль на учебном языке. В этом случае имя модуля и имя файла должны совпадать. Но в общем случае это не обязательно – файл может содержать только неисполняемую информацию, и его имя в этом случае может быть произвольным. Таким образом, в редакторе кода можно создавать как программные, так и информационные проекты. Примером информационных проектов в Seman-tic IDE являются проект справочной системы и проект Задачник (см. рис.1 – вкладки слева), содержащий список лабораторных работ.

Программные проекты могут быть исполняемыми и неисполняемыми. Примером неисполняемого проекта в среде является проект Framework, в котором собраны модули стандартной библиотеки. Если программный проект требуется выполнять, то один из модулей назначается стартовым: выполнение программы начнется с секции инициализации этого модуля. Остальные модули загружаются и связываются по мере необходимости.

Исполняемый проект, который предоставляется в составе Semantic IDE, является проект Example, в который включено множество примеров программ на учебном языке (см. рис. 1).

Для хранения проектов на диске был разработан xml-формат. Файл проекта сохраняется в папке проекта в виде xml-файла с расширением «prj». Например, текущая версия проекта Задачник выглядит следующим образом:
<?xml version="1.0" encoding="utf-16"?>
  <Project Name="Задачник" StartupModule="#notset">
    <Files>
        <Module Path="Лабораторная_1.sl" />
        <Module Path="Лабораторная_2.sl" />
        <Module Path="Лабораторная_3.sl" />
        <Module Path="Лабораторная_4.sl" />
        <Module Path="Лабораторная_5.sl" />
        <Module Path="Лабораторная_6.sl" />
        <Module Path="Лабораторная_7.sl" />
        <Module Path="Лабораторная_8.sl" />
        <Module Path="Лабораторная_9.sl" />
        <Module Path="Лабораторная_10.sl" />
        <Module Path="Лабораторная_11.sl" />
    </Files>
  </Project>

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

Редактирование кода

Редактор кода – это не традиционный текстовый редактор. В Semantic IDE реализован семантический редактор. Как было описано в работе [4], семантический редактор оперирует не символами, а операторами учебного языка и объектами программы. Поэтому, во-первых, большинство элементов оператора сразу вставляются в код в правильном виде, и во-вторых, полностью исчезают ошибки набора ключевых слов. Редактор разрешает символьный ввод только в строго определенных позициях оператора. Например, в операторе объявления переменной разрешено вводить посимвольно имя переменной.

Операторы учебного языка добавляются в код программы с помощью сниппетов и контекстного подсказчика. Каждый оператор учебного языка [5] начинается ключевым словом, поэтому сниппет – это первые две-три буквы ключевого слова оператора. При наборе первой буквы сниппета в окне редактора выводится список операторов, и подсветка устанавливается на конкретном операторе (рис. 2). Подсказчик можно вызвать без ввода сниппета, нажав горячую клавишу.

Редактор следит за действиями программиста и сообщает об ошибках в момент набора программы. Пока ввод оператора не завершен, в окне ошибок «вывешены» сообщения об ошибках, которые возникают по мере ввода составных частей оператора. Например, при ошибке в написании имени переменной во время ввода оператора мгновенно появляется сообщение о том, что данная переменная не была определена. Отметим, что в кодовом окне нумеруются операторы, а не строки текста.

Аналогично выполняется удаление – удаляется вся конструкция целиком. Если же опера-тор не удаляется, то разрешается замена элементов оператора. При замене тоже работает контекстный подсказчик. Например, в операторе объявления переменной можно заменить тип переменной, выбрав его из списка предложенных типов. Точно так же предлагается список видимых в данной точке имен переменных, если программисту потребовалось заменить имя переменной. Контекстный подсказчик работает и при наборе составного имени при вводе селектора-точки. Если перед точкой было набрано имя модуля, то в списке выводится список всех откры-тых объектов этого модуля. Если перед точкой было набрано имя объекта определяемого типа, то подсказчик выводит имена всех открытых полей и методов, определенных в этом типе.

Заметим, что программа при вводе/удалении операторов всегда является синтаксически правильной – синтаксические ошибки отсутствуют. Даже при наборе арифметических или логических выражений всегда вставляется (и удаляется) синтаксически правильная часть выражения. Все возникающие ошибки являются исключительно лексическими (например, неверно набранная числовая константа) или семантическими (например, в операторе присваивания требуется недопустимое преобразование типа). Таким образом, набор кода программы требует минимального количества действий от программиста, и при этом существенно сокращается количество ошибок.


Рис. 2. Сниппет и контекстный подсказчик

Поскольку традиционные текстовые операции с кодом отсутствует, редактор обеспечивает набор высокоуровневых семантических операций модификации, которые не нарушают синтаксической корректности кода. Примером подобной операции может служить вставка объемлющего оператора цикла, преобразующая выделенную последовательность операторов в тело этого цикла. Как обычно, доступные операции можно выбирать из списка, предоставляемого контекстным подсказчиком.

Оператор-комментарий


Одним из операторов, размещаемых в код, является оператор-комментарий, обозначаемый в окне редактора символом «решетка» (#). Оператор-комментарий может быть вставлен в код в любом месте, где допускается вставка оператора языка программирования. Однако в семантическом редакторе оператор-комментарий трактуется более широко, чем обычные комментарии в программах: оператор-комментарий может быть вставлен как перед программным кодом, так и после него. Более того, среда позволяет создавать в редакторе произвольную последовательность операторов-комментариев даже без программного кода.

В пределах оператора-комментария разрешается посимвольный ввод любого текст, и выполняются традиционные текстовые операции с символами и строками. Разрешены и обычные операции с буфером обмена – это позволяет вставлять фрагменты материалов из других про-грамм (например, из MS Word). Шрифт комментария можно настраивать обычными для системы Windows способами. В операторе-комментарии можно создать таблицы, разрешается вставлять рисунки. Обеспечивается возможность связывания операторов-комментариев с помощью гиперссылок. Таким образом, возможности представления информации практически не уступа-ют возможностям подготовки документа в MS Word в rtf-формате.

Все это позволяет непосредственно в редакторе готовить обучающие материалы. Проект справочной системы и проект Задачник подготовлены именно таким образом. Заметим, что в документе может содержаться код программы-примера, которую можно выполнить. Во-первых, подобным образом можно демонстрировать образцы хорошего стиля программирования. Во-вторых, возможность выполнения программы-примера способствует более быстрому и прочному усвоению изучаемой темы.

Синтаксис как интерфейс

Внутренним представлением программы в редакторе кода является семантический граф, который является входным для интерпретатора. Один модуль многомодульной программы – это семантическое дерево (рис. 3) с вершинами сложной структуры. Каждый вершина (узел) дерева представляет отдельный оператор программы. В каждом узле имеются две ссылки: на следующий узел, и на дочерний. Следующий узел – это следующий оператор в коде программы. Таким образом, последовательность операторов представляет собой последовательный список узлов семантического дерева. Дочерний узел представляет собой тело блочного оператора, к которым в учебном языке [5] относятся оператор определения модуля, оператор определения типа, оператор определения процедуры/функции, условный оператор и оператор цикла. В операторах, не имеющих тела, ссылка на дочерний узел не используется.
Для многомодульной программы редактор строит полный семантический граф, начиная со стартового модуля, и включает в него все импортированные модули. Таким образом, семантический граф представляет собой совокупность связанных семантических деревьев.


Рис. 3. Вид семантического дерева модуля

Представление программы в виде семантического графа позволяет реализовать идею Б.Страуструпа о том, что синтаксис языка программирования является только интерфейсом [3], который, вообще говоря, можно менять, выбирая наиболее удобный. В Semantic IDE помимо синтаксиса Semantic Language реализованы С-подобный, Pascal-подобный и Python-подобный синтаксисы. Кроме того, каждое из представлений может быть показано как в русской лексике, так и в английской. В таблице показана одна и та же программа вычисления факториала, пред-ставленная в разных синтаксисах в англоязычной лексике.

Представление одной программы в разных синтаксисах
Semantic Language    
module Факториал
start
    variable-integer i := 1;
    variable-real current := 1;
    constant integer N = 15;
        while i < N repeat
        let current := current * i;
        let i := i + 1;
        output '\n';
        output current;
    end of while;
end Факториал.


С-подобное представление — здесь зеленым покрашено (не входит в язык): function var const let и комментарии после закрывающих скобок.
package Факториал;
function Main() {
var int i = 1;
var double current = 1;
const int N = 15;
while (i < N) {
let current = current * i;
let i = i + 1;
Console.Write('\n');
Console.Write(current);
}
} конец Main
конец Факториал

Pascal-подобное представление — здесь зеленым покрашено LET.
MODULE Факториал;
BEGIN
    VAR i: INTEGER := 1;
    VAR current: REAL := 1;
    CONST N: INTEGER = 15;
    WHILE i < N DO
        LET current := current * i;
        LET i := i + 1;
        Log.Out('\n');
        Log.Out(current);
    END
END Факториал.


Си-подобный синтаксис разработан на основе языка Java [7], а за образец Pascal-подобного принят синтаксис языка Component Pascal [8], реализованного в системе BlackBox Component Builder. В таблице зеленым цветом показаны слова, которые не являются ключевыми словами в соответствующем языке.
Естественно, пока в представлениях есть некоторые расширения базового синтаксиса. Но в дальнейшем мы от них избавимся.

Каждый синтаксис определяется собственной грамматикой. Грамматика нужна только для определения внешнего представления программы в окне редактора кода, и не используется для синтаксического анализа программы. Например, грамматика для Pascal-подобного представления в русской лексике выглядит так:
<Root> ::= <body>
<While> ::= "ПОКА" <expression> "ПОВТОРЯТЬ"<body>"КОНЕЦ"
<DoNTimes> ::= "СДЕЛАТЬ" <expression> "РАЗ"<body>"КОНЕЦ"";"
<Array> ::= "%МАССИВ" <name>":" "МАССИВ" <expression> "ТИПА" <Type>";"
<Assign> ::= "%ПРИСВОИТЬ" <expression> ":=" <expression>";"
<Call> ::= "%ВЫЗВАТЬ" <expression>";"
<Constant> ::= "КОНСТАНТА" <name>":" <Type> "=" <expression>";"
<Field> ::= "%ПОЛЕ" <name><visibility>": "<Type>";"
<Input> ::= "Журнал.Ввести""("<expression>");"
<NullOperator> ::= <keyword>
<Output> ::= "Журнал.Вывести""("<expression>");"
<Return> ::= "ВЕРНУТЬ" <expression>";"
<Variable> ::= "ПЕРЕМЕННАЯ" <name>":" <Type>";"
<VariableWithInit> ::= "ПЕРЕМЕННАЯ" <name>":" <Type> ":=" <expression>";"
<Beginning> ::= "НАЧАЛО"<body>
<Comment> ::= "(*" <text> "*)"
<Do> ::= "ПОВТОРЯТЬ"<body>"ПОКА" <expression>";"
<Else> ::= "ИНАЧЕ"<body>
<ElseIf> ::= "А ЕСЛИ" <expression> "ТОГДА" <body>
<If> ::= "ЕСЛИ" <expression> "ТОГДА" <body> "КОНЕЦ"
<Module> ::= "МОДУЛЬ" <name>";"<body>"КОНЕЦ" <endname>";"
<RecordType> ::= <visibility> "ЗАПИСЬ" <name> "=" "ЗАПИСЬ""("<Type>")"<body>"КОНЕЦ" <endname>";"
<Function> ::= <visibility> "ПРОЦЕДУРА" <name>  "(" <FormalParameters> "):" <Type> ";" <body> "КОНЕЦ" <endname>";"
<Procedure> ::= <visibility> "ПРОЦЕДУРА" <name> "(" <FormalParameters> ");" <body> "КОНЕЦ" <endname>";"
<MethodFunction> ::= <visibility> "ПРОЦЕДУРА""("<ClassParameter>")" <name> "(" <FormalParameters>")" ":" <Type> ";" <body> "КОНЕЦ" <endname>";"
<MethodProcedure> ::= <visibility> "ПРОЦЕДУРА" "(" <ClassParameter>")" <name> "("<FormalParameters> ");" <body> "КОНЕЦ" <endname>";"
<Parameter> ::= <mode> <name>":" <Type>
<TypeParameter> ::= <mode> <Type>
<ArrayType> ::= "МАССИВ" <expression> "ТИПА" <Type>
<ProcedureType> ::= "ПРОЦЕДУРА" "("<FormalParameters>")"
<FunctionType> ::= "ПРОЦЕДУРА" "(" <FormalParameters> ") :" <Type> 
<PointerType> ::= "УКАЗАТЕЛЬ НА" <Type> 
<SimpleType> ::= "ЦЕЛОЕ"|"ВЕЩЕСТВЕННОЕ"|"БУЛЕВСКОЕ"|"СИМВОЛ"
<ClassParameter> ::= <name>":" <Type>
<Import> ::= "ПОДКЛЮЧИТЬ" <name>";"

Нетерминалы в левой части каждого правила – семантические единицы (понятия), которые в семантическом дереве представлены узлами. В правой части правил – синтаксис представления этого понятия в окне редактора. Последовательности символов, заключенные в кавычки, выводятся буквально в окно редактора.
Нетерминалы, представленные в правых частях правил, представляют собой параметры данной семантической единицы (узла в семантическом дереве). Например, нетерминал <body> в правиле говорит о том, что данная семантическая единица имеет тело, что в узле дерева представляется ссылкой на дочерний узел.

Знак процента «%» в начале слова означает, что это слово является исключением в данном синтаксисе. Например, в Pascal-подобных языках отсутствует ключевое слово «присвоить» (английское слово «let») в операторе присваивания. Обычно ключевые слова в коде изображаются синим цветом, а слово-исключение выводится другим цветом (в таблице они показаны зеленым цветом).

Подчеркнем, что подобная грамматика используется редактором только для изображения программы на экране, а не для синтаксического анализа. В правой части некоторых правил встречаются семантические понятия, для которых в грамматике не определено правило. Например, для понятия <Type> может быть определено следующее правило:
 <Type> ::= <RecordType> | <ArrayType> | <ProcedureType> | <FunctionType> | <PointerType> | <SimpleType>

Однако в правой части этого правила нет ни одного изображаемого элемента. Еще раз подчеркнем, что грамматика определяет внешнее представление программы и не используется для синтаксического анализа. Подобные правила, в которых в правой части нет ни одного изображаемого элемента, бесполезна. Поэтому эти правила из грамматики исключены.

Для хранения программ на диске был разработан xml-формат. Файл с кодом программы сохраняется в папке проекта в виде xml-файла с расширением «sl».

<?xml version="1.0" encoding="utf-16"?>
<Root Id="16" Version="1.1">
<Module NameWord="Факториал"></Module>
<Beginning>
  <VariableWithInit NameWord="i" TypeWord="1«целое" 
                    Expresion= "Operand§1">
  </VariableWithInit>
  <VariableWithInit NameWord="current" TypeWord="1«вещественное"
                    Expression="Operand§1">
  </VariableWithInit>
  <Constant NameWord="N" TypeWord="1«целое" 
            Expression="Operand§15">
  </Constant>
  <While Expression="Operand§iSpace§ 
                     BinaryOperation§&lt;Space§ 
                     Operand§N">
    <Assign RightValue="Operand§currentSpace§ 
                        BinaryOperation§*Space§ Operand§i" 
            LeftValue="Operand§current">
    </Assign>
    <Assign RightValue="Operand§iSpace§ 
                        BinaryOperation§+Space§ Operand§1" 
            LeftValue="Operand§i">
    </Assign>
    <Output Expression="Operand§'\n'"></Output>
    <Output Expression="Operand§current"></Output>
  </While>
</Beginning>
</Root>

Каждому оператору в окне редактора соответствует конкретный тег в xml-представлении, а элементам оператора соответствует параметры в теле тега. Редактор при чтении файла преобразует каждый тег в узел семантического дерева, а параметры тега определяют соответствующие параметры узла.

Заключение

Описанная в данной статье интегрированная среда Semantic IDE является основой обучающей системы, требования к которой изложены в [4]. Реализация Semantic IDE выполняется на языке C# в среде Visual Studio 2012. Уже полностью реализована вся процедурная составляющая учебного языка, и большая часть объектно-ориентированной. Разработана начальная версия системной библиотеки.

В настоящее время среда апробируется в учебном процессе на кафедре АСОИУ для вы-полнения лабораторных работ по дисциплине «Основы алгоритмизации». Небольшой пока опыт использования показывает существенное сокращение времени по созданию кода программы и практически полное исчезновение ошибок набора. По отзывам преподавателей представление программы в русской лексике облегчает студентам усвоение базовых понятий и способствует усвоению профессиональной терминологии.

В настоящий момент развитие среды продолжается: реализуется механизм наследования в объектно-ориентированной части языка Semantic Language, разрабатываются модули стандартной библиотеки, развивается система справочной информации, дополняется проект Задачник.

СПИСОК ЛИТЕРАТУРЫ

1. Пауэрс, Л. Microsoft Visual Studio 2008 / Л. Пауэрс, М. Снелл: Пер. с англ. – СПб.: БХВ-Петербург, 2009. – 1200 с.
2. Давыдов С.В., Ефимов А.А. IntelliJ IDEA. Профессиональное программирование на Java. – СПб.: БХВ-Петербург, 2005. – 800 с.
3. Страуструп Б. Дизайн и эволюция С++. – М.: ДМК Пресс; СПб.: Питер, 2006. – 448 с.
4. Лаптев В.В. Требования к современной обучающей среде по программированию // Объектные системы-2010 (Зимняя сессия): материалы II Международной научно-практической конференции. Россия, Ростов-на-Дону, 10-12 ноября 2010 г. / Под общ. Ред. П.П. Олейника. – Ростов-на-Дону, 2010. – с. 104-110.
5. Грачёв А.Д., Лаптев В.В. Разработка языка программирования для обучающей среды //
6. Дейкстра Э. Дисциплина программирования. – М.: Мир, 1978. – 275 с.
7. Хорстман К.С., Корнелл Г. Java 2. Библиотека профессионала, том 1. Основы. – М.: ООО «И.Д. Вильямс», 2011. – 816 с.
8. Потопахин В. Современное программирование с нуля! – М.: ДМК Пресс, 2010. – 240 с.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Отредактировано 21.12.2014 9:46 LaptevVV . Предыдущая версия . Еще …
Отредактировано 21.12.2014 9:44 LaptevVV . Предыдущая версия .
Отредактировано 21.12.2014 9:41 LaptevVV . Предыдущая версия .
Отредактировано 21.12.2014 9:40 LaptevVV . Предыдущая версия .
Отредактировано 21.12.2014 9:35 LaptevVV . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.