Генерация кодов программ это

Перевод статьи от Роба Пайка про генерацию кода. Из официального блога.

Свойство универсальных вычислений — Тьюринг полнота — это способность одной программы написать другую программу. Это мощная идея, которая, как правило, остается недооцененной. Например, это одна из основных частей, определяющих компилятор. Так же, по этому принципу работает команда для тестирования: сначала сканируются пакеты, которые должны быть протестированы, создается временная программа содержащая обвязку для тестов и кастомизированная для конкретного пакета, затем эта временная программа компилируется и запускается. Хотя это довольно большая последовательность действий, современные компьютеры достаточно быстры, чтобы выполнить ее за миллисекунды.

Есть много других примеров, когда программы пишут программы. Yacc, к примеру, читает описание грамматики и генерирует программу, которая может разбирать эту грамматику. «Компилятор» протобафа(Protocol Buffers) читает описание интерфейсов и генерирует определения структур, методов и другой сопутствующий код. Различные средства конфигурации работают аналогичным способом, получая информацию о окружении и генерируя различные скрипты, кастомизированные под конкретное окружение.

Автоматическая генерация кода

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

Но теперь такая возможность появилась.

Последний релиз Go 1.4 включает новую команду, которая упрощает запуск таких тулз. Она называется go generate . Работает сканируя определенные комментарии в исходниках go кода, которые определяют какие средства генерации нужно запустить. Важный момент для понимания: go generate не является частью go build . Эта команда не анализирует зависимости и должна запускаться до go build . Кроме того, генерация должна использоваться только автором пакета, но не его пользователями.

Использовать go generate довольно просто. Для разминки, посмотрим как генерировать Yacc грамматики. Предположим, у вас есть входной файл gopher.y который описывает грамматики для вашего нового языка. Для получения исходника .go реализующего эту грамматику, нам, как правило, нужно вызвать стандартную версию yacc для Go:

go tool yacc -o gopher.go -p parser gopher.y

Параметр -o определяет имя выходного файла, -p указывает пакет.

Чтобы воспользоваться командой go generate в любом не генерируемом .go файле укажите специальный комментарий в любом месте файла:

//go:generate go tool yacc -o gopher.go -p parser gopher.y

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

G-коды для ЧПУ фрезера для начинающих.

Теперь запустим все это. Заходим в директорию с проектом и выполняем go generate затем go build и так далее:

$ cd $GOPATH/myrepo/gopher $ go generate $ go build $ go test

Вот так это работает. Предположим, что у нас не было никаких ошибок, команда go generate запустила yacc который создаст gopher.go. После этого у нас будут все необходимые файлы для сборки проекта, тестирования и нормальной работы. После каждой модификации gopher.y необходимо просто запустить go generate для перегенерирования парсера.

Смотрите документацию для более подробного понимания работы go generate , включая параметры, переменные окружения и все остальное.

Эта команда не делает ничего, что нельзя сделать с помощью Make или других инструментов сборки, но теперь это идет в коробке и прекрасно вписывается в Go экосистему. Только не забывайте, это инструмент для разработчиков пакетов но не для их пользователей. Кроме того, если пакет должен быть получен с помощью go get , как только файл будет сгенерирован и оттестирован он должен быть добавлен в систему контроля версий и быть доступным для клиентов.

Теперь попробуем что то новенькое. Совершенно другой пример выгоды от go generate — возможность использовать программу stringer доступную как часть golang.org/x/tools. Эта программа автоматически генерирует строковые методы для наборов целочисленных констант. Она не является частью стандартной поставки, но ее легко установить дополнительно:

$ go get golang.org/x/tools/cmd/stringer

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

package painkiller type Pill int const ( Placebo Pill = iota Aspirin Ibuprofen Paracetamol Acetaminophen = Paracetamol )

Для отладки неплохо было бы красиво и информативно выводить отображать константы, что означает, нам нужен метод с такой сигнатурой:

func (p Pill) String() string

Который реализуется вот так:

func (p Pill) String() string < switch p < case Placebo: return «Placebo» case Aspirin: return «Aspirin» case Ibuprofen: return «Ibuprofen» case Paracetamol: // == Acetaminophen return «Paracetamol» >return fmt.Sprintf(«Pill(%d)», p) >

Конечно, есть и другие способы, чтобы написать эту функцию. Мы можем использовать слайс со строками и Pill в качестве индексов, или мапы, или другие способы. Но в любом случае, мы должны следить за списком таблеток и за правильностью этой функции (два названия парацетамола сделают эту задачу еще сложнее). Кроме того, сам вопрос какой подход использовать зависит от типов и значений: знаковые и беззнаковые, с нуля или нет и так далее.

Программа stringer заботиться о всех деталях. Хотя она может быть запущена отдельно, но также ее можно использовать совместно с go generate . Чтобы заюзать это, добавьте специальный комментарий рядом с определением типа:

//go:generate stringer -type=Pill

Это правило означает, что должна запуститься программа stringer, которая сгенерирует String метод для типа Pill . Результат работы программы запишется в файл pill_string.go (это название по умолчанию, его можно изменить с помощью флага -o ).

Давайте запустим это:

$ go generate

Содержимое файла pill_string.go:

// generated by stringer -type Pill pill.go; DO NOT EDIT package pill import «fmt» const _Pill_name = «PlaceboAspirinIbuprofenParacetamol» var _Pill_index = [. ]uint8 func (i Pill) String() string < if i < 0 || i+1 >= Pill(len(_Pill_index)) < return fmt.Sprintf(«Pill(%d)», i) >return _Pill_name[_Pill_index[i]:_Pill_index[i+1]] >

Читайте также:
Программа чтобы было много подписчиков в инстаграме

Теперь при изменении типа Pill , нам просто нужно будет запустить

$ go generate

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

Без сомнения, сгенерированный метод уродский. Это нормально, потому что люди не будут работать в рамках этого метода, а машино-сгенерированный код всегда уродский. Все имена объединены вместе в одну строку, которая сохраняется в памяти(только одно строка для всех имен, даже если имен будет 100500 миллионов). Дальше идет массив _Pill_index с индексами для имен, реализующий простую технологию, аналогичную map . Обратите внимание, что _Pill_index именно массив(а не слайс) с элементами типа uint8 — самый легкий интовый тип достаточный для охвата всего диапазона значений. Если у нас будет больше значений или появятся отрицательные, тогда, при генерации, тип автоматически замениться на более подходящий.

Подход, реализуемый в строковом методе который генерируется с помощью stringer, так же может меняться в зависимости от списка констант. К примеру, если константы не большие, можно использовать map . Вот тривиальный пример для констант степеней двойки:

const _Power_name = «p0p1p2p3p4p5. » var _Power_map = map[Power]string < 1: _Power_name[0:2], 2: _Power_name[2:4], 4: _Power_name[4:6], 8: _Power_name[6:8], 16: _Power_name[8:10], 32: _Power_name[10:12], . >func (i Power) String() string < if str, ok := _Power_map[i]; ok < return str >return fmt.Sprintf(«Power(%d)», i) >

Проще говоря, автоматическая генерация делает работу быстрей и эффективней чем живой человек.

Есть множество других способов использования go generate в Go. Это создание юникод-таблиц в пакете unicode , создание эффективных методов для кодирования и декодирования массивов в пакете encoding/gob , получение данных для временных зон в пакете time и многое другое.

Пожалуйста, используйте go generate творчески и экспериментируйте.

И обязательно используйте инструмент stringer для машинной генерации более эффективного кода.

Источник: kovardin.ru

11. Генерация программного кода

Одно из важных свойств Rational Rose — возможность генерации программного кода, пред­ставляющего модель. Варианты генерации программ меняются в зависимости от установленной версии Rose.

Процесс генерации программного кода состоит из шести основных этапов: проверка модели; создание компонентов; отображение классов на компоненты; установка свойств генерации программного кода; выбор класса, компонента или пакета; генерация программного кода.

В разных языках реализации не все этапы обязательны. Так, программы C++ генерируются и без предварите­льного создания компонентов. Создавать код программ на любом языке можно, не выполняя шага проверки модели, хотя во время генерации это порой приводит к различным ошибкам.

