вторник, 19 октября 2010 г.

Введение

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

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

Помимо базовых операций сопоставления с образцом и подстановки, многочисленные диалекты языка предлагают различные расширения, например, Рефал 2 предлагает более выразительные средства сопоставления с образцом, Рефал 5 — условия и блоки, Рефал Плюс обобщает идеи Рефала 5 относительно блоков и условий, вводя такую категорию, как действия, Рефал 6 систематизирует идеи Рефала Плюс, нововведением Рефала 7 являются вложенные функции, Динамический Рефал расширяет идеи Рефала 5 и Рефала 2.

Модульный Рефал

Модульный Рефал — диалект языка функционального программирования Рефал, разрабатываемый мной (А. В. Коноваловым aka Маздайщиком) в течение последних нескольких лет. Краткую историю разработки можно почитать здесь.

Можно отметить следующие ключевые особенности диалекта Модульного Рефала.

  • Синтаксис похож на синтаксис Рефала 5.
  • Модули в стиле Модулы-2 или Оберона-2 (эта особенность вынесена в название языка). Модуль предоставляет те или иные ресурсы в виде экспортируемых функций (в перспективе не только функций), чтобы использовать ресурсы модуля, его надо импортировать.
  • Модули могут группироваться в пакеты (практически калька с языка Java).
  • Термы абстрактных типов данных — особые скобочные термы, доступ к содержимому которых возможен только в том модуле, в котором они объявлены.
  • Ограниченная поддержка функций высших порядков — указатели возможны пока только на глобальные функции. Библиотечный модуль MLambda в некоторой степени нивелирует этот недостаток.
  • Управляющие конструкции (пока) ограничены только подмножеством Базисного Рефала, то есть предложения имеют вид образец = результат;.
  • Наличие глобальных переменных, так называемых статических ящиков (название взято из диалекта Рефал 2, там есть ещё и динамические ящики). Отсутствие копилки (её можно эмулировать применением статических ящиков, поэтому она не нужна).
  • Отсутствие встроенных в язык функций. Функции, которые можно вызвать в коде, должны быть или объявлены в текущем модуле, или импортироваться из других модулей. Соответственно, функции, невыразимые на Рефале (ввод-вывод, арифметика), реализованы в библиотечных модулях, которые не имеют исходных текстов и реализуются на целевом языке.
Особенности реализации Модульного Рефала.

  • Реализация представляет собой самоприменимый компилятор (т. е. компилятор написан на Модульном Рефале).
  • Архитектура поддерживает несколько front-end’ов и back-end’ов. На данный момент реализован только front-end Модульного Рефала, 5 back-end’ов, из которых два служебных (генерация интерфейсов модулей и построение таблицы перекрёстных связей),
    три других генерируют код на целевом языке:
    • генерация непосредственно в C++,
    • генерация в Простой Рефал (это тоже мой диалект, см. ниже),
    • генерация в Рефал 5 (исторически самый первый, является устаревшим, но жалко выкинуть).
  • Относительно удобный интерфейс с языком C++.
  • Отсутствие оптимизаций. :-(
  • Работает только на платформе Windows.

Простой Рефал

Когда я начинал работать над Модульным Рефалом, у меня не было чёткого представления о том, как преобразовывать код на Рефале в императивный код (после рассмотрения синтаксиса и семантики будет понятно, почему эта задача нетривиальна). Для выяснения этого вопроса было решено разработать транслятор упрощённого подмножества Рефала в императивный код, в качестве которого был выбран C++.

Со свойственным мне размахом, я расширил задачу и стал писать компилятор Рефала в C++, максимально упрощая второстепенные сложности (например, задачи оптимизации полученного кода, модульность, произвольный порядок функций в файле исходного текста — требуется, чтобы перед использованием функция была объявлена или определена). В результате получился диалект Простого Рефала.

Поскольку в данном случае первичными были требования к реализации (максимально простая, компиляция в C++), то особенности языка и реализации я перечислю в общем списке:

  • Наиболее простая компиляция в C++, и как следствие:
    • каждый файл исходного текста Простого Рефала при компиляции превращается в файл исходного текста на C++,
    • как следствие, модульность в стиле языка C: функции, объявленные экспортируемыми с ключевым словом $ENTRY (без модификатора static), объявляются в других единицах трансляции как $EXTERN (соответственно, extern),
    • однопроходная компиляция: отдельные элементы (предобъявления функций, определения функций, даже отдельные предложения) компилируются в соответствующие конструкции целевого языка (соответственно, объявления и определения функций),
    • если вызываемая функция находится после вызывающей, необходимо её предобъявление,
    • подмножество только Базисного Рефала,
    • стандарт C++98 требует, чтобы функция с модификатором static объявлялась тоже с модификатором static, поэтому чтобы различать предъобъявления локальных функций и функций, объявленных как $ENTRY, используются, соответственно, ключевые слова $FORWARD и $EXTERN.
  • Типы данных: структурные скобки, символы (characters), целые числа ограниченного диапазона (0…2³² − 1) и имена функций (указатели на функции).
  • Поскольку идентификаторов в языке нет, а потребность в них имеется, существуют синтаксические средства для определения «пустых» функций: ключевые слова $ENUM и $EENUM (entry enum или export enum). Такие функции в дальнейшем используются вместо идентификаторов.
  • Отсутствие оптимизаций.
  • Поскольку реализация использует только стандартную библиотеку C++, компилятор может работать как на платформе Windows, так и на платформах POSIX.
Позже я решил использовать Простой Рефал и для генерации исполняемого кода Модульным Рефалом, в результате чего появился соответствующий back-end. Позже я подключил кодогенератор от Простого Рефала к Модульному, в результате чего получился back-end, генерирующий код на C++. Разумеется, диалект пришлось пополнить следующими возможностями:

  • статические ящики, объявляются ключевыми словами $SWAP и $ESWAP (второе, разумеется, entry swap),
  • идентификаторы, объявляются ключевым словом $LABEL, при их использовании имя следует предварять символом # (например, #True).
В дальнейшем, компилятор, в следствие своей простоты, служил тестовым полигоном для различных исследований, в частности

  • оптимизация построения результатных выражений путём замены прямой кодогенерации на интерпретацию. Выполнялось совместно с Сухаревым Вадимом aka XpycT;
  • использование безымянных вложенных функций в стиле Рефала 7,
  • написание компилятора Простого Рефала в C#. Выполнялось совместно с Быкадоровой Натальей,
  • оптимизация построения результатных выражений путём минимизации числа преобразований поля зрения. Выполнялось совместно с Дорофеевым Михаилом aka Patriot.
Об этом руководстве

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

Хочу поблагодарить Сухарева Вадима aka XpycT, Быкадорову Наталью и Дорофеева Михаила aka Patriot за помощь в проведении исследований, и в особенности преподавателя кафедры ИУ9 МГТУ имени Н. Э. Баумана Скоробогатова Сергея Юрьевича, разработчика Рефала 7, за ценные советы и ценную критику.