Транслятор – это… Виды трансляторов. Преобразование и трансляция программы

ТрансляторДля перевода с одного языка на другой программам, как и людям, требуется переводчик или, говоря по-научному, транслятор.

Транслятор: основные понятия

Такая программа как транслятор представляет собой лингвистическое представление вычислений I ->P ->P (i). Интерпретатор представляет собой программу, на вход которой подается программа P с некоторыми входными данными X.Выполняет он P на X: I(P, x)=P(x).Существует единственный транслятор, который способен выполнять все возможные программы (которые можно представить в формальной системе). Это является очень значительным и глубоким открытием Тьюринга. Процессор представляет собой интерпретатор программ на машинном языке. Писать интерпретаторы для языков высокого уровня, как правило, слишком дорого, поэтому их транслируют в ту форму, которую легче интерпретировать. Некоторые виды трансляторов обладают очень странными именами. Программа транслирует программы на ассемблере в машинный язык. Компилятор позволяет транслировать с языка высокого уровня на язык более низкого уровня. Транслятор представляет собой программу, которая в качестве входных данных принимает программу на некотором языке S и после обработки выдает программу на языке T.Таким образом, они обе имеют ту же семантику: P->X->Q. Таким образом, для любого xP(x)=Q(x). Если транслировать всю программу в нечто интерпретируемое, то это называется компиляцией перед исполнением или компиляцией AOT. Компиляторы AOT могут использоваться последовательно. Последний из них очень часто является ассемблером. Так, рассмотрим пример: Исходный код ->Компилятор (транслятор) -> Ассемблерный код -> Ассемблер (транслятор) -> Машинный код -> ЦПУ (интерпретатор). Динамическая или оперативная компиляция осуществляется в том случае, если часть программы транслируется, когда исполняются другие скомпилированные ранее части. Трансляторы JIT запоминают то, что они уже выполнили ранее, чтобы снова и снова не повторять исходный код. Они даже способны выполнять адаптивную компиляцию и перекомпиляцию, которая основана на поведении среды выполнения программы. Многие языки дают возможность выполнять код во время трансляции, а также компилировать новый код во время выполнения программы.

Трансляция: этапы

Процесс трансляции состоит из этапов синтеза и анализа. Схематично этот процесс выглядит примерно следующим образом: Исходный код -> Анализатор -> Концептуальное представление -> Синтезатор (генератор) -> Целевой код. Обусловлено это следующими причинами:

— любой другой способ просто не подходит;

— перевод по словам просто не работает.

Можно использовать следующее инженерное решение: если необходимо написать трансляторы для M исходных языков и N целевых, потребуется написать только M+N простых программ (полукомпиляторов), а не MxN полных (комплексных) трансляторов. На практике, тем не менее, концептуальное представление довольно редко бывает выразительным и мощным, чтобы охватить все существующие целевые и исходные языки. Хотя некоторые пользователи смогли приблизиться к этому. Реальные компиляторы проходят через множество различных этапов. При создании собственного компилятора не нужно будет заново проводить всю тяжелую работу, которую программисты уже проделали при создании генераторов и представлений. Свой язык можно транслировать непосредственно в JavaScript или C и использовать для этой цели существующие компиляторы языка C и JavaScript движки для того, чтобы сделать все остальное. Можно также использовать существующие промежуточные представления и виртуальные машины.

Запись транслятора

Транслятор может представлять собой техническое средство или программу, в которой используются три языка: исходный, целевой, базисный. Записать их можно в форме T, расположив слева исходный, справа целевой  и ниже базисный. Всего существует три вида компиляторов.

  1. Транслятор – это самокомпилятор, если исходный язык у него соответствует базисному.
  2. Саморезидентным называется компилятор, у которого целевой язык равняется базисному.
  3. Если целевой и базисный языки различные, то транслятор – это кросс-компилятор.

Почему важно различать эти виды компиляторов? Даже если вы никогда не создадите по-настоящему качественный компилятор, неплохо будет узнать о технологии его создания, поскольку все используемые для этой цели концепции применяются повсеместно в языках запросов к базам данных, при форматировании текстов, в расширенных компьютерных архитектурах, графических интерфейсах, обобщенных задачах оптимизации, машинных переводах, контроллерах и в виртуальных машинах. Также, если необходимо написать препроцессоры, загрузчики, сборщики, отладчики или профилировщики, необходимо пройти через все те же этапы, что и при написании компилятора. Можно также узнать о том, каким образом лучше писать программы, поскольку разработка транслятора для языка программирования означает лучшее понимание всех его неясностей и тонкостей.  Благодаря изучению общих принципов трансляции вы можете стать хорошим дизайнером языка. Но действительно ли это важно? Насколько крут язык, если он не может быть эффективно реализован?

Масштабная технология

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

Проектирование компилятора. Возможные проблемы, возникающие при создании реального транслятора

Какие проблемы могут возникать с исходным языком? Легко ли его скомпилировать? Имеется ли для этого препроцессор? Каким образом обрабатываются типы? Какая группировка проходов компилятора используется – одно- или многоходовая?  Также особого внимания заслуживает желаемая степень оптимизации. Быстрая и нечистая трансляция программы практически без оптимизации может быть нормальной. Чрезмерная оптимизация может тормозить компилятор, однако, во время выполнения лучший код может того стоить.

Степень обнаружения ошибок. Нужно ли, чтобы транслятор остановился уже на первой ошибке? Когда он должен остановиться? Стоит ли доверять компилятору процедуру исправления ошибок?

Необходимый набор инструментов

Если в вашем случае исходный язык является не слишком маленьким, то наличие генератора анализаторов и сканера являются обязательным условием. Также существуют и специальные генераторы кода, но они не получили слишком большого распространения.

Что касается вида целевого кода для генерации, тут необходимо выбирать из чистого, дополненного или виртуального машинного кода. Можно также написать входную часть, которая создает популярные промежуточные представления, такие как LLVM, JVM, RTL. Можно также сделать трансляцию из исходного в исходный код на Java Script или C. Если говорить о формате целевого кода, тут здесь можно выбрать переносимый машинный код, машинный код образа памяти, язык ассемблера.

Перенацеливание

При использовании большого количества генераторов неплохо было бы иметь общую входную часть. Также по этой причине для многих входных частей лучше иметь один генератора.

Компоненты компилятора

Перечислим главные функциональные компоненты транслятора, который генерирует машинный код, если выходной программой является программа, написанная на языке C или виртуальная машина:

— входная программа поступает в лексический анализатор, или по-другому сканер, который преобразует ее в поток токенов;

— синтаксический анализатор (парсер) строит из них абстрактное синтаксическое дерево;

— семантический анализатор раскладывает семантическую информацию и проверяет на предмет наличия ошибок узлы дерева;

— в результате строится семантический граф. Под этим термином понимают абстрактное синтаксическое дерево с установленными ссылками и дополнительными свойствами;

— генератор промежуточного кода строит граф потока (кортежи группируются в основные блоки);

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

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

—  для распределения памяти между виртуальными регистрами и выполнения планирования команд используется машинозависимый оптимизатор-компоновщик. Он также осуществляет преобразование программы, написанной на ассемблере, в настоящий ассемблер с применением конвейерной обработки.

— используются подсистемы обнаружения ошибок и менеджер таблиц символов;

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

Те ошибки, которые могут встретиться при сканировании, называются лексическими. Они включают в себя следующие:

— отсутствующие в алфавите символы;

— превышение количества знаков в строке или слове;

— не закрытый строковый литерал или знак;

— конец файла в комментарии.

Синтаксический анализ или парсинг применяется для преобразования последовательности токенов в абстрактное синтаксическое дерево. При этом каждый узел дерева сохраняется как объект с именованными полями. Многие из них сами являются узлами дерева. Циклы на этом этапе отсутствуют. При создании парсера нужно в первую очередь обращать внимание на уровень сложности грамматики (LRили LL) и выяснить, имеются ли какие-то правила снятия неоднозначности. Действительно некоторые языки требуют проведения семантического анализа. Ошибки, которые встречаются на данном этапе, называются синтаксическими.

Семантический анализ

При проведении семантического анализа, необходимо, прежде всего, проверить правила допустимости и связать в одно целое части синтаксического дерева для формирования семантического графа путем вставки операции для неявного приведения типов, разрешения ссылок имен и т.п. Понятно, что разные языки программирования имеют различный набор правил допустимости. Если осуществляется компиляция Java-подобных языков, трансляторы могут обнаружить следующие ошибки:

— множественные объявления переменной в пределах области ее действия;

— ссылки не переменную до объявления;

— нарушение правил доступности;

— наличие ссылок на необъявленное имя;

— чересчур большое или, наоборот, недостаточное число аргументов при вызове метода;

— несоответствие типов.

Генерация

Путем генерации промежуточного кода производится граф потока, который составлен из кортежей, сгруппированных в базовые блоки. После генерации кода получается реальный машинный код. На первом этапе в традиционных компиляторах для машин RISC на первом этапе создается ассемблер с бесконечным количеством виртуальных регистров. Вероятно, этого не произойдет для машин CISC.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *