В информатике препроцессор (или прекомпилятор ) [1] — это программа , которая обрабатывает входные данные для получения выходных данных , которые используются в качестве входных данных в другой программе. Выходные данные называются предварительно обработанной формой входных данных, которая часто используется некоторыми последующими программами, такими как компиляторы . Объем и вид выполняемой обработки зависят от характера препроцессора; некоторые препроцессоры способны выполнять лишь относительно простые текстовые замены и расширения макросов , в то время как другие обладают возможностями полноценных языков программирования .
Типичным примером компьютерного программирования является обработка исходного кода перед следующим этапом компиляции. В некоторых компьютерных языках (например, C и PL/I ) существует этап перевода , известный как предварительная обработка . Он также может включать обработку макросов, включение файлов и языковые расширения.
Лексические препроцессоры являются препроцессорами самого низкого уровня, поскольку они требуют только лексического анализа , то есть они работают с исходным текстом перед любым синтаксическим анализом , выполняя простую замену токенизированных последовательностей символов на другие токенизированные последовательности символов в соответствии с заданными пользователем. правила. Обычно они выполняют макроподстановку , текстовое включение других файлов и условную компиляцию или включение.
Наиболее распространенным примером этого является препроцессор C , который принимает строки, начинающиеся с '#', в качестве директив . Препроцессор C не ожидает, что его входные данные будут использовать синтаксис языка C. Некоторые языки используют другой подход и используют встроенные функции языка для достижения аналогичных целей. Например:
if-then-else
устранение мертвого кода для достижения условной компиляции .Другие лексические препроцессоры включают m4 общего назначения , наиболее часто используемый в кроссплатформенных системах сборки, таких как autoconf , и GEMA , макропроцессор с открытым исходным кодом, который работает с шаблонами контекста.
Синтаксические препроцессоры были представлены в семействе языков Lisp . Их роль заключается в преобразовании синтаксических деревьев в соответствии с рядом определяемых пользователем правил. Для некоторых языков программирования правила написаны на том же языке, что и программа (отражение во время компиляции). Это относится к Lisp и OCaml . Некоторые другие языки полагаются на полностью внешний язык для определения преобразований, например препроцессор XSLT для XML или его статически типизированный аналог CDuce .
Синтаксические препроцессоры обычно используются для настройки синтаксиса языка, расширения языка путем добавления новых примитивов или встраивания предметно-ориентированного языка программирования (DSL) в язык общего назначения.
Хорошим примером настройки синтаксиса является существование двух разных синтаксисов в языке программирования Objective Caml . [2] Программы могут быть написаны как с использованием «нормального синтаксиса», так и с «пересмотренным синтаксисом», и по запросу могут быть красиво напечатаны с использованием любого из синтаксисов.
Аналогичным образом, ряд программ, написанных на OCaml , настраивают синтаксис языка путем добавления новых операторов.
Лучшие примеры расширения языка с помощью макросов можно найти в семействе языков Лисп . Хотя языки сами по себе представляют собой простые динамически типизированные функциональные ядра, стандартные дистрибутивы Scheme или Common Lisp допускают императивное или объектно-ориентированное программирование, а также статическую типизацию. Почти все эти функции реализуются посредством синтаксической предварительной обработки, хотя следует отметить, что фаза компиляции «расширения макроса» обрабатывается компилятором в Lisp. Это по-прежнему можно считать формой предварительной обработки, поскольку она происходит перед другими этапами компиляции.
Одной из необычных особенностей семейства языков Lisp является возможность использования макросов для создания внутреннего DSL. Как правило, в большом проекте, основанном на Lisp , модуль может быть написан на множестве таких мини-языков : один, возможно, использует диалект Lisp , основанный на SQL , другой написан на диалекте, специализированном для графических интерфейсов или красивой печати, и т. д. Common Lisp Стандартная библиотека содержит пример этого уровня синтаксической абстракции в виде макроса LOOP, который реализует мини-язык типа Алгола для описания сложных итераций, в то же время позволяя использовать стандартные операторы Лиспа.
Препроцессор /язык MetaOCaml предоставляет аналогичные функции для внешних DSL. Этот препроцессор берет описание семантики языка (то есть интерпретатора) и, комбинируя интерпретацию во время компиляции и генерацию кода, превращает это определение в компилятор языка программирования OCaml , а из этого языка либо в байт-код, либо в родной код.
Большинство препроцессоров предназначены для конкретной задачи обработки данных (например, компиляции языка C). Препроцессор может рекламироваться как универсальный , что означает, что он не предназначен для конкретного использования или языка программирования и предназначен для использования для широкого спектра задач обработки текста.
M4 , вероятно, является наиболее известным примером такого препроцессора общего назначения, хотя препроцессор C иногда используется в роли, не специфичной для C. Примеры: