Skip to content

Commit ced8665

Browse files
author
Marat Zimnurov
committed
Оболочка в бинарнике из brew: язык можно потрогать сразу после установки
Поставив компилятор через brew или asdf, потрогать язык было нельзя: бинарник читает JSON со stdin и отвечает JSON — интерфейс для программ, не для человека. Оболочка была только в Node-версии, то есть ровно там, где Node и так есть. Вычислителя в этом бинарнике нет — в нём лексер, разбор, типы, завершаемость и печать. Поэтому выражение вычисляется единственным честным способом: сессия печатается в C, собирается системным `cc` против рантайма, поставленного рядом формулой, и запускается. Компилируется только сессия — рантайм уже собран. Без `cc` оболочка НЕ выключается. Она продолжает делать то, что бинарник умеет, и говорит об этом один раз при запуске, а не на каждой строке: вычислять нечем: компилятора C нет (ни $FLANG_CC, ни cc, ни gcc, ни clang). Разбор, типы и завершаемость проверяются по-прежнему; выражение отвечает «проверено». Отдельным файлом, а не внутри прогонщика, потому что обещания у них разные: прогонщик остаётся переносимым C99, который ничего не спрашивает у мира, а оболочка обязана спросить, где `cc` и где каталоги установки. И печатается она по просьбе — осмысленна ровно у одной программы, у самого компилятора: только у него есть точки входа, которые оболочка зовёт. НЕПОДВИЖНАЯ ТОЧКА СОШЛАСЬ, и файлов в ней стало семь вместо шести — оболочка встала в сверку наравне с остальными, а не сбоку. ПОПУТНО НАЙДЕНА ДЫРА В ПРОВЕРКЕ, и она дороже самой оболочки. «Долги» в self/SPEC.md записывают, сколько в каждом слое функций и сколько из них тотальных, и каждая обычная там названа с причиной — то есть это контракт. Но тест сверял только два слоя из пяти, а числа для них стояли константами в самом тесте. Итог: «Долги» разошлись с кодом сразу у трёх слоёв — parser, types и emit-c, — и ни один прогон этого не заметил. Теперь тест читает числа ИЗ «Долгов» и сверяет все пять слоёв. Список, который обязан совпадать с кодом, но лежит отдельно от него, расходится — единственная защита в том, чтобы брать число оттуда, где оно записано. Проверено настоящим релизным бинарником: объявление принято с доказательством завершения, «Удвоить» от 21 дало 42, «разложить "мир" на символы» — три буквы, ошибка типов названа с кодом, строкой и столбцом ВВОДА. Отдельно проверено поведение без `cc` в PATH. Весь набор: 71 / 416 / 1544, ноль падений.
1 parent c94f283 commit ced8665

14 files changed

Lines changed: 4189 additions & 37 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@ an install path. Neither needs anything but a C compiler. This is how self-hosti
6161
ship — Go carried generated C for years, Nim still does.
6262

6363
**Be clear about what that binary is.** It is the five layers of [`flang/self/`](flang/self):
64-
lexer, parser, types, totality, printing to C. There is no evaluator among them, so running a
65-
program, running its examples or opening a shell needs the full toolchain below.
64+
lexer, parser, types, totality, printing to C. There is no evaluator among them — which is why
65+
`flang repl` there evaluates the only honest way this binary can: it prints the session to C,
66+
builds it with the system `cc` against the runtime installed beside it, and runs that. Without a
67+
`cc` the shell does not switch off — it keeps checking parse, types and totality, and says so
68+
once. Running a program or its examples non-interactively still needs the full toolchain below.
6669

6770
**The full toolchain does need Node.js 20 or newer**, and here is exactly why: the interpreter,
68-
the shell, the language server, the MCP server and seven of the eight backends exist only in
69-
JavaScript. The self-hosted compiler — the one in the release — prints to **C and nothing else**.
71+
the language server, the MCP server and seven of the eight backends exist only in JavaScript. The
72+
self-hosted compiler — the one in the release — prints to **C and nothing else**.
7073

7174
```bash
7275
npm install -g @digitable-lol/fts

README.ru.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ make # cc -std=c99 -Wall -Wextra -Werror -pedantic -O2
6464
сгенерированный C, Nim возит до сих пор.
6565

6666
**Стоит назвать прямо, что это за бинарник.** Это пять слоёв [`flang/self/`](flang/self): лексер,
67-
парсер, типы, завершаемость, печать в C. Вычислителя среди них нет, поэтому запустить программу,
68-
прогнать её примеры или открыть оболочку можно только полным инструментарием — он ниже.
67+
парсер, типы, завершаемость, печать в C. Вычислителя среди них нет — и `flang repl` здесь
68+
вычисляет единственным способом, каким этот бинарник может: печатает сессию в C, собирает её
69+
системным `cc` против рантайма, поставленного рядом, и запускает. Без `cc` оболочка не
70+
выключается: она по-прежнему проверяет разбор, типы и завершаемость и говорит об этом один раз.
71+
Запустить программу или прогнать её примеры не в оболочке можно только полным
72+
инструментарием — он ниже.
6973

7074
**Полному инструментарию Node.js 20 или новее нужен**, и вот почему именно: интерпретатор,
71-
оболочка, языковой сервер, сервер MCP и семь бэкендов из восьми существуют только на JavaScript.
75+
языковой сервер, сервер MCP и семь бэкендов из восьми существуют только на JavaScript.
7276
Самоприменяющийся компилятор — тот, что лежит в релизе, — печатает **в C и никуда больше**.
7377

7478
```bash

docs/overview.ru.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,13 @@ Node.js для установки не нужен. Такой способ ра
157157
самоприменяющиеся языки: Go долго возил сгенерированный C, Nim возит до сих пор.
158158
Проблема начальной загрузки остаётся у тех, кто развивает сам язык.
159159

160-
Полный инструментарий — восемь бэкендов, интерпретатор, оболочка `flang repl`
161-
и сервер MCP — распространяется через npm и требует Node:
160+
Оболочка `flang repl` есть и здесь: вычислителя в бинарнике нет, поэтому она
161+
печатает сессию в C, собирает её системным `cc` против поставленного рядом
162+
рантайма и запускает; без `cc` она продолжает проверять разбор, типы и
163+
завершаемость.
164+
165+
Полный инструментарий — восемь бэкендов, интерпретатор и сервер MCP —
166+
распространяется через npm и требует Node:
162167

163168
```bash
164169
npm install -g @digitable-lol/fts

flang/self/SPEC.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ AST** и выдаёт **те же диагностики и тот же верд
195195

196196
### `self/emit-c.flang`
197197

198-
Состояние: 328 функций, 190 тотальных, 138 обычных. Напечатанный C совпадает с
198+
Состояние: 329 функций, 191 тотальная, 138 обычных. Напечатанный C совпадает с
199199
эталоном (`src/emit/c.mjs`) **побайтово** на 37 программах `.flang` репозитория
200200
(`flang/stdlib`, `flang/examples`, `flang/core`, включая собственный исходник) и
201201
на 17 моделях `.fts` через `compat.mjs` — вместе с постусловиями, которых в
@@ -273,7 +273,7 @@ AST** и выдаёт **те же диагностики и тот же верд
273273

274274
### `self/parser.flang`
275275

276-
Состояние: 372 функции, 147 тотальных, 225 обычных. AST совпадает с эталоном
276+
Состояние: 373 функции, 147 тотальных, 226 обычных. AST совпадает с эталоном
277277
(`src/parser.mjs`) **побайтово** — то есть `«Печать значения»` от построенного
278278
узла и `JSON.stringify(parse(исходник))` дают одну и ту же строку — на 38
279279
исходниках `.flang` репозитория (`flang/stdlib`, `flang/core`,
@@ -395,7 +395,7 @@ AST означала бы, что из неё можно что-то постр
395395

396396
### `self/types.flang`
397397

398-
Состояние: 276 функций, 148 тотальных, 128 обычных. Множество диагностик
398+
Состояние: 277 функций, 148 тотальных, 129 обычных. Множество диагностик
399399
совпадает с эталоном (`src/types.mjs`) — **код, текст сообщения и место (строка
400400
и столбец), в том же порядке** — на всех 40 файлах `.flang` репозитория
401401
(`flang/stdlib`, `flang/core`, `flang/examples/leetcode`, `flang/self`, включая

flang/self/emit-c.flang

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,8 @@
27622762
«рантайм заголовок»: строка
27632763
«рантайм исходник»: строка
27642764
«исходник прогонщика»: строка
2765+
«оболочка»: признак
2766+
«исходник оболочки»: строка
27652767

27662768
объект «Итог печати»
27672769
«файлы»: список «Файл»
@@ -3180,11 +3182,12 @@
31803182
соединить [(«Без перевода в конце» от «шапка»), "\n\n", («Склеить тела» от «тела»), "\n"] по ""
31813183

31823184
тотальная функция «Печать Makefile»
3183-
принимает «файл»: строка, «прогонщик»: признак
3185+
принимает «файл»: строка, «прогонщик»: признак, «оболочка»: признак
31843186
возвращает строка
31853187
пусть «цель» равно (если «прогонщик» то " flang_cli" иначе "")
3186-
пусть «правило» равно (если «прогонщик» то (соединить ["\nflang_cli: flang_cli.o $(OBJECTS)\n\t$(CC) $(CFLAGS) -o $@ flang_cli.o $(OBJECTS) $(LDLIBS)\n"] по "") иначе "")
3187-
соединить ["# Сгенерировано flang (бэкенд C). Флаги здесь — часть контракта бэкенда:\n", "# сгенерированный код обязан собираться без единого предупреждения.\n", "CC ?= cc\n", "CFLAGS ?= -std=c99 -Wall -Wextra -Werror -pedantic -O2\n", "LDLIBS ?= -lm\n", "\n", "OBJECTS = flang_runtime.o ", «файл», ".o\n", "\n", "all: lib", «файл», ".a", «цель», "\n", "\n", "lib", «файл», ".a: $(OBJECTS)\n", "\tar rcs $@ $(OBJECTS)\n", «правило», "\n", "clean:\n", "\trm -f $(OBJECTS) flang_cli.o flang_cli lib", «файл», ".a\n", "\n", ".PHONY: all clean\n"] по ""
3188+
пусть «объект оболочки» равно (если «оболочка» то " flang_repl.o" иначе "")
3189+
пусть «правило» равно (если «прогонщик» то (соединить ["\nflang_cli: flang_cli.o", «объект оболочки», " $(OBJECTS)\n\t$(CC) $(CFLAGS) -o $@ flang_cli.o", «объект оболочки», " $(OBJECTS) $(LDLIBS)\n"] по "") иначе "")
3190+
соединить ["# Сгенерировано flang (бэкенд C). Флаги здесь — часть контракта бэкенда:\n", "# сгенерированный код обязан собираться без единого предупреждения.\n", "CC ?= cc\n", "CFLAGS ?= -std=c99 -Wall -Wextra -Werror -pedantic -O2\n", "LDLIBS ?= -lm\n", "\n", "OBJECTS = flang_runtime.o ", «файл», ".o\n", "\n", "all: lib", «файл», ".a", «цель», "\n", "\n", "lib", «файл», ".a: $(OBJECTS)\n", "\tar rcs $@ $(OBJECTS)\n", «правило», "\n", "clean:\n", "\trm -f $(OBJECTS) flang_cli.o", «объект оболочки», " flang_cli lib", «файл», ".a\n", "\n", ".PHONY: all clean\n"] по ""
31883191

31893192
// ═══════════════════════════ печать программы ═══════════════════════════
31903193

@@ -3316,8 +3319,11 @@
33163319
пусть «файлы рантайма» равно [(запись «Файл» с «путь» равным "flang_runtime.h" и «содержимое» равным «заголовок рантайма»), (запись «Файл» с «путь» равным "flang_runtime.c" и «содержимое» равным «исходник рантайма»)]
33173320
пусть «файлы модуля» равно [(запись «Файл» с «путь» равным (соединить «файл» с ".h") и «содержимое» равным («Заголовок модуля» от «файл» и «есть модуль» и «модуль» и «общее»)), (запись «Файл» с «путь» равным (соединить «файл» с ".c") и «содержимое» равным («Исходник модуля» от «файл» и «есть модуль» и «модуль» и «общее» и «состояние» и («тела».«части»)))]
33183321
пусть «с модулем» равно («Слить файлы» от «файлы рантайма» и «файлы модуля»)
3322+
// Оболочка живёт только рядом с прогонщиком: звать её больше неоткуда.
3323+
пусть «нужна оболочка» равно (если («настройки».«прогонщик») то («настройки».«оболочка») иначе нет)
33193324
пусть «с прогонщиком» равно (если («настройки».«прогонщик») то («Слить файлы» от «с модулем» и [(«Файл прогонщика» от «есть модуль» и «модуль» и «общее» и «настройки»)]) иначе «с модулем»)
3320-
пусть «все» равно (добавить (запись «Файл» с «путь» равным "Makefile" и «содержимое» равным («Печать Makefile» от «файл» и («настройки».«прогонщик»))) к «с прогонщиком»)
3325+
пусть «с оболочкой» равно (если «нужна оболочка» то («Слить файлы» от «с прогонщиком» и [(«Файл оболочки» от «есть модуль» и «модуль» и «общее» и «настройки»)]) иначе «с прогонщиком»)
3326+
пусть «все» равно (добавить (запись «Файл» с «путь» равным "Makefile" и «содержимое» равным («Печать Makefile» от «файл» и («настройки».«прогонщик») и «нужна оболочка»)) к «с оболочкой»)
33213327
запись «Итог печати» с «файлы» равным «все» и «ошибка» равным («состояние».«ошибка»)
33223328

33233329
тотальная функция «Слить файлы»
@@ -3328,9 +3334,22 @@
33283334
тотальная функция «Файл прогонщика»
33293335
принимает «есть модуль»: признак, «модуль»: строка, «общее»: «Общее», «настройки»: «Настройки»
33303336
возвращает «Файл»
3331-
пусть «содержимое» равно (соединить [(«Шапка файла» от «есть модуль» и «модуль» и "прогонщик: JSON на входе, JSON на выходе"), "\n#define FL_PROGRAM_CALL ", («общее».«префикс»), "_call\n\n", («настройки».«исходник прогонщика»)] по "")
3337+
пусть «просьба» равно (если («настройки».«оболочка») то "#define FL_WITH_REPL 1\n" иначе "")
3338+
пусть «содержимое» равно (соединить [(«Шапка файла» от «есть модуль» и «модуль» и "прогонщик: JSON на входе, JSON на выходе"), "\n#define FL_PROGRAM_CALL ", («общее».«префикс»), "_call\n", «просьба», "\n", («настройки».«исходник прогонщика»)] по "")
33323339
запись «Файл» с «путь» равным "flang_cli.c" и «содержимое» равным «содержимое»
33333340

3341+
// Оболочка печатается рядом с прогонщиком, а не внутри него, и линкуется с ним
3342+
// в один бинарник. Порознь они потому, что обещания у них разные: прогонщик —
3343+
// переносимый C99, который ни от чего не зависит и ничего не спрашивает у мира;
3344+
// оболочка обязана спросить, где `cc` и где каталоги установки. И печатается
3345+
// она по просьбе: осмысленна она ровно у одной программы — у самого компилятора
3346+
// flang, потому что только у него есть точки входа, которые она зовёт.
3347+
тотальная функция «Файл оболочки»
3348+
принимает «есть модуль»: признак, «модуль»: строка, «общее»: «Общее», «настройки»: «Настройки»
3349+
возвращает «Файл»
3350+
пусть «содержимое» равно (соединить [(«Шапка файла» от «есть модуль» и «модуль» и "оболочка: «flang repl» для человека"), "\n#define FL_PROGRAM_CALL ", («общее».«префикс»), "_call\n\n", («настройки».«исходник оболочки»)] по "")
3351+
запись «Файл» с «путь» равным "flang_repl.c" и «содержимое» равным «содержимое»
3352+
33343353
тотальная функция «Блок настроек»
33353354
принимает «настройки»: «Настройки», «общее»: «Общее»
33363355
возвращает строка

0 commit comments

Comments
 (0)