Расширяемое программирование — это термин, используемый в информатике для описания стиля компьютерного программирования, который фокусируется на механизмах расширения языка программирования , компилятора и среды выполнения . Расширяемые языки программирования, поддерживающие этот стиль программирования, были активной областью работы в 1960-х годах, но в 1970-х это движение было маргинализировано. [1] Расширяемое программирование стало темой нового интереса в 21 веке. [2]
Первой статьей, обычно [1] [3] , связанной с движением за расширяемые языки программирования, является статья М. Дугласа Макилроя 1960 года о макросах для языков программирования более высокого уровня. [4] Другое раннее описание принципа расширяемости встречается в статье Брукера и Морриса 1960 года о компиляторе-компиляторе . [5] Пик движения был отмечен двумя академическими симпозиумами, в 1969 и 1971 годах. [6] [7] К 1975 году обзорная статья о движении Томаса А. Стэндиша [1] была по сути вскрытием. Язык программирования Форт был исключением, но он остался практически незамеченным.
Как обычно предполагалось, расширяемый язык программирования состоял из базового языка, обеспечивающего элементарные вычислительные возможности, и метаязыка, способного модифицировать базовый язык. Программа тогда состояла из модификаций метаязыка и кода на модифицированном базовом языке.
Наиболее известным методом расширения языка, использованным в движении, было макроопределение. Модификация грамматики также была тесно связана с этим движением, что в конечном итоге привело к развитию адаптивных грамматических формализмов . Языковое сообщество Лиспа оставалось отделенным от расширяемого языкового сообщества, по-видимому, потому, что, как заметил один исследователь ,
любой язык программирования, в котором программы и данные по существу взаимозаменяемы, можно рассматривать как расширяемый [sic] язык. ... это очень легко увидеть из того факта, что Лисп уже много лет используется как расширяемый язык. [8]
На конференции 1969 года Simula была представлена как расширяемый язык программирования.
Стэндиш описал три класса расширения языка, которые он назвал парафразой , орфразой и метафразой (иначе парафраз и метафраза были терминами перевода ).
Стэндиш объяснил неудачу движения за расширение сложностью программирования последовательных расширений. Обычный программист мог бы построить единственную оболочку макросов вокруг базового языка, но если вокруг него нужно было построить вторую оболочку макросов, программисту пришлось бы хорошо знать как базовый язык, так и первую оболочку; третья оболочка потребует знакомства с основой, а также с первой и второй оболочками; и так далее. (Обратите внимание, что целью движения за абстракцию , пришедшего на смену движению за расширяемость, является защита программиста от деталей более низкого уровня .)
Несмотря на более раннее представление Simula как расширяемой, к 1975 году исследование Стэндиша, похоже, на практике не включало новые технологии, основанные на абстракции (хотя он использовал очень общее определение расширяемости, которое технически могло их включать). История абстракции программирования 1978 года от изобретения компьютера до (тогда) наших дней не упоминала макросы и не давала никаких намеков на то, что движение за расширяемые языки когда-либо имело место. [9] Макросы были предварительно допущены в движение абстракций к концу 1980-х годов (возможно, из-за появления гигиенических макросов ), получив псевдоним синтаксические абстракции . [10]
В современном понимании система, поддерживающая расширяемое программирование, будет обеспечивать все описанные ниже возможности .
Это просто означает, что компилируемые исходные языки не должны быть закрытыми, фиксированными или статическими. Должна быть предусмотрена возможность добавления новых ключевых слов, концепций и структур к исходному языку(ам). Языки, которые позволяют добавлять конструкции с пользовательским синтаксисом, включают Coq , [11] Racket , Camlp4 , OpenC++, Seed7 , [12] Red , Rebol и Felix. Хотя допустимо, чтобы некоторые фундаментальные и внутренние особенности языка были неизменяемыми, система не должна полагаться исключительно на эти особенности языка. Должна быть возможность добавления новых.
В расширяемом программировании компилятор не является монолитной программой, преобразующей входные данные исходного кода в двоичный исполняемый вывод. Сам компилятор должен быть расширяемым до такой степени, что на самом деле он представляет собой набор плагинов, которые помогают переводить входные данные исходного языка во что угодно . Например, расширяемый компилятор будет поддерживать генерацию объектного кода, документацию по коду, переформатированный исходный код или любой другой желаемый результат. Архитектура компилятора должна позволять пользователям «попадать внутрь» процесса компиляции и предоставлять альтернативные задачи обработки на каждом разумном этапе процесса компиляции.
Только для задачи перевода исходного кода во что-то, что может быть выполнено на компьютере, расширяемый компилятор должен:
Во время выполнения расширяемые системы программирования должны позволять языкам расширять набор операций, которые они допускают. Например, если система использует интерпретатор байт-кода , она должна позволять определять новые значения байт-кода. Как и в случае с расширяемым синтаксисом, допустимо наличие некоторого (небольшого) набора фундаментальных или внутренних операций, которые являются неизменяемыми. Однако должна быть возможность перегружать или расширять эти внутренние операции, чтобы можно было поддерживать новое или дополнительное поведение.
Расширяемые системы программирования должны рассматривать программы как данные, подлежащие обработке. Эти программы должны быть полностью лишены какой-либо информации о форматировании. Визуальное отображение и редактирование программ пользователям должно представлять собой функцию трансляции, поддерживаемую расширяемым компилятором, переводящим данные программы в формы, более удобные для просмотра или редактирования. Естественно, это должен быть двусторонний перевод. Это важно, поскольку должна быть возможность легко обрабатывать расширяемые программы различными способами . Недопустимо, чтобы ввод исходного языка использовался только для редактирования, просмотра и перевода в машинный код. Произвольная обработка программ облегчается за счет отделения исходного ввода от спецификаций того, как его следует обрабатывать (форматировать, хранить, отображать, редактировать и т. д.).
Системы расширяемого программирования должны поддерживать отладку программ с использованием конструкций исходного исходного языка независимо от расширений или преобразований, которым подверглась программа, чтобы сделать ее исполняемой. В частности, нельзя предполагать, что единственный способ отображения данных времени выполнения — это структуры или массивы . Отладчик, или, точнее, «инспектор программ», должен позволять отображать данные времени выполнения в формах, подходящих для исходного языка. Например, если язык поддерживает структуру данных для бизнес-процесса или рабочего процесса , отладчик должен иметь возможность отображать эту структуру данных в виде диаграммы «рыбий скелет» или другой формы, предоставляемой плагином.