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

flang и FTS: что унаследовано, что своё

flang и FTS: что унаследовано, что своё

Первое, что стоит уяснить перед чтением репозитория digitable-lol/flang: он назван по языку, но бо́льшая часть его содержимого — это FTS. Сам flang живёт в подкаталоге flang/; в корне лежат ядро FTS на TypeScript (src/), его документация, редакторская обвязка и девять инструментов в tools/.

Ещё несколько часов назад это приходилось выяснять самостоятельно: README.md начинался словами «FTS — Formal Type Surface» и упоминал flang одним пунктом списка. Теперь README переписан от языка и отвечает на вопрос сам — разделом «How FTS and flang relate» и абзацем, который стоит привести целиком, потому что так о себе пишут редко:

What is not settled, stated plainly. The TypeScript core is still the production implementation of FTS and still the reference the flang rewrite is measured against — not the other way round. […] So the repository is named for the language it is becoming, while a large part of its contents is still the FTS toolchain.

Различие практическое, а не педантичное. Почти вся инфраструктура репозитория принадлежит FTS, и приписывать её flang было бы прямым враньём о состоянии суточного языка.

Что чему принадлежит

Что Кому принадлежит
flang/SPEC.md, src/, bin/, stdlib/, core/, examples/, test/ flang
src/, schema/, docs/ FTS
tools/ — девять инструментов, среди них ftsc, ftsvm, ftspec FTS
editors/ — tree-sitter, VS Code, Chroma, Linguist FTS
benchmarks/, web/, examples/ в корне FTS
README.md, AGENTS.md, CONTRIBUTING.md в корне обоим

Пара следствий, которые легко перепутать.

Восемь целевых языков — это не flang. README.md обещает печать в C, Rust, C#, Java, Elixir, Go, Python и TypeScript; это возможности tools/ftsc, проектного компилятора FTS-моделей. У flang бэкендов пять, и их список видно из ответа самого инструмента на несуществующую цель:

$ node flang/bin/flang.mjs emit проба.flang --target zzz
{"error":"неизвестная цель «zzz»; доступны: c, go, js, python, rust", …}

Списки сближаются, но не совпадают. Четыре цели flang есть и у ftsc — C, Go, Python, Rust, — а пятая, js, своя: ftsc печатает TypeScript. В обратную сторону у ftsc остаются C#, Java и Elixir, которых у flang нет вовсе. Rust и Python, кстати, появились у flang уже после того, как была написана первая редакция этого трека.

Подсветки синтаксиса для flang нет. Каталог editors/ содержит tree-sitter-fts, vscode-fts, chroma/fts.xml и запись для Linguist — все про .fts. Ни грамматики, ни расширения, ни описания языка для .flang в репозитории нет. Подробнее — в главе «Чего в языке пока нет».

Бенчмарки — не про flang. В benchmarks/ лежит воспроизводимый стенд и базовая линия на Apple M1 Max, где измеряется компиляция и валидация FTS-модели. Замеров производительности интерпретатора flang или сгенерированного им кода в репозитории нет.

Что flang взял у FTS

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

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

Имена как фразы. «Рассчитать скидку», «постоянный клиент» — имя пишется в ёлочках или обычных кавычках и может содержать пробелы. Это решение FTS, и flang его повторяет буквально, вплоть до нормализации NFC в лексере.

Две равноправные поверхности. Русская и английская компилируются в один AST. Таблица ключевых слов в flang/src/lexer.mjs устроена как «канонический идентификатор → список поверхностей»:

  let: ["пусть", "let"],
  if: ["если", "if"],
  then: ["то", "then"],
  else: ["иначе", "else"],
  match: ["разбор", "match"],
  case: ["случай", "case"],

Парсер не знает, на каком языке написан исходник.

Формат диагностик. { code, message, severity, span } — ровно как в ядре. Коды у flang свои (FLANG_LEX, FLANG_PARSE, FLANG_TYPE, FLANG_NOT_TOTAL, …), но форма записи общая, чтобы редакторы и CI, уже умеющие читать вывод FTS, читали и вывод flang.

Контракт CLI. Результат — JSON в stdout, диагностика — JSON в stderr, отказ — ненулевой код возврата. В шапке flang/bin/flang.mjs это объяснено прямо: расхождение в контракте вывода стоило бы дороже, чем любая «улучшенная» подача.

Арифметика. Числа — IEEE-754 double, проценты считаются как (процент / 100) * значение именно в этом порядке. Не из любви к деталям: смена порядка множителей меняет последний бит мантиссы, и уже сгенерированный код разошёлся бы с новым.

Что flang добавил

FTS-модель описывает предметное решение: объекты с полями, утилиты с правилами, свойства и примеры. Циклов, рекурсии, списков и строковых вычислений там нет — и это осознанное ограничение исполняемой спецификации, а не недоделка.

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

  • суммы типов (тип «Токен» с вариантами) и сопоставление с образцом;
  • списки как полноценные значения, с голова/хвост, отобразить, отфильтровать, свёртка;
  • строки как данные, а не только как тип поля;
  • рекурсию;
  • локальные связывания пусть;
  • модули с импортом.

Одну вещь он сознательно не добавил: функции не являются значениями первого класса. Это не забыто, а записано в flang/SPEC.md, раздел 3, вместе с обоснованием — см. главу «Система типов».

Обещание совместимости

Центральный тезис записан в flang/SPEC.md, раздел 1:

вся существующая FTS-модель — это валидная программа flang, целиком лежащая в тотальном классе

И тут же — почему это не декларация:

Обратная совместимость не опциональна: flang обязан принимать любой .fts без правок и давать тот же результат, что ядро FTS. Это проверяется тестом на всех моделях репозитория.

Механика — модуль flang/src/compat.mjs, мост из FtsDocument в AST flang. Объект становится записью, утилита — тотальной функцией, правила — последовательностью независимых если, свойства — постусловиями, примеры — примерами. В шапке модуля перечислено, что именно повторяется буквально, а не «по смыслу»: порядок правил, короткое замыкание условий внутри правила, порядок множителей в процентах и проверка свойств после всех правил.

Отдельное решение стоит отметить: код и текст ошибки нарушенного свойства едут в AST данными, полем postconditions функции, а не знанием, зашитым в интерпретатор. Иначе совпадение кодов ошибок между двумя движками зависело бы от реализации и было бы случайным.

Масштаб проверки заявлен в корневом README.md: сверка с ядром на 19 593 входах, ноль расхождений.

Что из этого следует для практики

Ничего срочного. FTS-модель по-прежнему исполняется ядром FTS, и переходить на flang ради тех же моделей незачем. Значение обещания в другом: оно фиксирует, что flang не «новый язык рядом», а надстройка, у которой есть проверяемое обязательство перед предшественником. Для языка, который меняется ежедневно, это единственная часть, на которую можно опереться.

Практическая оговорка: чтобы CLI прочитал .fts, нужно сначала собрать ядро FTS — мост подгружает dist/src/index.js. Без сборки получите честный ERR_MODULE_NOT_FOUND. Файлы .flang работают без этого; подробности в главе «Инструмент».

Дальше — глава «Два класса программ», про решение, ради которого язык вообще устроен так, а не иначе.

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

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

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

Доска запросов
Дальше