Самоприменение: компилятор flang, написанный на flang
Каталог flang/self/ — исходники компилятора языка, написанные на самом языке. Двоичный компилятор собирается не из них напрямую, а из их печати в C99, закоммиченной в bootstrap/ (точка раскрутки): make -C bootstrap требует только cc и make. Второй реализации компилятора в дереве нет: реализация на JavaScript снята 20 августа 2026 (коммит fe8e8a37), и сверять слои теперь есть с чем ровно в одном смысле — с их же печатью, см. «Критерий готовности».
Компилятору тотальность не обязательна: он вправе упереться в предел шагов и сказать об этом. Поэтому в flang/self/ разрешены обычные функции (функция, а не тотальная функция); каждая такая функция — названный долг слоя, см. «Долги».
Состав каталога
64 файла .flang, 117 666 строк Имя модуля — первая строка файла; здесь оно повторено, чтобы читать использует «…» в других файлах без поиска.
Ядро компилятора
| файл | модуль | что делает |
|---|---|---|
lexer.flang | «Лексер flang» | текст → токены; отступы значимы |
parser.flang | «Парсер flang» | токены → AST (docs/flang/SPEC.md, раздел 5); словарь ввода-вывода (суммы «Поручение», «Отклик», «Продолжение») выписан здесь данными |
types.flang | «Проверка типов» | проверка и вывод типов, исчерпывающность разбора, типизация постусловий |
totality.flang | «Тотальность flang» | анализ завершаемости: часть значения, числовая мера, точный шаг, объявленная мера; отметка мер («Отметить меры») |
defunc.flang | «Дефункционализация» | снятие функций первого класса перед печатью; сторожа меры |
carriers.flang | «Носители обещания» | чем несётся обещание завершения каждой функции |
link.flang | «Связывание flang» | связывание модулей по использует в одну плоскую программу («Связать исходники»), отбрасывание недостижимого |
builtins.flang | «Встроенные формы flang» | встроенные формы языка, общие для всех целей |
tags.flang | «Теги программы» | теги функций как значений |
declared-properties.flang | «Объявленные свойства» | общая основа пяти законов (commutative, distributive, idempotent, monotone, partialorder): как берётся сетка значений, её предел, имя значения в тексте отказа. Всё здесь считается на конечной сетке и доказательством не является; устройство свойства (объявлена ли функция, сходятся ли типы) доказано раньше сличением объявлений |
cli.flang | «Командная строка flang» | разбор доводов команды flang (перечень команд — flang --help); ключ согласия --на-веру/--trust у run и io (ADR-0045) |
Печать в цели
| файл | модуль |
|---|---|
emit-c.flang | «Печать в C» |
emit-cpp.flang | «Печать в C++» |
emit-go.flang | «Печать в Go» |
emit-rust.flang | «Печать в Rust» |
emit-java.flang | «Печать в Java» |
emit-js.flang | «Печать в JavaScript» |
emit-elixir.flang | «Печать в Elixir» |
emit-python.flang | «Печать в Python» |
emit-csharp.flang | «Печать в C#» |
Цели те же, что принимает flang emit --target: c|cpp|go|rust|java|js|ts|elixir|python|csharp.
Доказательства
| файл | модуль | что делает |
|---|---|---|
obligations.flang | «Обязательства программы» | постусловие и теорема → обязательство: что именно надо доказать, данными |
proof-kernel.flang | «Сведение к допущениям» | ядро: сведение цели к допущениям; аксиом ноль |
proof-initial.flang | «Начальная алгебра» | принцип индукции читается с объявления суммы и строится термом |
proofterm.flang | «Проверяльщик термов» | свёртка терма доказательства в вердикт |
proof.flang | «Ведомость доказательства» | отчёт flang check --proof; в сборку компилятора как слой печати не входит; обычных функций в нём быть не должно (проверка: grep -c '^функция' flang/self/proof.flang → 0) |
proof-record.flang | «Запись доказательства» | запись доказательства для независимого чекера (docs/flang/proof/checker/README.md) |
obligations-and-kernel-examples.flang | «Обязательства и ядро под примерами» | свои примеры слоя обязательств и ядра: flang test считает примеры ввезённых модулей вместе со своими, и слой без единого своего примера выглядит проверенным; ядро здесь спрашивается о цели обязательства напрямую, без подстановки тела по случаям, то есть меньше, чем делает flang check --proof |
type-refusals.flang, totality-refusals.flang | «Отказы типов», «Отказы завершаемости» | отказы проверки типов и анализа завершаемости под примерами: отказ приходит значением с полем «код», и его сверяет обычный пример. Два файла, потому что types.flang и totality.flang ввозят из emit-c.flang разные наборы имён, а связывание отдаёт второму слою список первого |
Вычисление, инструменты, службы
| файл | модуль | что делает |
|---|---|---|
interpret.flang | «Вычислитель flang» | вычисление AST явным стеком: машина «кадр → значение»; формы вне среза отвечают кодом FLANG_SELF_EVAL_UNSUPPORTED, а не молчат |
io.flang | «Проверка планов» | проверка объявления план (flang io) |
repl/repl.flang | «Оболочка flang» | flang без доводов и flang repl |
lsp.flang | «Языковой сервер flang» | flang lsp --stdio |
mcp.flang | «Служба MCP» | flang --mcp-mode |
hotswap.flang | «Горячая замена» | замена кода работающего процесса |
factcheck.flang | «Факт-чекинг» | flang facts |
bounded.flang | «Ограниченность» | ограниченность по шагам и памяти |
bootstrap/corpus.flang | «Корпус flang» | прогон примеров корпуса |
bootstrap/compiler.flang | «Compiler flang» | точка входа: связать и напечатать, связать и проверить, напечатать связанный AST; связать и запустить с вердиктом о доказанности — «Запуск исходников» (flang run) и «Поиск плана исходников» (flang io) до вычисления печатают «доказано: утверждений N» либо «не доказано: …» о всём замыкании и без --на-веру недоказанную программу не запускают, код 3 (ADR-0045) |
Процессы
| файл | модуль |
|---|---|
conc.flang | «Планировщик конкурентности» |
processes.flang | «Проверка процессов» |
failures.flang | «Множество отказов процесса» |
distributed.flang | «Узел flang» |
Контракт этого слоя — docs/flang/concurrency/SPEC.md.
Законы и категории
| файл | модуль |
|---|---|
setoid.flang, setoid-oracle.flang | «Законы категории», «Оракул категорий» |
functor.flang, functor-oracle.flang | «Законы функтора», «Оракул функтора» |
monoid.flang | «Законы моноида» |
monad.flang, monad-expand.flang | «Монада», «Разворачивание монад» |
iso.flang | «Законы изоморфизма» |
sets.flang, sets-oracle.flang | «Отношения множеств», «Оракул множеств» |
commutative.flang, distributive.flang, idempotent.flang, monotone.flang, partialorder.flang | законы коммутативности, дистрибутивности, идемпотентности, монотонности, частичного порядка |
oracle.flang, law-oracle.flang | «Оракул свойств», «Оракул законов» |
grid.flang | «Поиск нарушений на сетке» |
Контракт этого слоя — docs/ct/spec.md. Законы считаются на сетке с названным числом значений; это счёт, а не доказательство, и отчёт говорит об этом словами.
Точка входа — bootstrap/compiler.flang
Компилятор целиком — один модуль «Compiler flang», связывающий слои в одну программу. Порядок использует в нём — требование, а не оформление: связывание сливает объявления в плоское пространство имён, и что видно из файла, решает первый импорт этого файла в обходе; поэтому parser.flang идёт первым, печать в цели — целиком, link.flang — последним (довод — в комментарии шапки файла).
Чтения файлов в языке нет. Исходники приезжают списком записей «Исходник» (путь и текст), и использует … из "…" разрешается по этому списку, а не по файловой системе. Ввод-вывод — у хозяина: двоичного flang (bootstrap/flang_cli.c).
AST — контракт между слоями
Форма AST задана в docs/flang/SPEC.md, раздел 5, и другой нет. Печатает её flang ast <файл>; печать в JSON — модуль «Печать JSON» (flang/core/json.flang). Если форма и печать расходятся, расширяется печать, а не заводится вторая.
Критерий готовности — неподвижная точка
0. bootstrap/ (в дереве) → cc, make → flang₁
1. flang₁ печатает flang/self/… → C → должен совпасть с bootstrap/ байт в байт
Совпадение означает, что компилятор воспроизводит сам себя. Сверяет:
make -C bootstrap # ярлык: bootstrap/flang run-script build
sh scripts/bootstrap-reprint.sh --check # ярлык: bootstrap/flang run-script reprint:check
Проверка печатает файлы заново и сравнивает с закоммиченными; расхождение называется файлом, байтом и строкой. Она краснеет на всех четырёх видах расхождения: правка исходника без перепечатки, правка bootstrap/ руками, пропавший файл печати, лишний файл от прошлой печати. Что печатает сам двоичный, а не что-то другое, стережёт bootstrap/flang run-script bootstrap-point:check (scripts/bootstrap-point-by-binary.flang).
Пределы печати (MAX_STEPS, MAX_DEPTH) записаны один раз — в scripts/bootstrap-reprint.sh — и попадают в напечатанный байт (FL_MAX_DEPTH в bootstrap/flang_runtime.h), то есть участвуют в совпадении. Печать с другими пределами разойдётся. Довод по величине пределов — в шапке scripts/bootstrap-reprint.sh; устройство круга для читателя — docs/guide/bootstrap-circle.ru.md.
В CI сверку выполняет .github/workflows/reprint.yml.
Порядок обновления точки раскрутки
- Правка любого файла, входящего в замыкание печати
bootstrap/compiler.flang(файлыflang/self/,flang/core/json.flangи модулиflang/stdlib/, которые они используют). sh scripts/bootstrap-reprint.shтем же коммитом — перепечататьbootstrap/.sh scripts/bootstrap-reprint.sh --checkотвечает 0.
Править bootstrap/ руками нельзя: правка потеряется при первой перепечатке, а до того валит сверку. Устройство каталога — docs/bootstrap-point.md.
Долги
Долг слоя — каждая функция, объявленная функция, а не тотальная функция. Счёт по файлу и по каталогу:
grep -c '^функция' flang/self/<файл>.flang
git ls-files 'flang/self/*.flang' | xargs grep -c '^функция'
Причины, по которым обычная функция допускается, две, и любая другая — ошибка:
- обход AST, глубина которого не выводится из типа узла (печать в цели, проверка типов, сведение в ядре доказательств);
- рекурсия по графу (граф импортов в
link.flang, граф вызовов в анализе завершаемости), где убывание — по посещённому множеству, а не по части значения.
Слои, где обычных функций быть не должно и сегодня нет: proof.flang, lexer.flang, builtins.flang, cli.flang, factcheck.flang, hotswap.flang, bootstrap/corpus.flang (проверка — команда выше; появление обычной функции в любом из них — регресс, а не долг).
Долги вычислителя: формы, которые interpret.flang не вычисляет, отвечают кодом FLANG_SELF_EVAL_UNSUPPORTED; молчаливого пропуска нет по построению.