Code Basics logo

Что такое транспиляция?

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

  • Разрабатывать программу для двух типов приставок: на старой версии языка для тех, которые давно не обновлялись, и на новой — для современных.

transpilers, cover

  • Использовать «переводчик» с версии языка, которую используете вы, на версии, работающие на всех устройствах

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

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

Когда нужна транспиляция?

Рассмотрим практический пример, в котором пригодится транспайлер. За основу возьмем кейс с обновлением софта в TV-приставках, но дополним его деталями.

Калькулятор запускается на встроенном браузере TV-приставки и написан на JavaScript последней версии. Также учтем, что приставки выпускались каждый год с ограниченным сроком поддержки и сейчас мы имеем множество устройств с разными версиями браузера (а, следовательно, и отличающиеся версии поддерживаемого JS-кода). Кроме того, браузер пользователя давно не обновлялся и код на JS, который может там работать, сильно отличается от текущих версий языка.

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

Как используют транспайлеры?

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

  • Транспиляция одной версии языка в другую. Поскольку языки программирования постоянно обновляются, возникают ситуации, когда разработчик уже пишет на новой версии языка, а среда, где используется код, поддерживает и работает только с предыдущими версиями. Например, для переходов между версиями стандарта ECMAScript используется Babel. Транспайлеры позволяют преобразовывать код и в другую сторону: например, помогают перевести проект на более новую версию языка. Делать это вручную долго и больно. Например, если проект написан на Python 2.x, перевести его на Python 3 можно с помощью трансплайнера 2to3

  • Транспиляция между разными языками. Например преобразование TypeScript в JS. Подробнее об этом расскажем ниже

Как работают транспайлеры?

В качестве примера рассмотрим работу транспайлера над возведением в квадрат кода на JS:

a ** b

Вот как выглядит то же преобразование, проведенное с помощью объекта Math — он и использовался для операций по возведению в квадрат в старых версиях языка:

Math.pow(a, b)

В данном случае у нас есть начальное и желаемое конечное состояния. Мы уже знаем, что для транспайлера не составит труда провести такое преобразование. Однако для нас механизм его работы пока выглядит как черный ящик. Пора в деталях разобраться, как происходит транспиляция.

Преобразование исходного кода в абстрактною форму

На первом этапе транспайлер создает на основе кода абстрактное представление. В общем случае такое преобразование происходить в два этапа — сначала программа проводит лексический, а затем синтаксический анализ.

  • Лексический анализ берёт «сырой» код и разбивает его на изолированные части, которые называются токенами. Это могут быть цифры, пунктуация, метки, операторы: всё, что угодно. В нашем случае токенами будут бинарный оператор (применяется к двум операндам, например 2 + 2) и его операнды:

transpilers, tokens

  • Синтаксический анализ строит объект, который описывает все токены и их связи между собой. Он называется AST (abstract syntax tree или абстрактное синтаксическое дерево). Оператор возведения в квадрат является бинарным выражением внутри программы (в нашем случае — единственным), а операнды станут идентификаторами в этом выражении:

transpilers, AST

Трансформация AST

После преобразования в абстрактную модель происходит трансформация, которая производит все необходимые для дальнейшей работы изменения в AST.

На этом этапе дерево из предыдущего шага изменяется: трансформация меняет текущий код на код на предыдущей версии языка. В данном случае оператор умножения заменяется на вызов выражения, при этом его операнды становятся аргументами выражения (Math.pow):

transpilers, transformedAST

Генерация конечного кода

Когда все изменения внесены, транспайлер берёт трансформированное AST кода и на его основе генерирует новый код.

transpilers, generate

В итоге код из начального состояния проходит ряд преобразований (анализ -> трансформация -> генерация) до конечного состояния — этот процесс и называется транспиляцией.

Обратная связь

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

Использование транспайлеров может затруднить отладку, когда нам нужно найти точное место поломки в исходном коде по транспилированному коду. В таких случаях используются различные утилиты, позволяющие сопоставить результат транспиляции с оригинальным кодом. Например, в JS есть утилита SourceMap, которая в процессе создания конечного кода создает файл-маппер — с его помощью можно связать исходный и конечный код.

Инструменты (реализации) транспиляции кода

Для фронтенд-разработки основной инструмент транспиляции — Babel. Он позволяет не только выполнять транспиляцию между версиями, которую мы рассмотрели выше, но и:

  • преобразовывать JSX-код в JavaScript при разработке на React
  • трансформировать LESS/SCSS в CSS при использовании CSS препроцессоров
  • транспилировать TypeScript в JS, когда нужно использовать типизацию

Среди транспиляторов, которые трансформируют код на бэкэнд языках программирования в код на других языках, популярностью пользуются:

  • Haxe предназначен для транспилирования во Flash, JavaScript и Neko. Со временем Haxe разросся до набора инструментов, поддерживающих транспиляцию на разные языки и платформы, включая JavaScript, C++, C#, Java, JVM, Python, Lua, PHP и Flash.
  • Lombok — решение для тех, кому хочется расширить возможности Java. Это плагин, добавляющий в Java новые «ключевые слова» и превращающий аннотации в Java-код. Он сокращает время на разработку и обеспечивает дополнительную функциональность.
  • Bridge.NET позволяет использовать в JavaScript производительность C#, а также мощные VisualStudio IDE и стандартные инструменты .NET. Grumpy обеспечивает трансляцию кода на языке Python в представление на языке Go и позволяет бесшовно запускать Python-программы в runtime-окружении Go.

Итого

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

На данный момент реализовано множество различных транспайлеров (Babel, Vala, Typescript transpiler, Bridge и другие), каждый из них решает свою задачу по-своему, но общий принцип работы транспайлеров похож у всех.

transpilers, scheme

Исходный код преобразуется в абстрактное синтаксическое дерево, далее AST трансформируется в структуру, которая соотносится с конечным кодом, и из этой структуры генерируется необходимый нам код.

Дополнительные материалы

Исходный код (github)
Казанцев Игорь
comments powered by Disqus