Понять, что такое транспайлеры и какова их роль в программировании, поможет следующий пример. Представим, что вы разрабатываете софт для TV-приставок и вам нужно добавить калькулятор на все приставки определенной фирмы. Есть одно но — некоторые приставки не обновлялись уже 7 лет и могут работать только со старой версией языка, на котором написан калькулятор. Тут появляется выбор:
Роль такого переводчика выполняют транспайлеры: они преобразуют код, на котором мы пишем, в другой, который может применяться и работать у конечного пользователя на любых устройствах с любыми версиям языка разработки.
Транспиляция — преобразование программы, написанной на одном языке программирования в качестве исходных данных, в эквивалентный код другой версии этого языка или в другой язык программирования того же уровня абстракции.
Рассмотрим практический пример, в котором пригодится транспайлер. За основу возьмем кейс с обновлением софта в TV-приставках, но дополним его деталями.
Калькулятор запускается на встроенном браузере TV-приставки и написан на JavaScript последней версии. Также учтем, что приставки выпускались каждый год с ограниченным сроком поддержки и сейчас мы имеем множество устройств с разными версиями браузера (а, следовательно, и отличающиеся версии поддерживаемого JS-кода). Кроме того, браузер пользователя давно не обновлялся и код на JS, который может там работать, сильно отличается от текущих версий языка.
В такой ситуации чаще всего может пригодиться транспиляция кода. Транспайлеры помогут преобразовать код, написанный на последней версии JavaScript, в тот, который будет работать у всех пользователей независимо от времени, прошедшего с последнего обновления.
Тринспайлеры широко используются во многих областях программирования. В целом можно выделить две основных области применения:
Транспиляция одной версии языка в другую. Поскольку языки программирования постоянно обновляются, возникают ситуации, когда разработчик уже пишет на новой версии языка, а среда, где используется код, поддерживает и работает только с предыдущими версиями. Например, для переходов между версиями стандарта ECMAScript используется Babel. Транспайлеры позволяют преобразовывать код и в другую сторону: например, помогают перевести проект на более новую версию языка. Делать это вручную долго и больно. Например, если проект написан на Python 2.x, перевести его на Python 3 можно с помощью трансплайнера 2to3
Транспиляция между разными языками. Например преобразование TypeScript в JS. Подробнее об этом расскажем ниже
В качестве примера рассмотрим работу транспайлера над возведением в квадрат кода на JS:
a ** b
Вот как выглядит то же преобразование, проведенное с помощью объекта Math — он и использовался для операций по возведению в квадрат в старых версиях языка:
Math.pow(a, b)
В данном случае у нас есть начальное и желаемое конечное состояния. Мы уже знаем, что для транспайлера не составит труда провести такое преобразование. Однако для нас механизм его работы пока выглядит как черный ящик. Пора в деталях разобраться, как происходит транспиляция.
На первом этапе транспайлер создает на основе кода абстрактное представление. В общем случае такое преобразование происходить в два этапа — сначала программа проводит лексический, а затем синтаксический анализ.
После преобразования в абстрактную модель происходит трансформация, которая производит все необходимые для дальнейшей работы изменения в AST.
На этом этапе дерево из предыдущего шага изменяется: трансформация меняет текущий код на код на предыдущей версии языка. В данном случае оператор умножения заменяется на вызов выражения, при этом его операнды становятся аргументами выражения (Math.pow):
Когда все изменения внесены, транспайлер берёт трансформированное AST кода и на его основе генерирует новый код.
В итоге код из начального состояния проходит ряд преобразований (анализ -> трансформация -> генерация) до конечного состояния — этот процесс и называется транспиляцией.
После всех преобразований мы получили конечный код, который работает на нужной нам версии языка. Но представим, что после трансилиляции в нашей программе появились ошибки. Как понять, где именно находится ошибка, если она ссылается какое-то место в трансипилированном коде (который может значительно отличаться от того, что мы писали изначально)?
Использование транспайлеров может затруднить отладку, когда нам нужно найти точное место поломки в исходном коде по транспилированному коду. В таких случаях используются различные утилиты, позволяющие сопоставить результат транспиляции с оригинальным кодом. Например, в JS есть утилита SourceMap, которая в процессе создания конечного кода создает файл-маппер — с его помощью можно связать исходный и конечный код.
Для фронтенд-разработки основной инструмент транспиляции — Babel. Он позволяет не только выполнять транспиляцию между версиями, которую мы рассмотрели выше, но и:
Среди транспиляторов, которые трансформируют код на бэкэнд языках программирования в код на других языках, популярностью пользуются:
В мире, где есть огромное количество языков программирования, а сами языки имеют множество версий, транспайлеры выполняют крайне важную работу — делают разработку удобнее. Программисты могут писать код на языке или версии языка, которая им удобна или имеет нужные функции, а транспайлеры выполнят работу по доставке этого кода до среды исполнения в нужном виде.
На данный момент реализовано множество различных транспайлеров (Babel, Vala, Typescript transpiler, Bridge и другие), каждый из них решает свою задачу по-своему, но общий принцип работы транспайлеров похож у всех.
Исходный код преобразуется в абстрактное синтаксическое дерево, далее AST трансформируется в структуру, которая соотносится с конечным кодом, и из этой структуры генерируется необходимый нам код.