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