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