T27.AI

Блог

Терминал для семи бэкендов t27 внутри поста в X

2026-10-04 · 6 мин чтения

[одна спека, hello_world; в браузере работает только JavaScript; нативные результаты получены на одной машине с macOS в один день; в X ничего не опубликовано] В плеере t27 теперь есть терминал: компилятор, работающий в странице как WebAssembly, печатает то, что выдаёт каждый из семи его бэкендов, и запускает тесты спеки. У каждого бэкенда своя страница для ссылок. Каждое слово терминала живёт в спеке. При нативной сборке hello_world в нынешнем виде собирается на двух языках из семи, и пост объясняет почему.

Гравюрный триптих к статье: Терминал для семи бэкендов t27 внутри поста в X
Открыть полный триптих в исходном размере
#t27#Compiler#Terminal

В плеере t27, на странице, которая компилирует спеку прямо внутри поста в X, теперь есть терминал. Наберите t27c gen rust, и компилятор t27, работающий в странице как WebAssembly, напечатает Rust, который он выдаёт для спеки в редакторе. t27c test запускает собственные тесты спеки. У каждого из семи бэкендов компилятора своя страница для ссылок, так что ссылка открывает терминал, который уже набирает команду для этого языка. Никуда ничего не отправляется: каждую строку печатает компилятор в странице или JavaScript, который он выдал.

t27 play · the terminal's own gates

Генератор пересобирает плеер из спеки и проверяет её 14 тестов (43 assert), затем 21 тест прогоняет каждую команду терминала на hello_world: 21 прошёл, 0 упало. 71,3 с в реальности, показано около 13 с; приглашение и набор постановочные, каждый напечатанный байт настоящий. Открыть запись на отдельной странице.

Семь страниц, по одной на бэкенд

Каждая страница лежит по адресу t27.ai/play/hello-world/<backend>/ и имеет свою карточку-превью, так что пост показывает, какой язык открывает ссылка. При автозапуске терминал набирает t27c gen <backend> по 45 мс на символ и выполняет команду. Самая длинная из семи, t27c gen verilog_hir, занимает 20 символов, то есть 0,9 с набора. Генератор отвергает демо длиннее 32 символов и проверяет, что 32 символа в этом темпе укладываются в десять секунд, которые X разрешает чему-либо в карточке-плеере запускаться само (AUTOPLAY_LIMIT_MS в спеке).

БэкендСтраницаФайлЧто делает run
Zig/zig/hello_world.zigпечатает zig test hello_world.zig
C/c/hello_world.cпечатает cc -std=c11 -c hello_world.c
Rust/rust/hello_world.rsпечатает rustc --crate-type lib --edition 2021 hello_world.rs
Verilog/verilog/HelloWorld.vпечатает iverilog -g2012 -o /dev/null HelloWorld.v
Verilog (HIR)/verilog-hir/HelloWorld.hir.vпечатает iverilog -g2012 -o /dev/null HelloWorld.hir.v
JavaScript/js/hello_world.jsимпортирует его в страницу и печатает 7 экспортов; ничего не вызывает
TypeScript/ts/hello_world.tsпечатает tsc --noEmit --strict hello_world.ts

В браузере работает только JavaScript. Для остальных шести бэкендов run говорит, что такого инструмента в странице нет, печатает save <file> и команду для машины, где он есть, и не заявляет никакого результата. Терминал понимает и t27c gen-rust, написание настоящего t27c. На gcc, cargo или yosys он отвечает, что этого инструмента в браузере нет, а не «command not found».

Каждое слово — в спеке

Всё, что терминал говорит или знает, записано константами в specs/x/player.t27:

В JavaScript, который всё это исполняет, public/play/shell.js, нет ни одной собственной английской фразы: он подставляет {names} в шаблоны спеки и выравнивает колонки. Генератор проверяет это в обе стороны. Он отвергает:

pub const BACKENDS : [7]str = ["zig", "c", "rust", "verilog", "verilog_hir", "js", "ts"];
; The backends whose output this page runs itself.
pub const RUNS_IN_BROWSER : [1]str = ["js"];
pub const SAY_WARN_NOT_HERE : str = "{lang} does not run in a browser: this page has no {tool}. On a machine that has it:";

Почему логика пока на JavaScript

Слова уже на t27, а поведение пока нет: разбор строки, подстановка в шаблон, выбор имени файла. Эта часть занимает 361 строку, написанную вручную, и причина измерена. gen-js по замыслу переводит только объявления. Задача, которая его создала, t27#4471, говорит: «The emitted module is data. Nothing in it may run.» Для спеки, описывающей сервер, это правило верно. Для спеки, которая сама является терминалом, оно значит, что поведение должно жить рядом со спекой. t27#6101 просит включаемый по флагу перевод тел функций, при котором сегодняшний вывод не меняется ни на байт. Когда он появится, разбор команд и шаблоны переедут в player.t27, а в shell.js останется только связка с DOM.

Шесть команд мы запустили сами

Терминал, который выдаёт файл и команду, должен знать, что эта команда сделает. Мы запустили каждую на файлах, которые терминал выдаёт для hello_world.t27 в том виде, в каком он лежит в репозитории, на одной машине с macOS:

БэкендКомандаКод выходаПервая ошибка
Zigzig test1hello_world.zig:33:23: error: use of undeclared identifier 'cast'
Ccc -std=c11 -c1hello_world.c:57:22: error: call to undeclared function 'cast'
Rustrustc --crate-type lib1error[E0425]: cannot find function cast in this scope
Verilogiverilog -g20122HelloWorld.v:79: error: No function named cast found in this context
Verilog (HIR)iverilog -g20122HelloWorld.hir.v:15: syntax error (порт config не экранирован)
JavaScriptnode0нет
TypeScripttsc --noEmit --strict0нет

Строка 47 спеки считает площадь как cast(config.width) * cast(config.height), а cast нигде не объявлен. Проверка типов этот вызов пропускает. Собственный вычислитель тестов в странице считает его преобразованием, поэтому в браузере t27c test сообщает 4 pass, 0 fail, 0 skip. Zig, C и Rust ту же строку отвергают. JavaScript и TypeScript проходят только потому, что выдают объявления, а не саму функцию. Если переписать строку как (config.width as u16) * (config.height as u16), проходят шесть из семи: zig test сообщает «All 2 tests passed», а HIR-Verilog по-прежнему падает на порту config. Обе проблемы заведены, это t27#6100 и t27#5966. Терминал этого не прячет: он печатает команду и никогда не заявляет её результат.

Страница, которую git потерял бы

Репозиторий игнорирует любой каталог с именем zig/: это место для скачанных наборов Zig. Под это же правило попал и public/play/hello-world/zig/. Сборка бы прошла, страница существовала бы на машине, которая её собрала, а опубликованный сайт отвечал бы 404 на ссылку для Zig. Это исправляет одна строка-исключение в .gitignore. Теперь тест спрашивает git check-ignore о каждой странице бэкенда, с контрольным путём под тем же правилом, который по-прежнему обязан игнорироваться.

Прежде чем это попадёт в X

Карточкам-плеерам X запрещено собирать ввод данных. Терминал принимает набранные команды, хотя ничего не покидает страницу. Публиковать ли страницу терминала как карточку-плеер или только как ссылку, решает владелец. Ничего не опубликовано.

Попробовать

Чего это не решает

Пруфы

Поработаем вместе

Нужно довести FPGA/RTL-задачу до замеров на железе?

Работаю по контракту и part-time с hardware-AI, FPGA/RTL и ML-системами — от спецификации и открытого тулчейна до воспроизводимых измерений.