Как читать репозиторий и как участвовать
Язык суточной давности — плохой выбор для продакшена и хороший для чтения. Всё помещается в один каталог, слои не переплетены, и почти каждое решение объяснено в комментарии рядом с кодом. Эта статья — про то, как этим воспользоваться.
Что где лежит
flang/
SPEC.md спецификация языка
bin/flang.mjs CLI: check | run | test | facts | ast | emit
src/
lexer.mjs текст → токены (INDENT/DEDENT)
parser.mjs токены → AST
types.mjs проверка и вывод типов, исчерпывающность разбора
totality.mjs анализ структурного убывания
interpret.mjs вычисление AST
builtins.mjs строки, списки, числа, представление значений
link.mjs связывание модулей
factcheck.mjs встраиваемый факт-чекинг
compat.mjs мост: FtsDocument → AST flang
emit/{c,go,js,python,rust}.mjs печать в целевые языки
stdlib/ пять модулей на самом flang
core/ ядро FTS, переписанное на flang
examples/ решения LeetCode и проба импорта
test/ 22 файла тестов
Рядом с каждым emit/<язык>.mjs, кроме js.mjs, лежит каталог того же имени с
рантаймом и прогонщиком на целевом языке: emit/c/, emit/go/, emit/rust/,
emit/python/.
Сверяясь со спецификацией, держите в голове: раздел 6 SPEC.md перечисляет
слои без link.mjs, из бэкендов знает один emit/js.mjs, а команду fmt
называет существующей. Код новее документа, и разрыв растёт.
Маршрут чтения
Если читать подряд, естественный порядок такой.
flang/SPEC.mdцеликом — 18 КБ, полчаса. Разделы 1 (два класса), 3 (типы) и 5 (AST) — обязательны, остальное по интересу. Раздел 10, «известные ограничения», прочитайте обязательно: он задаёт тон.flang/src/totality.mjs, первые шестьдесят строк. Не код, а объяснение метода — с контрпримером, доказывающим, почему нужна единая убывающая позиция. Лучший текст в репозитории.flang/stdlib/optional.flang— 126 строк, читается как статья. Тип, объяснение зачем, и честный разбор двух ограничений, которые видны прямо в коде модуля.flang/src/interpret.mjs, шапка — почему явный стек кадров вместо рекурсии по стеку JS.flang/src/emit/go.mjs, шапка — шесть пунктов «где расходятся сами языки». Полезно даже тем, кому flang безразличен. Следом — шапкиemit/rust.mjsиemit/python.mjs: тот же жанр, но про другие языки, и вместе три файла читаются как сравнительный разбор.flang/core/SPEC.md— если интересно, как на этом языке пишут настоящий парсер. Раздел «Долги» занимает больше половины и стоит того.
Файл, который не стоит читать целиком: flang/src/parser.mjs, 1922 строки.
И flang/core/parser.flang, 2675 строк на самом flang, — туда лучше ходить с
конкретным вопросом.
Запуск
Клон, и всё:
git clone https://github.com/digitable-lol/flang.git
cd flang
node flang/bin/flang.mjs check flang/stdlib/lists.flang --pretty
node flang/bin/flang.mjs test flang/stdlib/optional.flang --pretty
Ни npm install, ни сборки. В README.md заявлен Node.js 20 или новее; CI
проверяет на 20, 22 и 24.
Тесты
node --test flang/test/*.test.mjs
Наш прогон на собранном ядре FTS дал 1163 теста: 1140 прошли, 23 пропущены,
ноль упали. Все двадцать три пропуска — тесты бэкенда Go: они требуют
настоящего тулчейна Go, а его на машине нет. Тесты Rust и Python не
пропускались — rustc 1.96.0 и python3 3.12.3 в системе есть, поэтому оба
бэкенда действительно печатали, компилировали и запускали код настоящим
процессом.
Оговорка про сборку существенна. Восемь тестовых файлов из двадцати двух
сверяются с ядром FTS напрямую — compat, core-evaluate, core-json,
core-parser, emit-c, emit-js, factcheck, interpret, — и импортируют
dist/src/index.js. На свежем клоне без сборки они дают
ERR_MODULE_NOT_FOUND, а не «ошибку теста»:
npm install && npm run build
npm run test:flang
Скрипт test:flang — это ровно node --test flang/test/*.test.mjs; npm test
прогоняет ядро, инструменты и flang.
Остальные четырнадцать файлов (lexer, parser, types, totality, link,
stdlib, leetcode, regression, builtins, emit-go, emit-python,
emit-rust, cli-emit, core-lexer) работают без сборки. Например:
$ node --test flang/test/totality.test.mjs
ℹ tests 25
ℹ pass 25
ℹ fail 0
Что говорят AGENTS.md и CONTRIBUTING.md
Ещё несколько часов назад оба файла были написаны про FTS, а про flang не говорили ничего; правила приходилось вычитывать из кода. Теперь оба переписаны, и это как раз тот случай, когда стоит прочитать документ, а не догадываться.
AGENTS.md теперь открывается словами «этот репозиторий содержит два языка» и
требует читать раздел «How FTS and flang relate» из README прежде, чем менять
любой из них. Разделение зафиксировано прямо: семантика FTS живёт в
src/parser.ts, src/validate.ts, src/interpreter.ts, семантика flang — в
flang/src/, а flang/bin/flang.mjs в обоих случаях адаптер. И отдельным
пунктом: «ядро на TypeScript — эталон для flang/core/, а не наоборот;
сознательное расхождение уходит в список долгов flang/core/SPEC.md, но никогда
в молчание».
У CONTRIBUTING.md появился раздел «Changes to flang must include» из четырёх
требований:
- соответствующий раздел
flang/SPEC.md, обновлённый тем же изменением; - тест тайпчекера или тотальности — смотря что затронуто;
- совпадающее поведение интерпретатора и каждого бэкенда, «потому что тесты бэкендов сравнивают собранный бинарник с интерпретатором, и расхождение — это падение, а не заметка»;
- запись долга в
flang/core/SPEC.md, если расхождение с ядром на TypeScript оставлено сознательно.
Требование заметок в MIGRATION.md, которое стояло раньше, ушло: файла с этим
именем в репозитории нет с коммита 18b4a3b.
Отдельного полноценного руководства для участия в разработке flang по-прежнему нет — четыре пункта это не руководство. Но остальные правила, которых репозиторий держится последовательно, из кода по-прежнему вычитываются, и им разумно следовать.
AST — контракт, менять нельзя мимоходом. Раздел 5 спецификации начинается словами: парсер выдаёт ровно это, тайпчекер, интерпретатор и кодогенераторы читают только это, «ни один слой не разбирает текст повторно».
Коды и тексты диагностик — наблюдаемое поведение. В бэкендах записано, что сообщения копируются буквально, вплоть до кавычек-ёлочек, потому что «код и текст — часть наблюдаемого поведения, а не украшение». Изменение текста ошибки — изменение поведения.
Изменение в языке — изменение в шести местах. Интерпретатор и пять бэкендов
обязаны совпадать. Добавили встроенную форму — реализуйте её в builtins.mjs,
emit/js.mjs, emit/c.mjs, emit/go.mjs, emit/rust.mjs и
emit/python.mjs, причём везде, кроме JS, ещё и в рантайме на целевом языке.
Цена одной встроенной формы за сутки выросла в полтора раза, и это, пожалуй,
главное практическое следствие пяти бэкендов.
Совместимость с FTS проверяется, а не декларируется. Любая существующая FTS-модель обязана оставаться валидной программой flang с тем же результатом.
Отвергнутую альтернативу записывают рядом. Это не формальное требование, но стиль выдержан по всему репозиторию: почти каждое нетривиальное решение сопровождается объяснением, что рассматривалось и почему не подошло. Правка в этом стиле будет читаться как своя.
Долг оформляется явно. В flang/core/SPEC.md есть отдельный раздел, а в
тестах — случаи с именами вида «известный долг: одинокий суррогат не
экранируется», закреплённые затем, «чтобы закрытие долга не прошло молча».
Приём хороший и переносится в любой проект.
С чего начать, если хочется поучаствовать
Самое дешёвое и полезное — обновить то, что уже устарело. Мы нашли пять
пунктов в flang/examples/leetcode/index.json, описывающих ограничения,
которых больше нет (глава «Чего в языке пока нет»), и комментарии в
flang/stdlib/optional.flang и result.flang, объясняющие, почему у функций
нет примеров, — при том что примеры с вариантами теперь работают. Проверка
каждого пункта занимает минуту, правка — строку.
Там же — раздел 6 flang/SPEC.md: он всё ещё перечисляет из бэкендов один
emit/js.mjs и всё ещё называет существующей команду fmt, которой нет.
Дальше по возрастанию сложности: диагностика для варианта, названного ключевым
словом (спецификация сама называет текущее сообщение долгом); недостающие
встроенные формы, названные в core/SPEC.md как условие закрытия долгов
(«нормализовать»; «код символа», которая держит сразу два долга — одинокий
суррогат в печати JSON и печать имени-неидентификатора в ts_compat; и обратная
ей «символ по коду», без которой не развернуть \uXXXX в литерале); логические
операции, отсутствие которых портит читаемость любого условия сложнее одного
сравнения.
Лицензия
BSD 2-Clause, LICENSE в корне; намерение по-русски — в LICENSE-RU.md. В
README.md отмечено, что ранние версии выходили под Apache-2.0, унаследованной
от исходного репозитория, а не выбранной; кто получил код под Apache-2.0,
сохраняет те права.
И напоследок
Всё, что описано в этом треке, сверено на 4 августа 2026 года, коммит 9164aa3.
Язык меняется быстрее, чем пишется документация о нём, и мы убедились в этом
дважды. Сначала — обнаружив устаревшим список ограничений, который сам проект
составил несколькими часами раньше. Потом — на собственном тексте: между первой
редакцией трека (коммит ec89e78) и этой сверкой прошёл час, и за него у языка
прибавилось два бэкенда кодогенерации, а парсер ядра FTS научился разбирать
скобочный диалект. Править пришлось одиннадцать глав из тринадцати.
Отстал за тот же час и корневой README.md, переписанный на двадцать четыре
минуты раньше последнего коммита: он до сих пор говорит про три бэкенда и про
«53 модели из 56». Отставание документации — не чья-то небрежность, а свойство
режима, в котором живёт репозиторий.
Поэтому главный совет остаётся тем же, что в главе «flang: язык, которому сутки»: проверяйте запуском. Инструмент отвечает за секунды и не врёт.