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

В плеере t27, на странице, которая компилирует спеку прямо внутри поста в X, теперь есть терминал. Наберите t27c gen rust, и компилятор t27, работающий в странице как WebAssembly, напечатает Rust, который он выдаёт для спеки в редакторе. t27c test запускает собственные тесты спеки. У каждого из семи бэкендов компилятора своя страница для ссылок, так что ссылка открывает терминал, который уже набирает команду для этого языка. Никуда ничего не отправляется: каждую строку печатает компилятор в странице или JavaScript, который он выдал.
t27 play · the terminal's own gates
Каждая страница лежит по адресу 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} в шаблоны спеки и выравнивает колонки. Генератор проверяет это в обе стороны. Он отвергает:
SAY_, которую терминал никогда не произносит, или фразу, которую он произносит, а спека не определяет;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:";
Слова уже на 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:
| Бэкенд | Команда | Код выхода | Первая ошибка |
|---|---|---|---|
| Zig | zig test | 1 | hello_world.zig:33:23: error: use of undeclared identifier 'cast' |
| C | cc -std=c11 -c | 1 | hello_world.c:57:22: error: call to undeclared function 'cast' |
| Rust | rustc --crate-type lib | 1 | error[E0425]: cannot find function cast in this scope |
| Verilog | iverilog -g2012 | 2 | HelloWorld.v:79: error: No function named cast found in this context |
| Verilog (HIR) | iverilog -g2012 | 2 | HelloWorld.hir.v:15: syntax error (порт config не экранирован) |
| JavaScript | node | 0 | нет |
| TypeScript | tsc --noEmit --strict | 0 | нет |
Строка 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. Терминал этого не прячет: он печатает команду и никогда не заявляет её результат.
Репозиторий игнорирует любой каталог с именем zig/: это место для скачанных наборов Zig. Под это же правило попал и public/play/hello-world/zig/. Сборка бы прошла, страница существовала бы на машине, которая её собрала, а опубликованный сайт отвечал бы 404 на ссылку для Zig. Это исправляет одна строка-исключение в .gitignore. Теперь тест спрашивает git check-ignore о каждой странице бэкенда, с контрольным путём под тем же правилом, который по-прежнему обязан игнорироваться.
Карточкам-плеерам X запрещено собирать ввод данных. Терминал принимает набранные команды, хотя ничего не покидает страницу. Публиковать ли страницу терминала как карточку-плеер или только как ссылку, решает владелец. Ничего не опубликовано.
/zig/, /c/, /verilog/, /verilog-hir/, /js/, /ts/)help, t27c test, t27c gen zig, run js, run rust, t27c checkПоработаем вместе
Работаю по контракту и part-time с hardware-AI, FPGA/RTL и ML-системами — от спецификации и открытого тулчейна до воспроизводимых измерений.