В Rose существует не зависящее от языка реализации средство проверки моделей, применяемое для обеспечения корректности модели перед генерацией программного кода (меню Tools > Check Model). Также можно обнаружить нарушения правил доступа, возникающие тогда, когда существует связь между двумя классами разных пакетов. При этом связи между самими пакетами нет.

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

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

Во время генерации программного кода Rose выбирает информацию из логического и компонентного представлений модели. За один раз можно создать класс, компонент или целый пакет. Программный код генерируется с помощью диаграммы или браузера. В результате работы Rose получается большой объем «скелетного» (skeletal) программного кода.

По умолчанию корневой каталог, применяемый для генерации программ, ─ это каталог с системой Rose. Изменить его можно с помощью установки свойств генерации программного кода.

После генерации файлов остается сделать два шага. Сначала разработчики кодируют каждую из операций классов. Затем проектируется графический пользовательский интерфейс. Для создания экранов и форм следует пользоваться средой выбранного языка программирования.

Упражнение 10

Для модели обработки заказов сгенерируйте программный код на языке С++.

Этапы выполнения упражнения

Ввод тел пакетов на диаграмму компонентов системы

1. Откройте диаграмму компонентов системы (System). Выберите в браузере пакет компонентов Entities: тело пакета Order. Перетащите тело пакета Order на диаграмму компонентов системы.

2. Повторите п. 1 для следующих компонентов (см. рис.11.1):

Entities: тело пакета OrderItem, Boundaries: тело пакета OrderOptions, Boundaries: тело пакета OrderDetail, Control: тело пакета TransactionMgr и Control: тело пакета OrderMgr.

Проверка модели Rose

Выберите в меню Tools > Check Model. Проанализируйте и исправьте все найденные ошибки в окне журнала.

Обнаружение нарушений правил доступа

Откройте главную диаграмму классов модели (Main) в логическом представлении (Logical view) модели. Выберите в меню Report > Show Access Violations. Проанализируйте и исправьте все нарушения правил доступа, показанные в соответствующем диалоговом окне.

Рис.11.1. Диаграмма компонентов системы Order

Установка языка С++

Выберите в меню Add-Ins > Add-In Manager. В диалоговом окне Add-In Manager поставьте флажок для варианта Rose C++ и нажмите кнопку Apply (Применить), затем ОК.

Назначение компонентам системы языка генерации программного кода

  1. Откройте спецификацию компонента Order (спецификацию пакета) в пакете компонентов Entities. Выберите в качестве языка реализации С++.
  2. Повторите п. 1 для остальных компонентов системы (см. рис.11.1).

Источник: studfile.net

Генератор исходного кода

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

Читайте также:
Что проходят во 2 классе по программе перспектива

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

Что такое генератор исходного кода?

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

Вам не придется писать код на языках программирования и использовать сложные схемы баз данных. Этот сгенерированный код может быть использован отдельно от генерирующей системы во время выполнения. Подобный уровень генерации кода может быть реализован и на таких платформах, как Visual Studio. Visual Studio позволяет вам писать код, а некоторые из его дополнительных модулей позволяют вам автозаполнять код.

Инженеры могут видеть пользовательский код в процессе его генерации с помощью генераторов исходных текстов вместо того, чтобы использовать языки программирования для написания кода. Они могут генерировать код, в котором новые исходные файлы могут быть внедрены в пользовательскую сборку на лету. Вы можете генерировать код, который выполняется во время компиляции.

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

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

Эти два фактора хорошо работают вместе, делая генерацию исходного кода более практичной, чем языки программирования. Это может быть гораздо проще, чем изучать целые языки программирования. Вся подробная информация, которую компилятор накапливает во время компиляции, может быть использована для анализа пользовательских программ. Программа из вашего генератора кода, основанная на информации, которую вы оценили, затем поступает в тот же компилятор. Некоторые распространенные генераторы с открытым исходным кодом включают компилятор FreeVASIC . Вы также можете использовать инструменты с открытым исходным кодом, такие как Visual Studio .

История генераторов исходного кода

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

Существует несколько генераторов кода, и они используются в различных областях. .NET 5 , и даже Microsoft начала работать в этой нише. Общим примером используемого инструмента генерации исходного кода является ADO.NET’s Entity Data Model Designer. Это визуальный инструмент разработки программного обеспечения, позволяющий создавать таблицы и их взаимосвязи.

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

