1. Языково-ориентированная разработка. (0.3 мин) Языково-ориентированная разработка -- это подход к созданию программного обеспечения, который предполагает проектирование предметно-ориентированных языков для выполнения задачи. Существует широкий класс задач, для которых введение нового языка является достойным рассмотрения, а порой и единственным, вариантом решения. 2. Упрощённая обработка формальных языков. (0.5 мин) Однако языки, которые проектируются для конкретной задачи, часто бывают очень простыми и для обработки не требуют сложных механизмов, которые свойственны средствам обработки языков программирования общего назначения. В частности, зачастую язык настолько простой, что для него нет смысла разделять стадии построения синтаксического дерева и статического анализа и можно производить вычисления на стадии чтения исходного текста программы. 3. Сравнение с другими нотациями. (1.5 мин) В силу этого была разработана новая нотация, которая позволяет задавать вычисления непосредственно над грамматическими конструкциями. Уже существует большое количество нотаций, однако ни одна не подошла под требования, в которые входят: * Простота чтения; * Возможность задавать вычисления в грамматике; * Возможность распространения грамматики с вычислениями в качестве полноценного описания алгоритма над исходным кодом; * Возможность использования для автоматической генерации парсеров; * Интуитивность для человека, который работает с формальными языками профессионально. Наиболее близок к работе был модуль Marpa для языка Perl, но он позволяет только прикрепить к вычислению набор функций на языке Perl. Таким образом, грамматика не является самодостаточной, а чтение вычислений затруднено тем, что Perl не предполагает чтение программ на нём. 4. Цели и задачи. (0.2 мин) В связи с тем, что подходящей нотации не обнаружилось, была поставлена цель разработать собственную, а также оформить в её терминах статический анализатор для какого-либо языка совместно с его грамматикой. 4. Выбор языка для грамматики. (0.5 мин) При проектировании нотации первым делом нужно было понять, каким образом описывать грамматику языка. Анализ показал, что среди существующих средств выражения грамматик есть полностью подходящие для заявленных целей. В частности, в широко распространённом семействе нотаций BNF обнаружилась ABNF-нотация, которая предоставляет требуемый функционал. 5. Выбор языка для вычислений. (0.5 мин) Далее, для предствления вычислений тоже требуется язык. В результате анализа обнаружилось, что лучше всего подходят языки семейства Lisp. Из них наиболее подходящим кандидатом оказался Scheme версии r6rs, поскольку он явным образом рассчитан на простоту реализации и лёгкость встраивания. 6. Пример текста в разработанной нотации. (1.0 мин) В результате работы была получена нотация, пример текста в которой представлен на слайде. Здесь отражены два правила. Правило bin задаёт строку, равную "zero" или "one". В случае строки "zero" вычисление возвращает ноль, в случае строки "one" -- единицу. Правило top представлено комбинацией двух bin, причём если сумма вычислений в bin равна 0 -- то есть ни одно из них не вернуло 1 -- это считается ошибкой. В обычном BNF выразить недопустимость последовательности двух zero было бы сложнее: потребовалось бы перечислять все возможные варианты последовательностей строк. Показанная здесь гибкость даёт нашей нотации возможность валидировать на этапе парсинга весьма сложные структуры данных. Расскажем теперь более подробно, как устроена нотация. 7. Передача параметров. (0.5 мин) Для того, чтобы вычисления имели смысл, нужно, чтобы они были связаны с грамматикой, то есть чтобы термы языка в том или ином виде приходили как параметры. Параметры могуть быть именованные и неименованные. Если нет ни одного параметра, у которого в фигурных скобках указано имя, все элементы текущего правила приходят в вычисление в списке параметров. Если же есть хоть один элемент с именем, то становится доступна переменная с таким именем, а в список параметров попадают только именованные параметры. 8. Среда (0.5 мин) Далее, для многих вычислений требуется по ходу обработки грамматики держать некую среду. К примеру, список доступных в данной области программы переменных. Мы позволяем вычислениям изменять эту среду. Однако чтобы независимо от типа парсера вычисления происходили детерминированно, необходимо задать порядок изменения среды. Так, после имени в фигурных скобках через двоеточие нужно указать порядковый номер, с которым выполнится данное правило, если хоть одно правило может изменять среду. Ещё через одно двоеточие можно указать, от какого правила следует унаследовать среду. 9. Интеграция со Scheme (0.5 мин) С другой стороны, интерпретатор Scheme должен выполнять вычисления в некотором окружении, которое пробрасывает в вычисление среду, определяет, может ли вычисление её менять и так далее. На слайде представлено разработанное окружение. Функция bind-args все именованные аргументы вводит в контекст исполнения, затем тело вычисления получает свою копию среды. Если вычислению дозволено менять среду, то её копия синхронизируется обратно в основную среду. 10. Ограничение на значения в среде (0.5 мин) Важным ограничением в таких условиях является то, что в среде нельзя сохранять процедуры. Связано это с особенностью Scheme: дело в том, что этот язык поддерживает механизм продолжений и они функционально неотличимы от процедур. Если мы позволим передавать продолжения, то вычисления, которым не дозволено менять среду, могут прочитать из среды продолжения вычислений, которые могут, и тем самым поменять среду и внести недетерминизм в обработку текста. Пример кода, который мог бы внести недетерминизм, представлен на слайде. 11. Статический анализатор (0.2 мин) Для проверки пригодности нотации для сложных вычислений был спроектирован наивный алгоритм анализа границ значений, на примере которого были определены достоинства и недостатки разработанной нотации. 12. Преимущества и недостатки (0.3 мин) Обнаружилось, что нотация хорошо подходит для случаев, когда требуется лишь один проход по каждому синтаксическому элементу: к примеру, для трансляторов между схожими языками или для препроцессоров. Когда появляется рекурсия, требуется в вычислениях выстраивать синтаксическое дерево, что приводит к усложнению кода до такой степени, что требуется классический инструмент обработки исходного кода. 13. Заключение. По материалам данной работы в соавторстве с Лаздиным Артуром Вячеславовичем и Муромцевым Дмитрием Ильичом имеется публикация, индексируемая в Scopus.