Ладно, DOOM на TS типах
TL;DR
- DOOM на типах TS работает через WASM, а не интерпретируемый TS
- 3,5 триллиона строк типов для 320×200 ASCII-пикселей
- Стек использует infer для извлечения значений из типов
- Числа представлены как бинарные строки для битовых операций
Ладно, DOOM на TS типах
Как? И наху... Ну нас вообще только первый вопрос интересует
Для начала развеем чуть кликбейтный на мой взгляд заголовок - работает все на WASM. То есть это не чистый ts где ты написал
Зато графическая часть тут тоже на типах. Это ASCII графика, где каждый из 320х200 фреймов (пикселей) представлен в виде объекта
Да, это огроменное количество кода - 3,5 ТРИЛЛИОНА строк типов. Подозреваю что чувак взял вариацию дума на ассемблере и перегнал все инструкции в типы. Конечный код с самим думом я не нашел, но выложили сам рантайм TS в WASM, на него и будем смотреть
Что такое WASM? Это способ исполнить низкоуровневые инструкции в браузере. Соответственно нужно придумать как эти инструкции описать с помощью тайпскриптовых типов и как их потом исполнять (за это тут Rust файлик отвечает, но там ниче интересного)
Есть интерфейсик, который описывает че вообще надо исполнить, выглядит максимально похоже для всех инструкций
Это мы описали что у нас в принципе есть какая то операция, теперь зацените как она реализована
Дальше там тернарник на проверку разраядности числа (32 или 64) и сама проверка, которую вынесли в либу. Расписывать ее не буду, но принцип основан на таком же трюке с
Таких штук там дофигища, все в один пост не влезет, пока это переварите
Как? И наху... Ну нас вообще только первый вопрос интересует
Для начала развеем чуть кликбейтный на мой взгляд заголовок - работает все на WASM. То есть это не чистый ts где ты написал
node ./index.ts и вот у тебя уже дум готовый, нифига. TS тут выступает как декларативный язык, описывающий инструкции (сложение, деление, сдвиги побитовые), прям как вы колонки таблицы в каком нибудь Ant design описываетеЗато графическая часть тут тоже на типах. Это ASCII графика, где каждый из 320х200 фреймов (пикселей) представлен в виде объекта
Да, это огроменное количество кода - 3,5 ТРИЛЛИОНА строк типов. Подозреваю что чувак взял вариацию дума на ассемблере и перегнал все инструкции в типы. Конечный код с самим думом я не нашел, но выложили сам рантайм TS в WASM, на него и будем смотреть
Что такое WASM? Это способ исполнить низкоуровневые инструкции в браузере. Соответственно нужно придумать как эти инструкции описать с помощью тайпскриптовых типов и как их потом исполнять (за это тут Rust файлик отвечает, но там ниче интересного)
Есть интерфейсик, который описывает че вообще надо исполнить, выглядит максимально похоже для всех инструкций
export type IGreaterThanOrEqual = {
kind: "GreaterThanOrEqual"
type: WasmType
signed: boolean
}Это мы описали что у нас в принципе есть какая то операция, теперь зацените как она реализована
export type GreaterThanOrEqual<
instruction extends IGreaterThanOrEqual,
state extends ProgramState
> = Satisfies<ProgramState,
state['stack'] extends [
...infer remaining extends WasmValue[],
infer a extends WasmValue,
infer b extends WasmValue,
]
? State.Stack.set<
[
...remaining,
instruction['type'] extends 'i32'
? instruction['signed'] extends true
? Wasm.I32GeS<a, b>
: Wasm.I32GeU<a, b>
: instruction['type'] extends 'i64'
? instruction['signed'] extends true
? Wasm.I64GeS<a, b>
: Wasm.I64GeU<a, b>
: never
],
state
>
: State.error<"stack exhausted", instruction, state>
>stack хранит аргументы для инструкций, нам надо взять два верхних и мы это делаем ебически прекрасным образом...infer remaining extends WasmValue[],
infer a extends WasmValue,
infer b extends WasmValue,
infer позволяет создать новую "переменную" для типа в тернарных условиях. Этим воспользовались чтобы взять два последних элемента из стека a и b. А еще он умеет в спред, чего я не знал. Уже тут я словил технический экстаз - рабочая структура данных на чистых типах!Дальше там тернарник на проверку разраядности числа (32 или 64) и сама проверка, которую вынесли в либу. Расписывать ее не буду, но принцип основан на таком же трюке с
infer но уже для строки (числа представлены в двоичной системе строками, да). Если a 1, а b 0 то true и наоборот. Короче просто сложный тернарникТаких штук там дофигища, все в один пост не влезет, пока это переварите

Хотите больше таких постов?
Подпишитесь на канал и читайте продолжение в Telegram.