Попробуйте no-code платформу AppMaster

AppMaster поможет создать любое веб, мобильное или серверное приложение в 10 раз быстрее и 3 раза дешевле

Преимущества генерации исходного кода

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

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

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

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

  • Улучшенное тестирование и стандарт

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

  • Стабильная архитектура и согласованность

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

Читайте также:
Настройка программы 1 с розница

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

Недостатки генерации исходного кода

Есть некоторые недостатки генерации исходного кода вместо использования языков программирования. Вот некоторые недостатки генерации кода, о которых вы должны знать:

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

Модели, используемые в инструментах генерации исходного кода, могут становиться все сложнее и сложнее, особенно это касается схем баз данных.

Инструменты генерации кода могут создавать слишком много кода. Чтобы убедиться в эффективности сгенерированного кода, пользовательский код должен пройти проверку. Если пользовательский код будет неоправданно длинным, это может привести к осложнениям и путанице в дальнейшем.

Как работают генераторы кода?

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

Чтобы сгенерировать любой код, используются такие пользовательские шаблоны и предоставляется некоторая информация для работы. Шаблон обычно включает в себя способ выполнения таких распространенных операций языка программирования, как циклы и селекты, а также некоторый метод обработки информации. Таким образом, вместо того чтобы тратить неделю на ручное кодирование совершенно одинаковых, но разных HTML-файлов, вы можете сэкономить кучу времени, используя генерацию кода.

Какой генератор кода лучше?

AppMaster Это идеальное решение, если вы ищете хороший no-code инструмент для разработки программного обеспечения, который упростит вашу работу. Вы можете поручить один и тот же программный проект как команде программистов, так и no-code инструмент и получить больше преимуществ от no-code платформа. Для чего угодно — от простых операций до интеграции API, AppMaster может разработать сгенерированный код для вас. Платформа выполнит ваш проект быстрее, эффективнее и за меньшие деньги.

Попробуйте no-code платформу AppMaster

AppMaster поможет создать любое веб, мобильное или серверное приложение в 10 раз быстрее и 3 раза дешевле

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

Как AppMaster генерирует код?

Платформа AppMaster платформа может генерировать код, потому что платформа содержит все требования к бэкенду, схемы баз данных, фронтенд, мобильные приложения и даже структуры данных. Наша платформа начинает с модели данных, когда пользователь нажимает кнопку публикации. Она собирает все модели данных.

На основе этих моделей данных она строит стандартную схему базы данных, которая будет помещена в двоичный файл бэкенда приложения. После того, как основная структура таблиц и SQL построены запросы, и как только завершены запросы к схеме базы данных, все бизнес-процессы, находящиеся внутри системы, начинают генерировать код. Поскольку AppMaster платформа делает все полностью на RAM , мы достигли скорости генерации исходного кода 22 000 строк кода в секунду.

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

Неоптимальные места в исходном коде при первоначальной генерации кода были созданы потому, что мы ориентируемся на бизнес-процессы, которые создал человек, и, как правило, мало кто из пользователей и разработчиков может с первого раза сделать хороший уровень абстракции и правильно разместить все блоки и выстроить логику с самого начала. Но благодаря тому, что у нас есть ИИ, мы проходим так называемую постпроцессинговую обработку, проходим по кодовой базе еще раз и улучшаем всю кодовую базу. Это приводит к тому, что двоичные файлы уменьшаются, то есть становятся меньше. Они загружаются быстрее. Это может повысить производительность, поскольку они работают лучше, и в целом пост-оптимизация работает для нас на самом высоком уровне.

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

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

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

Например, бизнес-логика очень зависит от модели данных, а конечные точки зависят от бизнес-логики и модели данных. UI Элементы, в свою очередь, зависят от всего, включая конечные точки модели данных и бизнес-логики. И часто нашей платформе приходится автоматически решать проблему, что делать, когда пользователь делает очень большие радикальные изменения. Например, в модели данных он удаляет какие-то сущности, меняет тип полей и так далее. То есть наша система, основываясь на предыдущем опыте и нашей нейронной сети, автоматически перестраивает все связи в блоках бизнес-логики, конечных точках, а в некоторых случаях, UI элементы тоже.

Заключение

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru