lumen

Динамически типизированный скриптовый язык на Kotlin. Исходник сканируется, парсится в AST, компилируется в байткод и выполняется на стековой виртуальной машине.

lumen
TL;DR

Динамически типизированный скриптовый язык на Kotlin, который по-настоящему выполняет код: сканирование и разбор в AST, компиляция в байт-код и запуск на собственной стековой виртуальной машине.

Обзор

lumen - это динамически типизированный скриптовый язык, написанный на Kotlin, который по-настоящему выполняет код. Исходник сканируется, разбирается в AST, компилируется в байт-код, а в конце запускается на собственной стековой виртуальной машине. Выглядит как обычный скриптовый язык, но под низом каждая строка проходит полный путь от текста до инструкций машины.

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

Интерпретатор, ходящий по синтаксическому дереву, прост в написании, но быстро упирается в потолок производительности. Каждый прогон одного и того же цикла означает повторный обход тех же узлов дерева, со всеми накладными расходами, которые это несёт. Для маленького скрипта это незаметно, для чего угодно крупнее - ощутимо.

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

От текста до байт-кода

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

example.lm · javascript
fun fib(n) {
  if (n < 2) return n;
  return fib(n - 1) + fib(n - 2);
}

print(fib(10));
1
Сканер

исходный текст становится токенами.

2
Парсер

токены складываются в синтаксическое дерево (AST).

3
Компилятор

дерево превращается в байт-код.

4
Виртуальная машина

байт-код выполняется на стеке, инструкция за инструкцией.

Стековая машина изнутри

У стековой машины нет регистров, как у настоящего процессора - операнды она кладёт на стек и снимает с него. Сложение сводится к трём движениям: положить два числа, снять их и сложить, положить результат. Та же модель под капотом движет, среди прочего, Python и Java, поэтому написать её самому - это заглянуть в то, как работают языки, которыми вы пользуетесь каждый день.

Сложение на стековой машине

ШагИнструкцияСтек после
1положить 22
2положить 32, 3
3сложить5
i
Примечание

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

Эффект: слово "скомпилировано" перестаёт быть абстракцией

На выходе - язык, который по-настоящему выполняет код - он не интерпретирует дерево на ходу, а компилирует его и запускает на собственной машине. Это проект, после которого слово "скомпилировано" перестаёт быть абстракцией, потому что сам прошёл весь путь от символа в файле до инструкции, которая что-то делает. Маленький язык, но полный вплоть до самого выполнения.

Больше проектов

Другие работы из той же категории - посмотрите, как мы решаем похожие задачи.

Есть похожий проект?

Напишите нам - смета бесплатна и приходит в течение часа.