В прошлом году грубый прототип Sempiler был доступен в виде плагина Visual Studio Code.

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

Язык реализации движка

Прототип был написан на TypeScript, но теперь исходный код — на C#.

Это было связано с желанием, чтобы компилятор завершил работу как можно быстрее (даже в большей степени с холодного старта), чтобы обеспечить быструю обратную связь для разработчика. Особенно с учетом того, что в обычной настройке инструмента диагностика достигается путем передачи вывода Sempiled другому компилятору в бэкэнде (например, javac), что имеет свою собственную неотъемлемую стоимость.

Конвейер движка

Прототип был отделен от необходимости использовать определенный исходный синтаксис и целевой домен, но на этом все.

Теперь Sempiler разделен на 4 фазы (анализ, преобразование, эмиссия, потребление), которые полностью настраиваются, что важно для цели демократизации компиляции (т.е. предоставления разработчику полного контроля над преобразованиями, внесенными в исходный код).

Анализ

Прототип анализировал TypeScript или JavaScript, но делал это с помощью обертки tsc.

По сути, tsc сначала создаст свое дерево, которое затем будет проходить Sempiler, преобразовывая узлы в эквивалентные узлы абстрактного семантического дерева в стране Sempiler.

Это преобразование явно снижает производительность, особенно на сложных деревьях исходного кода.

Кроме того, tsc проверяет это исходное дерево на соответствие правилам домена для Интернета или Node.js. Одной из фундаментальных концепций Sempiler является разделение синтаксиса и семантики (разновидность правил проверки, которые вы применяете к фрагменту кода).

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

(примечание: я упомянул об этой проблеме в Части вопросов и ответов доклада Sempiler FullStackCon 2018)

Новый движок компилятора имеет специальный модуль анализатора TypeScript, который можно подключить к внешнему интерфейсу.

Его задача — просто разобрать синтаксические конструкции и создать абстрактное семантическое дерево непосредственно из исходного текста.

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

Следовательно, это гораздо более эффективно, и полученное абстрактное семантическое дерево может быть впоследствии проверено на соответствие семантике любого домена (например, iOS/Swift).

Проверка типов и семантическая проверка

В прототипе реализована собственная проверка типов.

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

В настоящее время новый движок не выполняет проверку типов по нескольким причинам:

  • Код, который вы сэмпилируете, будет ссылаться на API и символы, которые существуют в целевом контексте (например, сетевой API «выборки»), но семпилер может не видеть или не знать эти символы.
  • Сложность написания программы проверки типов — это серьезное мероприятие.
  • Накладные расходы во время выполнения, связанные с проверкой типов, нетривиальны.
  • Этап потребления предназначен для связывания с существующими, зрелыми инструментами, такими как закаленные в боях компиляторы, которые сами могут сообщать об ошибках типа (например, javac для проверки Java/Android).

При сборе диагностических данных от потребителя Sempiler переводит все ошибки обратно в строку, вызвавшую проблему в исходном тексте.

Поэтому, если вы пишете TypeScript, но в конечном итоге он становится Java, а javac обнаруживает ошибку, эта ошибка будет отображаться обратно в строку вашего TypeScript, вызвавшую проблему.

Таким образом, конечным результатом является то, что ваш код по-прежнему будет статически проверяться на тип и проверяться на соответствие правилам для определенного домена, просто Sempiler не должен будет выполнять эту задачу. (Фу!)

Отключение IDE Intellisense

Прототип пытался работать с существующей системой Visual Studio Code Intellisense, предоставляя определения, которые хорошо сочетались бы с Intellisense.

Однако то, как это было реализовано, включало ряд хаков и обходных путей только для того, чтобы обмануть автозаполнение, чтобы предложить правильные варианты (например, автозавершения в стиле Android при написании JavaScript).

Один из вышеупомянутых вопиющих взломов также был удален.

Поддельные слова

В прототипе Sempiler использовался болезненный обходной путь под названием фальшивые ключевые слова.

По сути, это было злоупотреблением аннотациями типов, когда вы могли использовать «фальшивые ключевые слова» в типах объединения.

Эти фальшивые ключевые слова были определены в соответствии с undefined в системе типов, что означало, что Intellisense их игнорировала, и они не конфликтовали с реальным выражаемым типом:

Необходимость в таких механизмах возникла из-за того, что прототип зависел от tsc и стандартного интерфейса Intellisense для TypeScript/JavaScript.

Новый движок не имеет этой зависимости, поэтому потребность в поддельных ключевых словах (к счастью) отпадает, потому что ваш код теперь проверяется внутренними потребителями, а не внешним интерфейсом.

Заглушки API

Разработчикам также приходилось писать API-заглушки (API-заполнители с теми же сигнатурами, что и их настоящие аналоги в целевом домене) просто для проверки типов.

Этот огромный источник трений сродни написанию файлов объявлений в современном мире TypeScript — то, что, как я видел, отталкивает людей от принятия.

Вы хотите получить инструмент программирования и приступить к работе как можно скорее — вам не нужно возиться с настройкой и настройкой.

В новом семпилере отсутствуют API-интерфейсы-заглушки или файлы объявлений любого типа.

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

  • Может быть, вы определили это явно в своем коде?
  • Может быть, он вводится в процессе компиляции?
  • Может быть, он уже существует в целевом домене?

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

Языковая служба

Прототип был невероятно скрыт, потому что он запускал Sempiler внутри того же потока, который обновлял IDE или редактор кода. (рррр!)

Вы можете себе представить, насколько это болезненно, когда каждый введенный вами символ вызывает заикание.

(Мне пришлось задним числом взломать грубый механизм кэширования только для того, чтобы сократить время компиляции с 20 до менее 5 секунд!)

Целесообразным (читай: правильным) способом является использование языкового сервера, а именно Sempiler запускается в отдельном фоновом процессе, который остается в живых, предоставляя результаты асинхронно основному потоку.

Токен отмены

В новом движке сеанс компиляции можно завершить с помощью механизма .NET CancellationToken.

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

Процесс компиляции можно немедленно прервать и начать заново с обновленными данными (последним набранным исходным текстом).

Цели

Наконец, цели проекта немного изменились.

Это немного более расплывчато, но исходный прототип был непоколебимо самоуверенным:

Как разработчик, вы всегда должны помнить о целевом домене.

Хотя суть этого по-прежнему верна для Sempiler, смысл этого утверждения заключался в том, что разработчик будет писать разный код для разных доменов (платформ).

Это слишком пуристично, снижает производительность и не приносит удовольствия (и позиция, порожденная недовольством состоянием программного обеспечения, а не обоснованием).

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

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

Ознакомиться с текущими целями проекта можно на домашней странице.