flang — полный язык поверх FTS Как читать репозиторий и как участвовать
0%

Как читать репозиторий и как участвовать

Как читать репозиторий и как участвовать

Язык суточной давности — плохой выбор для продакшена и хороший для чтения. Всё помещается в один каталог, слои не переплетены, и почти каждое решение объяснено в комментарии рядом с кодом. Эта статья — про то, как этим воспользоваться.

Что где лежит

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 называет существующей. Код новее документа, и разрыв растёт.

Маршрут чтения

Если читать подряд, естественный порядок такой.

  1. flang/SPEC.md целиком — 18 КБ, полчаса. Разделы 1 (два класса), 3 (типы) и 5 (AST) — обязательны, остальное по интересу. Раздел 10, «известные ограничения», прочитайте обязательно: он задаёт тон.
  2. flang/src/totality.mjs, первые шестьдесят строк. Не код, а объяснение метода — с контрпримером, доказывающим, почему нужна единая убывающая позиция. Лучший текст в репозитории.
  3. flang/stdlib/optional.flang — 126 строк, читается как статья. Тип, объяснение зачем, и честный разбор двух ограничений, которые видны прямо в коде модуля.
  4. flang/src/interpret.mjs, шапка — почему явный стек кадров вместо рекурсии по стеку JS.
  5. flang/src/emit/go.mjs, шапка — шесть пунктов «где расходятся сами языки». Полезно даже тем, кому flang безразличен. Следом — шапки emit/rust.mjs и emit/python.mjs: тот же жанр, но про другие языки, и вместе три файла читаются как сравнительный разбор.
  6. 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: язык, которому сутки»: проверяйте запуском. Инструмент отвечает за секунды и не врёт.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов