Тема написания собственного YAP преследует меня уже около шести месяцев. Я не ставил целью "убить" CoffeeScript, TypeScript, ELM и тысячи других. Я просто хотел понять, что такое кухня и как они обычно пишутся.
Неудобно, но большинство этих языков используют Jison (Bison для JavaScript), но Jison в основном делает все и строит AST по определенным правилам, поэтому "понять его" не было моей целью. (Сам Jison является отличным инструментом и выполняет большую часть работы, но не в настоящее время).
В конце концов, путем проб и ошибок (или чтения статей и обратного проектирования), я научился создавать свой собственный полноценный язык программирования, начиная с разбора исходного текста по словарю и заканчивая переводом его в JS-код.
Руководство не имеет никакого отношения к JavaScript и было выбрано исключительно для скорости разработки и читабельности, поэтому вы можете писать "lisp" / "python" / "совершенно новый синтаксис" на любом языке.
Кроме того, до момента создания компилятора (в данном случае транслятора), процесс создания языка такой же, как и процесс создания языка, скомпилированного в ASM /JVM биткод/ LLVM биткод и т.д. Другими словами, этот учебник не ограничивается созданием переводимого языка JavaScript.
Весь код, описанный в этом (и будущих) руководствах, доступен на Github. Для удобства пользования теги расположены в начале и конце статьи.
Немного теории
Не углубляясь в Википедию, процесс преобразования исходного кода в конечный JS-код выглядит следующим образом
Что здесь происходит:.
1) Лексер.
Исходный код нашей программы разделен на лексеры. Проще говоря, это поиск ключевых слов, грамматики, символов, идентификаторов и т.д. в исходном коде.
Другими словами, вывод этого (CoffeeScript):.
Получите это (сокращенно):.
CoffeeScript чувствителен к отступам, поскольку нет явного варианта блока со скобками.< и >блоки имеют отступы (INDENT и OUTDENT), эффективно заменяя круглые скобки.
2) Аналитик.
Парсер компилирует AST из лексем. Он просматривает всю таблицу и рекурсивно выбирает подходящий шаблон на основе типа или последовательности символов.
Из лексем, полученных на шаге 1, синтаксический анализатор строит почти такое дерево (сокращенная нотация).
Пусть вас не вводит в заблуждение размер дерева. На самом деле он генерируется рекурсивно, и создать его несложно.
3) Компилятор.
Окончательная сборка кода с помощью AST. Этот компонент может быть заменен компилятором байткода или временем выполнения, но в контексте этой серии статей мы обсудим реализацию транслятора на другом языке программирования.
Компилятор (читай транслятор) преобразует абстрактное синтаксическое дерево в код JavaScript.
Вот и все. Большинство компиляторов работают по этой схеме (с небольшими изменениями, см. ниже). В некоторых случаях процесс рационализации источника добавляется к потоку символов, в других — синтаксический разбор и компиляция объединяются в один этап, что находится вне нашего контроля).
Habrlang
Поэтому, разобравшись в теории, мы создаем свой собственный язык программирования. Это будет иметь примерно следующий синтаксис (для простоты мы будем смешивать Ruby, Python и CoffeeScript).
В следующей главе мы реализуем все основные классы трансляторов и научим вас переводить комментарии Habrlang в JavaScript.
