tsclang 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONCEPT.md ADDED
@@ -0,0 +1,3517 @@
1
+ # TypeScript C (tsc) — Language Design Concept
2
+
3
+ ## Overview
4
+
5
+ A TypeScript-inspired language that compiles to C and auto-generates build files (CMakeLists.txt).
6
+
7
+ - File extension: `.tsc`
8
+ - CLI: `tsclang`
9
+ - Output: `.c` / `.h` files + `CMakeLists.txt`
10
+
11
+ ---
12
+
13
+ ## Установка
14
+
15
+ ### Требования
16
+
17
+ - Node.js `>=18.0.0`
18
+ - npm `>=9.0.0`
19
+ - CMake `>=3.16` (для emit: binary / hex)
20
+ - Компилятор C: gcc, clang, или avr-gcc (для AVR targets)
21
+
22
+ ### Установка CLI
23
+
24
+ ```bash
25
+ # Глобальная установка (рекомендуется)
26
+ npm install -g tsclang
27
+
28
+ # Обновление
29
+ npm update -g tsclang
30
+
31
+ # Проверка установки
32
+ tsclang --version
33
+
34
+ # Запуск без установки
35
+ npx tsclang build
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Core Goals
41
+
42
+ - [ ] TypeScript-like syntax and type system
43
+ - [ ] Compiles to readable, idiomatic C
44
+ - [ ] Auto-generates CMakeLists.txt
45
+ - [ ] Dependency/library management
46
+
47
+ ---
48
+
49
+ ## Design Decisions
50
+
51
+ ### Syntax
52
+
53
+ #### Переменные
54
+
55
+ - `let` — мутабельная переменная: можно переприсвоить, можно вызывать `mut` методы, можно передавать как `Mut<T>`
56
+ - `const` — иммутабельная: нельзя переприсвоить, нельзя вызывать `mut` методы, нельзя передавать как `Mut<T>`
57
+
58
+ #### Перегрузка функций
59
+
60
+ Перегрузка по типам и по количеству параметров. Компилятор выбирает нужную версию на callsite, в C генерирует функции с mangled именами:
61
+
62
+ ```typescript
63
+ // по типам
64
+ function process(x: i32): void { ... } // → process_i32 в C
65
+ function process(x: string): void { ... } // → process_string в C
66
+
67
+ process(42); // вызывает process_i32
68
+ process("hello"); // вызывает process_string
69
+
70
+ // по количеству параметров
71
+ function foo(x: i32): void { ... } // → foo_i32 в C
72
+ function foo(x: i32, y: i32): void { ... } // → foo_i32_i32 в C
73
+
74
+ foo(1); // вызывает foo_i32
75
+ foo(1, 2); // вызывает foo_i32_i32
76
+
77
+ // комбинация
78
+ function add(a: i32, b: i32): i32 { ... } // → add_i32_i32
79
+ function add(a: f64, b: f64): f64 { ... } // → add_f64_f64
80
+ function add(a: string, b: string): string { ... } // → add_string_string
81
+ ```
82
+
83
+ Перегрузка работает и для методов класса:
84
+ ```typescript
85
+ class Printer {
86
+ print(x: i32): void { ... }
87
+ print(x: string): void { ... }
88
+ }
89
+ ```
90
+
91
+ #### Ограничение: extern "C" запрещает перегрузку
92
+
93
+ `extern "C"` функции имеют фиксированное C-имя — манглинг невозможен. Перегрузка — ошибка компилятора:
94
+
95
+ ```typescript
96
+ // ❌ импорт из C — линковщик не найдёт mangled имена
97
+ extern "C" function SDL_SetWindowSize(w: any, width: i32, height: i32): void { ... }
98
+ extern "C" function SDL_SetWindowSize(w: any, size: i32): void { ... }
99
+ // ошибка: extern "C" функции не могут быть перегружены
100
+
101
+ // ❌ экспорт в C — C-код ищет символ "process", а не "process_string"
102
+ export extern "C" function process(data: string): void { ... }
103
+ export extern "C" function process(data: i32): void { ... }
104
+ // ошибка: extern "C" функции не могут быть перегружены
105
+
106
+ // ✅ правильно — разные имена для C, обёртка с перегрузкой внутри TSC
107
+ extern "C" function SDL_SetWindowSize(w: any, width: i32, height: i32): void { ... }
108
+
109
+ export extern "C" function process_str(data: string): void { ... }
110
+ export extern "C" function process_int(data: i32): void { ... }
111
+
112
+ // внутренняя перегрузка — ok
113
+ function process(data: string): void { process_str(data); }
114
+ function process(data: i32): void { process_int(data); }
115
+ ```
116
+
117
+ #### Дефолтные параметры
118
+
119
+ Работают для функций, методов и конструкторов. На callsite компилятор подставляет дефолтное значение:
120
+
121
+ ```typescript
122
+ function greet(name: string, greeting: string = "Hello"): string {
123
+ return `${greeting}, ${name}!`;
124
+ }
125
+
126
+ greet("Alice"); // "Hello, Alice!"
127
+ greet("Alice", "Hi"); // "Hi, Alice!"
128
+
129
+ // методы
130
+ class Printer {
131
+ print(text: string, times: i32 = 1): void { ... }
132
+ }
133
+
134
+ printer.print("hi"); // times=1
135
+ printer.print("hi", 3); // times=3
136
+ ```
137
+
138
+ - Дефолтные параметры должны быть в конце списка
139
+ - Дефолтное значение — константа или литерал, не выражение с побочными эффектами
140
+ - Запрещено иметь overload, сигнатура которого совпадает с вызовом другого overload при подстановке дефолтных значений — ошибка компилятора:
141
+
142
+ ```typescript
143
+ function foo(x: i32, y: i32 = 0): void { ... }
144
+ function foo(x: i32): void { ... }
145
+ // ошибка: ambiguous overload — foo(x: i32) совпадает с foo(x: i32, y: i32 = 0) при y=0
146
+ ```
147
+
148
+ #### Функции
149
+
150
+ - Ключевое слово: `function`
151
+ ```typescript
152
+ function add(a: i32, b: i32): i32 {
153
+ return a + b;
154
+ }
155
+ ```
156
+ - **Стрелочные функции** — сокращённый синтаксис, тип выводится:
157
+ ```typescript
158
+ const add = (a: i32, b: i32): i32 => a + b; // expression body
159
+ const add = (a: i32, b: i32): i32 => {
160
+ return a + b;
161
+ }; // block body
162
+ ```
163
+ - **Анонимные функции** — `function` без имени, присваивается переменной или передаётся аргументом:
164
+
165
+ ```typescript
166
+ const add = function (a: i32, b: i32): i32 {
167
+ return a + b;
168
+ };
169
+
170
+ array.sort(function (a: i32, b: i32): i32 {
171
+ return a - b;
172
+ });
173
+ ```
174
+
175
+ - **IIFE** — немедленный вызов функции:
176
+ ```typescript
177
+ // стрелочная функция
178
+ ((a: i32, b: i32) => a + b)(1, 2); // => 3
179
+
180
+ // блочное тело
181
+ ((a: i32, b: i32): i32 => {
182
+ return a + b;
183
+ })(1, 2); // => 3
184
+
185
+ // анонимная функция
186
+ (function (a: i32, b: i32): i32 {
187
+ return a + b;
188
+ })(1, 2); // => 3
189
+ ```
190
+ - **Замыкания** — стрелочные функции захватывают переменные из внешнего скопа:
191
+ ```typescript
192
+ let multiplier = 3;
193
+ const triple = (x: i32) => x * multiplier; // захватывает multiplier
194
+ ```
195
+ - Захват **по значению** для примитивов (копируется в момент создания замыкания); `T | null` где T — примитив, тоже захватывается по значению (copy), несмотря на struct-представление в C:
196
+ ```typescript
197
+ let x: i32 | null = 5;
198
+ const fn = () => console.log(x);
199
+ x = null;
200
+ fn(); // 5 — захвачена копия на момент создания
201
+ ```
202
+ - Захват **по ссылке** для сложных типов (следует правилам borrow checker) — по умолчанию
203
+ - Явный список захвата — те же типы что везде: `T`, `Ref<T>`, `Mut<T>`, `Shared<T>`:
204
+ ```typescript
205
+ const fn = [data: Data]() => process(data); // T — move (Owner)
206
+ const fn = [data: Ref<Data>]() => data.length; // Ref — immutable borrow
207
+ const fn = [data: Mut<Data>]() => { data.push(1); }; // Mut — mutable borrow
208
+ ```
209
+ - Список захвата нужен когда компилятор не может вывести тип или нужен move
210
+ - В C компилируется в struct с захваченными переменными + функцию принимающую этот struct
211
+
212
+ #### Семантика передачи значений
213
+
214
+ - **Примитивы** (`i8`..`i64`, `u8`..`u64`, `f32`, `f64`, `boolean`) — всегда **по значению** (copy)
215
+ - **Сложные типы** (объекты, массивы, коллекции, структуры, классы, строки) — управляются ownership системой
216
+ - Явные `&` аннотации не нужны для базовых случаев — компилятор решает сам
217
+
218
+ ### Type System
219
+
220
+ #### Generics
221
+
222
+ - **Монорфизация** — компилятор генерирует отдельный код для каждого конкретного типа:
223
+ - `identity<i32>` → `identity_i32` в C
224
+ - `identity<User>` → `identity_User` в C
225
+ - **Синтаксис** — TypeScript-стиль `<T>`:
226
+ ```typescript
227
+ function identity<T>(x: T): T { return x; }
228
+ function map<T, U>(arr: Ref<T[]>, f: (x: Ref<T>) => U): U[] { ... }
229
+
230
+ class Stack<T> {
231
+ items: T[];
232
+ mut push(item: T): void { ... }
233
+ mut pop(): T { ... }
234
+ }
235
+ ```
236
+ - **Bounds — нет**, проверка при инстанцировании. Правила ownership применяются в момент подстановки конкретного типа:
237
+ ```typescript
238
+ first<i32>(arr); // ok — примитив, копируется
239
+ first<User>(arr); // ошибка в точке вызова: User — сложный тип, нельзя вернуть T из Ref<T[]>
240
+ ```
241
+ - **Ownership с generics** — `Ref<T>`, `Mut<T>`, `Shared<T>`, `Weak<T>` работают как обычно:
242
+ ```typescript
243
+ function first<T>(arr: Ref<T[]>): Ref<T> { ... } // borrow элемента
244
+ function pop<T>(arr: Mut<T[]>): T { ... } // move с удалением
245
+ function process<T>(graph: Shared<T>) { ... } // ARC
246
+ ```
247
+
248
+ #### Типизация
249
+
250
+ - **Номинальная** — тип определяется именем, не формой
251
+ - `type Point = { x: f64, y: f64 }` и `type Vector = { x: f64, y: f64 }` — разные типы
252
+ - **Type inference** — тип выводится если не указан явно
253
+ - `const p = { x: 1, y: 0 }` → `{ x: f64, y: f64 }` → анонимная struct в C
254
+ - **Автокаст числовых типов:**
255
+ - Widening **без потерь** — неявно, молча:
256
+ - `i8`/`i16`/`i32` → любой больший int (`i64`)
257
+ - `u8`/`u16`/`u32` → любой больший uint (`u64`)
258
+ - `i32` → `f64`, `u32` → `f64`, `f32` → `f64`
259
+ - Widening **с потерей точности** — требует явный `as`:
260
+ - `i32` → `f32`, `i64` → `f32`, `i64` → `f64`, `u64` → `f64`
261
+ - Narrowing (f64→i32 и т.д.) — всегда требует `as`
262
+ - **Оператор `as`** — явное приведение типа, три случая:
263
+ ```typescript
264
+ // 1. Числовые типы — C-cast, может быть lossy
265
+ 3.14 as i32 // (int32_t)3.14 в C → 3
266
+ 1000 as i8 // переполнение — поведение как в C (implementation-defined)
267
+
268
+ // 2. Non-null assertion — убрать null из типа без проверки
269
+ let x: i32 | null = getValue();
270
+ let y = x as i32; // runtime error если x == null
271
+ // лучше использовать if (x != null) для безопасности
272
+
273
+ // 3. any — явный cast когда тип неизвестен
274
+ let val: any = getFromC();
275
+ let s = val as string;
276
+ ```
277
+ - **`as` НЕ работает для:**
278
+ - ownership типов: `user as Ref<User>` — ошибка компилятора
279
+ - конвертации строк: `42 as string` — ошибка, используй `.toString()`
280
+
281
+ - **`as` для struct/interface** — структурная совместимость проверяется компилятором:
282
+ ```typescript
283
+ interface Point { x: f64; y: f64; }
284
+
285
+ let p = { x: 1.0, y: 2.0 }; // анонимная struct
286
+ foo(p as Point); // ok — поля совпадают
287
+
288
+ let q = { x: 1.0, z: 2.0 };
289
+ foo(q as Point); // ошибка: поле 'z' не совпадает с 'y'
290
+
291
+ // лучше — явная аннотация сразу:
292
+ let p: Point = { x: 1.0, y: 2.0 };
293
+ foo(p); // ok, без as
294
+ ```
295
+ - **Объектные литералы** без типа → анонимная struct, генерируется компилятором
296
+ - **Пустой объектный литерал `{}`** — ошибка компилятора: struct без полей бессмысленна в TSC, память под поля не выделяется динамически:
297
+ ```typescript
298
+ let obj = {}; // ошибка: пустой объектный литерал запрещён
299
+ // hint: используй Map<K, V> для динамических ключей
300
+ // или объяви тип: let obj: { field: T } = { ... }
301
+ let obj = {};
302
+ obj.a = 1; // невозможно — struct фиксирована на этапе компиляции
303
+
304
+ // правильно — динамические ключи:
305
+ let obj = new Map<string, i32>();
306
+ obj.set("a", 1);
307
+
308
+ // правильно — фиксированная struct:
309
+ let obj = { a: 1, b: 2 }; // { a: i32, b: i32 } известна компилятору
310
+ obj.a = 5; // ok
311
+ ```
312
+
313
+ #### Числовые типы
314
+
315
+ - Полный набор: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `f32`, `f64`
316
+ - Синоним: `number` = `f64` по умолчанию (совместимость с TypeScript-стилем)
317
+ - Переопределяется через `"defaultNumber"` в `tsc.packages.json`
318
+ - На 8-bit таргетах (`"target": "avr"` и др.) — warning если встречается `f64`
319
+ ```json
320
+ // tsc.packages.json — AVR
321
+ { "target": "avr", "mcu": "atmega328p", "defaultNumber": "f32" }
322
+ ```
323
+ ```typescript
324
+ // Десктоп (defaultNumber = f64)
325
+ const a = 1; // f64
326
+ const b: number = 1; // f64
327
+ const c: f32 = 1; // f32 (явно)
328
+
329
+ // AVR (defaultNumber = f32)
330
+ const a = 1; // f32
331
+ const b: number = 1; // f32
332
+ const c: f32 = 1; // f32 (явно)
333
+ const d: f64 = 1; // f64 + warning: f64 on 8-bit target is inefficient
334
+ ```
335
+ - Type inference выводит конкретный тип для всех значений:
336
+ - числа → `number` (= `f64` или переопределённый тип)
337
+ - строки → `string`, булевые → `boolean`, массивы → `number[]` и т.д.
338
+ - явная аннотация переопределяет: `const i: i32 = 1` → `i32`
339
+ - Сообщения об ошибках используют конкретный тип: `expected f64, got i32`
340
+ - Все числа — примитивы, передаются по значению
341
+
342
+ #### Конвертация типов
343
+
344
+ ##### Число → строка
345
+
346
+ Три способа:
347
+
348
+ ```typescript
349
+ const age: i32 = 30;
350
+ const pi: f64 = 3.14159;
351
+
352
+ // 1. .toString() — явный метод на любом числовом типе
353
+ const s1 = age.toString(); // "30"
354
+ const s2 = pi.toString(); // "3.14159"
355
+
356
+ // 2. Template literal — автоматически
357
+ const s3 = `Age: ${age}`; // "Age: 30"
358
+ const s4 = `Pi = ${pi}`; // "Pi = 3.14159"
359
+
360
+ // 3. Конкатенация со строкой
361
+ const s5 = "Age: " + age; // "Age: 30"
362
+
363
+ // as — НЕ работает для конвертации в строку:
364
+ const bad = age as string; // ошибка компилятора
365
+ ```
366
+
367
+ ##### Строка → число
368
+
369
+ Явный парсинг — возвращает результат или ошибку:
370
+
371
+ ```typescript
372
+ // parse — бросает ParseError если строка не число
373
+ const age = i32.parse("30"); // i32
374
+ const pi = f64.parse("3.14"); // f64
375
+ const bad = i32.parse("abc"); // throws ParseError
376
+
377
+ // tryParse — возвращает T | null, без throws
378
+ const age = i32.tryParse("30"); // 30
379
+ const bad = i32.tryParse("abc"); // null
380
+
381
+ // использование с обработкой ошибок:
382
+ function getAge(raw: string): i32 throws ParseError {
383
+ return i32.parse(raw)?; // propagate ParseError
384
+ }
385
+
386
+ // использование с дефолтом:
387
+ const age = i32.tryParse(raw) ?? 0; // 0 если не распарсилось
388
+
389
+ // as — НЕ работает для парсинга строк:
390
+ const bad = "30" as i32; // ошибка компилятора: используй i32.parse()
391
+ ```
392
+
393
+ Доступно для всех числовых типов: `i8.parse`, `i16.parse`, `i32.parse`, `i64.parse`, `u8.parse`, ..., `f32.parse`, `f64.parse`.
394
+
395
+ ##### JS-совместимые глобальные функции
396
+
397
+ Синонимы для привычного JS-синтаксиса:
398
+
399
+ ```typescript
400
+ // parseFloat(a) — синоним f64.tryParse(a) → f64 | null
401
+ parseFloat("3.14") // 3.14
402
+ parseFloat("abc") // null
403
+
404
+ // parseInt(a) — парсит как f64, затем обрезает дробную часть → i64 | null
405
+ parseInt("3.14") // 3
406
+ parseInt("42") // 42
407
+ parseInt("abc") // null
408
+ parseInt("-7.9") // -7 (truncate, не floor: к нулю)
409
+
410
+ // Number(a) — синоним parseFloat(a) → f64 | null
411
+ Number("3.14") // 3.14
412
+ Number("abc") // null
413
+
414
+ // String(a) — синоним a.toString() → string (всегда успешно)
415
+ String(42) // "42"
416
+ String(3.14) // "3.14"
417
+ String(true) // "true"
418
+ String(null) // "null"
419
+ ```
420
+
421
+ Отличия от JS: `parseInt`/`parseFloat`/`Number` возвращают `T | null` вместо `NaN` — в TSC нет `NaN`.
422
+
423
+ #### Строки
424
+
425
+ - Один тип `string` — heap, всегда передаётся по ссылке (сложный тип)
426
+ - Мутабельность через `let`/`const`
427
+ - Кодировка: **UTF-8** внутри
428
+ - Индексация: **по графемным кластерам** (user-perceived characters)
429
+ - `"0❤️abcАБВ"[1]` → `❤️` (два codepoint-а, один кластер)
430
+ - `s[i]` возвращает `string` (не `char` — тип `char` отсутствует)
431
+ - `s[i]` — O(n), строится обход графем
432
+ - Для сегментации графем — **utf8proc** (UAX #29, ~300KB, C-native, поддерживает все Unicode скрипты включая кириллицу, emoji, Devanagari; работает на embedded; locale-aware операции — отдельный пакет)
433
+ - Срезы: `s[1..3]` → подстрока по графемным индексам
434
+
435
+ #### Специальные типы
436
+
437
+ | Тип TSC | Тип C | Описание |
438
+ |---------|-------|----------|
439
+ | `void` | `void` | отсутствие значения — только для возвращаемого типа функции |
440
+ | `any` | `void*` | неизвестный тип — borrow checker не применяется |
441
+
442
+ ```typescript
443
+ function log(msg: string): void { ... } // void — нет return value
444
+
445
+ function getFromC(): any { ... } // void* в C
446
+ let val: any = getFromC();
447
+ let s = val as string; // явный cast обязателен
448
+ ```
449
+
450
+ - `void` нельзя использовать как тип переменной — только возвращаемый тип
451
+ - `any` = `void*` в C, **неявно nullable** — `void*` может быть `NULL`; писать `any | null` избыточно и запрещено (ошибка компилятора)
452
+ - `any` отключает borrow checker — **управление памятью ручное**, утечки на совести разработчика; использовать только на границах C interop
453
+
454
+ ```typescript
455
+ // void + throws — Result без value-поля в C
456
+ function connect(): void throws IOError { ... }
457
+ // → typedef struct { bool ok; IOError error; } _Result_void_IOError;
458
+
459
+ connect()?; // ok — propagate
460
+ connect()!; // ok — panic on error
461
+ ```
462
+
463
+ #### Null
464
+
465
+ - `null` — единственное "отсутствующее значение"
466
+ - `undefined` **отсутствует** — в отличие от JS, нет разделения на `null` и `undefined`
467
+ - `NaN` **отсутствует** — функции парсинга возвращают `T | null` вместо `NaN`; деление на ноль для целых → runtime panic, для float → поведение как в C (`Infinity`, `-Infinity` через IEEE 754, но не `NaN` как значение типа)
468
+
469
+ #### Операторы
470
+
471
+ ##### Арифметические
472
+
473
+ | Оператор | Описание |
474
+ |----------|----------|
475
+ | `+` | сложение; для `string` — конкатенация |
476
+ | `-` | вычитание |
477
+ | `*` | умножение |
478
+ | `/` | деление |
479
+ | `%` | остаток от деления |
480
+ | `**` | возведение в степень |
481
+ | `++` | инкремент (prefix / postfix) |
482
+ | `--` | декремент (prefix / postfix) |
483
+
484
+ ##### Присваивание
485
+
486
+ | Оператор | Эквивалент |
487
+ |----------|------------|
488
+ | `=` | присваивание |
489
+ | `+=` | `a = a + b` |
490
+ | `-=` | `a = a - b` |
491
+ | `*=` | `a = a * b` |
492
+ | `/=` | `a = a / b` |
493
+ | `%=` | `a = a % b` |
494
+ | `**=` | `a = a ** b` |
495
+ | `&=` | `a = a & b` |
496
+ | `\|=` | `a = a \| b` |
497
+ | `^=` | `a = a ^ b` |
498
+ | `<<=` | `a = a << b` |
499
+ | `>>=` | `a = a >> b` |
500
+ | `>>>=` | `a = a >>> b` |
501
+ | `&&=` | `a = a && b` |
502
+ | `\|\|=` | `a = a \|\| b` |
503
+ | `??=` | `a = a ?? b` |
504
+
505
+ ##### Сравнения
506
+
507
+ | Оператор | Описание |
508
+ |----------|----------|
509
+ | `==` | равенство (в TSC нет type coercion — идентично `===`) |
510
+ | `!=` | неравенство (идентично `!==`) |
511
+ | `===` | строгое равенство |
512
+ | `!==` | строгое неравенство |
513
+ | `<` | меньше |
514
+ | `>` | больше |
515
+ | `<=` | меньше или равно |
516
+ | `>=` | больше или равно |
517
+
518
+ > В TSC нет неявного приведения типов, поэтому `==` и `===` ведут себя одинаково. Рекомендуется `===` для ясности.
519
+
520
+ ##### Логические
521
+
522
+ | Оператор | Описание |
523
+ |----------|----------|
524
+ | `&&` | возвращает первый falsy операнд, или последний если все truthy |
525
+ | `\|\|` | возвращает первый truthy операнд, или последний если все falsy |
526
+ | `!` | логическое НЕ, возвращает `boolean` |
527
+ | `??` | возвращает правый операнд если левый `null` (не реагирует на `0`, `""`, `false`) |
528
+
529
+ Поведение `||` и `&&` аналогично JS — возвращают **сам операнд**, не `boolean`:
530
+
531
+ ```typescript
532
+ const name = user.name || "Anonymous" // string — "Anonymous" если name == ""
533
+ const port = config.port || 8080 // i32 — 8080 если port == 0
534
+ const value = a && b && c // тип c — c если все truthy, иначе первый falsy
535
+
536
+ // ?? vs || — разница:
537
+ const x = value ?? "default" // "default" только если value == null
538
+ const y = value || "default" // "default" если value == null, "", 0, false
539
+ ```
540
+
541
+ Тип результата `||` и `&&` — компилятор выводит из операндов (должны быть совместимых типов).
542
+
543
+ ##### Битовые
544
+
545
+ | Оператор | Описание |
546
+ |----------|----------|
547
+ | `&` | побитовое И |
548
+ | `\|` | побитовое ИЛИ |
549
+ | `^` | побитовое XOR |
550
+ | `~` | побитовое НЕ |
551
+ | `<<` | сдвиг влево |
552
+ | `>>` | сдвиг вправо (знаковый) |
553
+ | `>>>` | сдвиг вправо (беззнаковый) |
554
+
555
+ ##### Прочие
556
+
557
+ | Оператор | Описание |
558
+ |----------|----------|
559
+ | `? :` | тернарный оператор |
560
+ | `?.` | optional chaining — обращение к полю/методу если не `null` |
561
+ | `...` | spread |
562
+
563
+ ##### Приоритет операторов
564
+
565
+ От высшего к низшему. Операторы на одном уровне — левоассоциативны, если не указано иное.
566
+
567
+ | Приоритет | Оператор(ы) | Ассоциативность |
568
+ |-----------|-------------|-----------------|
569
+ | 18 | `()` группировка | — |
570
+ | 17 | `.` `?.` `[]` вызов `()` | левая |
571
+ | 16 | `++` `--` (postfix) | — |
572
+ | 15 | `!` `~` `+` `-` (unary) `++` `--` (prefix) | правая |
573
+ | 14 | `**` | правая |
574
+ | 13 | `*` `/` `%` | левая |
575
+ | 12 | `+` `-` | левая |
576
+ | 11 | `<<` `>>` `>>>` | левая |
577
+ | 10 | `<` `<=` `>` `>=` | левая |
578
+ | 9 | `==` `!=` `===` `!==` | левая |
579
+ | 8 | `&` | левая |
580
+ | 7 | `^` | левая |
581
+ | 6 | `\|` | левая |
582
+ | 5 | `&&` | левая |
583
+ | 4 | `\|\|` `??` | левая |
584
+ | 3 | `? :` | правая |
585
+ | 2 | `=` `+=` `-=` `*=` `/=` `%=` `**=` `&=` `\|=` `^=` `<<=` `>>=` `>>>=` `&&=` `\|\|=` `??=` | правая |
586
+
587
+ > `??` нельзя смешивать с `||` или `&&` без явных скобок — ошибка компилятора:
588
+ > ```typescript
589
+ > a || b ?? c // error: смешивание || и ?? требует скобок
590
+ > (a || b) ?? c // ok
591
+ > a || (b ?? c) // ok
592
+ > ```
593
+
594
+ #### Truthy / Falsy
595
+
596
+ Как в JS, без `undefined` и `NaN`:
597
+
598
+ | Тип | Falsy | Truthy |
599
+ |-----|-------|--------|
600
+ | `boolean` | `false` | `true` |
601
+ | числовые (`i8`..`f64`) | `0` | любое ненулевое |
602
+ | `string` | `""` (пустая строка) | любая непустая |
603
+ | `T | null` (сложный тип) | `null` | не null |
604
+ | `T | null` (примитив) | `null` или falsy значение | не null и truthy |
605
+ | class / struct | никогда (всегда truthy) | всегда |
606
+ | array / Set / Map | никогда (всегда truthy, даже пустые) | всегда |
607
+
608
+ ```typescript
609
+ if ("") { } // falsy
610
+ if ("hi") { } // truthy
611
+ if (0) { } // falsy
612
+ if (42) { } // truthy
613
+ if (null) { } // falsy
614
+
615
+ // string | null — truthy если не null И не ""
616
+ let s: string | null = getValue();
617
+ if (s) {
618
+ // s: string (не null и не пустая)
619
+ }
620
+
621
+ // i32 | null — truthy если не null И не 0
622
+ let n: i32 | null = getValue();
623
+ if (n) {
624
+ // n: i32 (не null и не 0)
625
+ }
626
+
627
+ // class — всегда truthy (non-null по определению)
628
+ let u = new User("Alice");
629
+ if (u) { } // всегда truthy — компилятор выдаёт warning: условие всегда true
630
+
631
+ // array / Set / Map — всегда truthy, даже пустые
632
+ let arr: i32[] = [];
633
+ if (arr) { } // truthy — warning: условие всегда true
634
+ // для проверки на пустоту используй arr.length === 0
635
+
636
+ let m = new Map<string, i32>();
637
+ if (m) { } // truthy — warning: условие всегда true
638
+ // для проверки на пустоту используй m.size === 0
639
+ ```
640
+
641
+ Narrowing через truthy/falsy:
642
+ ```typescript
643
+ let s: string | null = getValue();
644
+ if (s) {
645
+ console.log(s.length); // s: string — не null, не ""
646
+ } else {
647
+ // s: string | null, но точно null или ""
648
+ }
649
+ ```
650
+
651
+ C-output для truthy check:
652
+ ```c
653
+ // string | null
654
+ if (s != NULL && s->length > 0) { ... }
655
+
656
+ // i32 | null (struct)
657
+ if (x.has_value && x.value != 0) { ... }
658
+
659
+ // string (non-nullable)
660
+ if (s->length > 0) { ... }
661
+ ```
662
+
663
+ - Синтаксис nullable типа: `T | null` — для любых типов, компилятор выбирает реализацию:
664
+ - Сложные типы (строки, массивы, объекты, Map, Set) → `T* = NULL` в C (бесплатно)
665
+ - Примитивы (`i8`..`i64`, `u8`..`u64`, `f32`, `f64`, `boolean`) → `struct { bool has_value; T value; }` в C
666
+ - Компилятор сужает тип после проверки (type narrowing):
667
+ ```typescript
668
+ function findIndex(arr: i32[], val: i32): i32 | null {
669
+ for (let i = 0; i < arr.length; i++) {
670
+ if (arr[i] == val) return i;
671
+ }
672
+ return null;
673
+ }
674
+
675
+ const idx = findIndex(arr, 42);
676
+ if (idx != null) {
677
+ // здесь idx — просто i32
678
+ }
679
+ ```
680
+ - **Синтаксис `?`** — сахар для `T | null`, работает везде:
681
+ ```typescript
682
+ // переменные
683
+ let x?: i32; // то же что let x: i32 | null = null;
684
+ let s?: string; // то же что let s: string | null = null;
685
+
686
+ // параметры функции
687
+ function foo(x: i32, y?: i32) { ... } // y: i32 | null
688
+ foo(1); // y = null
689
+ foo(1, 5); // y = 5
690
+
691
+ // поля класса/структуры
692
+ class User {
693
+ name: string;
694
+ age?: i32; // то же что age: i32 | null
695
+ }
696
+ ```
697
+
698
+ - **Optional chaining `?.`** — обращение к полю/методу только если значение не null; возвращает `T | null`:
699
+ ```typescript
700
+ const name = user?.profile?.name; // null если user или profile = null
701
+ const len = user?.tags?.length; // i32 | null
702
+
703
+ // методы
704
+ const upper = user?.getName()?.toUpperCase();
705
+
706
+ // C-output (вложенные тернарные операторы или if-цепочки)
707
+ // String* name = (user != NULL && user->profile != NULL) ? user->profile->name : NULL;
708
+ ```
709
+ Тип результата `?.` всегда nullable: `T | null`.
710
+
711
+ - **Nullish coalescing `??`** — дефолтное значение если `null`:
712
+ ```typescript
713
+ const name = user.name ?? "Anonymous"; // string
714
+ const age = user.age ?? 0; // i32
715
+
716
+ // цепочка с ?.
717
+ const city = user?.address?.city ?? "Unknown";
718
+ ```
719
+ Правая часть `??` должна быть того же типа что `T` в `T | null` — ошибка компилятора иначе.
720
+
721
+ C-output зависит от типа:
722
+ ```typescript
723
+ // Примитив (struct { bool has_value; T value; }):
724
+ const x: i32 | null = getSomething();
725
+ const y = x ?? 0;
726
+ // → int32_t y = x.has_value ? x.value : 0;
727
+
728
+ // Сложный тип (указатель):
729
+ let s: string | null = getString();
730
+ const result = s ?? "default";
731
+ // → String result = s != NULL ? *s : str("default");
732
+ // s помечается компилятором как перемещённый (moved)
733
+ ```
734
+
735
+ #### Индексация и срезы (массивы и строки)
736
+
737
+ Единый синтаксис для массивов и строк. Конец среза всегда **эксклюзивный**, отрицательные индексы считают с конца:
738
+
739
+ | Синтаксис | Результат |
740
+ |-----------|-----------|
741
+ | `arr[i]` | элемент по индексу (отрицательный: с конца) |
742
+ | `arr[1..3]` | элементы 1, 2 |
743
+ | `arr[1..]` | с 1 до конца |
744
+ | `arr[..3]` | с начала до 3 (не включая) |
745
+ | `arr[..]` | весь массив |
746
+ | `arr[-1]` | последний элемент |
747
+ | `arr[0..-1]` | всё кроме последнего |
748
+ | `arr[-2..]` | последние два элемента |
749
+
750
+ #### Date
751
+
752
+ JS-совместимый тип даты/времени. Реализован поверх C `time_t` / `struct tm` из `<time.h>`.
753
+
754
+ Внутреннее представление — `int64_t` (миллисекунды с Unix epoch), как в JS.
755
+
756
+ ##### Создание
757
+
758
+ ```typescript
759
+ new Date() // текущее время
760
+ new Date(1710936000000) // из миллисекунд с epoch
761
+ new Date("2024-03-20") // из ISO строки
762
+ new Date("2024-03-20T14:30:00.000Z") // ISO с временем
763
+ new Date(2024, 2, 20) // год, месяц (0-11!), день
764
+ new Date(2024, 2, 20, 14, 30, 0, 0) // + часы, минуты, секунды, мс
765
+ ```
766
+
767
+ ##### Статические методы
768
+
769
+ ```typescript
770
+ Date.now() // i64 — текущее время в мс с epoch
771
+ ```
772
+
773
+ ##### Геттеры
774
+
775
+ ```typescript
776
+ const d = new Date("2024-03-20T14:30:00.000Z");
777
+
778
+ d.getFullYear() // i32 — 2024
779
+ d.getMonth() // i32 — 2 (0-11, март = 2)
780
+ d.getDate() // i32 — 20 (день месяца, 1-31)
781
+ d.getDay() // i32 — 3 (день недели, 0=воскресенье)
782
+ d.getHours() // i32 — 14
783
+ d.getMinutes() // i32 — 30
784
+ d.getSeconds() // i32 — 0
785
+ d.getMilliseconds() // i32 — 0
786
+ d.getTime() // i64 — мс с epoch
787
+ d.getTimezoneOffset() // i32 — смещение timezone в минутах
788
+ ```
789
+
790
+ ##### Сеттеры
791
+
792
+ ```typescript
793
+ d.setFullYear(2025)
794
+ d.setMonth(0) // январь
795
+ d.setDate(1)
796
+ d.setHours(12)
797
+ d.setMinutes(0)
798
+ d.setSeconds(0)
799
+ d.setMilliseconds(0)
800
+ d.setTime(1710936000000)
801
+ ```
802
+
803
+ ##### Форматирование
804
+
805
+ ```typescript
806
+ d.toISOString() // "2024-03-20T14:30:00.000Z"
807
+ d.toString() // "Wed Mar 20 2024 14:30:00 GMT+0000"
808
+ d.toDateString() // "Wed Mar 20 2024"
809
+ d.toTimeString() // "14:30:00 GMT+0000"
810
+ d.toLocaleDateString() // локализованная дата
811
+ d.toLocaleTimeString() // локализованное время
812
+ d.toLocaleString() // локализованные дата и время
813
+ d.valueOf() // i64 — то же что getTime()
814
+ ```
815
+
816
+ ##### C-output
817
+
818
+ ```c
819
+ typedef struct { int64_t ms; } Date;
820
+
821
+ // Date.now()
822
+ Date Date_now() {
823
+ struct timespec ts;
824
+ clock_gettime(CLOCK_REALTIME, &ts);
825
+ return (Date){ ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL };
826
+ }
827
+
828
+ // getFullYear()
829
+ int32_t Date_getFullYear(Date d) {
830
+ time_t t = d.ms / 1000;
831
+ struct tm* tm = gmtime(&t);
832
+ return tm->tm_year + 1900;
833
+ }
834
+ ```
835
+
836
+ > На embedded `gmtime` / `localtime` могут быть недоступны — используй `PlainDateTime` (Temporal, в разработке).
837
+
838
+ #### Массивы и коллекции
839
+
840
+ ##### Массивы
841
+
842
+ | Синтаксис | Тип | Память |
843
+ |-----------|-----|--------|
844
+ | `[1, 2, 3]` | литерал, динамический | heap |
845
+ | `i32[]` | тип динамического массива | heap |
846
+ | `i32[3]` | фиксированный, ровно 3 элемента | стек |
847
+
848
+ ```typescript
849
+ let a = [1, 2, 3]; // динамический, из литерала
850
+ let b: i32[] = []; // пустой динамический
851
+ let c: i32[3] = [1, 2, 3]; // фиксированный, ровно 3 элемента
852
+ let d: i32[] = new Array(100); // capacity=100, length=0 (тип из аннотации)
853
+ let e = new Array<i32>(100); // то же самое, без аннотации
854
+ // ВАЖНО: аргумент new Array(N) — это capacity, не length (расхождение с JS)
855
+ ```
856
+
857
+ Фиксированный массив `T[N]`:
858
+ - Размер известен на этапе компиляции, память на стеке
859
+ - Литерал инициализации должен содержать ровно N элементов — иначе ошибка компилятора
860
+ - `push`/`pop` недоступны — ошибка компилятора
861
+ - Передаётся в функции как `Ref<T[]>` / `Mut<T[]>` — фиксированный является подтипом динамического:
862
+ ```typescript
863
+ function sum(arr: Ref<i32[]>): i32 { ... } // принимает любой i32 массив
864
+
865
+ let fixed: i32[3] = [1, 2, 3];
866
+ let dynamic: i32[] = [1, 2, 3, 4];
867
+
868
+ sum(fixed); // ok — автоматически как Ref<i32[]>
869
+ sum(dynamic); // ok
870
+ ```
871
+
872
+ Методы и свойства динамического массива:
873
+ - `arr.push(item)` — move item в конец массива; бросает при OOM
874
+ ```typescript
875
+ let arr: User[] = [];
876
+ let user = new User();
877
+ arr.push(user); // move — arr владеет user
878
+ console.log(user); // ошибка: user перемещён
879
+ ```
880
+ - `arr.pop()` — удалить и вернуть последний элемент как owned `T | null`; null если массив пустой
881
+ ```typescript
882
+ let last = arr.pop(); // User | null
883
+ if (last != null) {
884
+ last.doSomething(); // ok — last владеет объектом
885
+ }
886
+ // или короче:
887
+ arr.pop()?.doSomething(); // ?. — только если не null
888
+ const u = arr.pop() ?? defaultUser; // ?? — дефолт если null
889
+ ```
890
+ - `arr.remove(i)` — удалить по индексу с возвратом ownership
891
+ - `arr.fill(value)` — **TSC**: заполнить все слоты 0..capacity, length становится равным capacity
892
+ - `arr.fill(value, start, end)` — **JS-совместимо**: заполнить индексы `start..end-1` в пределах `0..length`, length не меняется:
893
+ - `end > length` — ошибка компилятора (константы) или runtime error (переменные)
894
+ ```typescript
895
+ let arr: i32[] = new Array(100); // capacity=100, length=0
896
+ arr.fill(0); // capacity=100, length=100, все слоты = 0
897
+ arr.fill(5, 0, 10); // индексы 0..9 = 5, length остаётся 100
898
+ arr.fill(5, 90, 110); // ошибка: end=110 > length=100
899
+ ```
900
+ - `arr.resize(n)` — уменьшить length до n; если n > length — ошибка компилятора (используй `resize(n, value)`)
901
+ - `arr.resize(n, value)` — изменить length до n, новые слоты заполняются `value`; если n > capacity — автоматически реаллоцирует; при уменьшении `value` игнорируется
902
+ ```typescript
903
+ arr.resize(10); // ok — уменьшить, value не нужен
904
+ arr.resize(50); // ошибка компилятора: n > length, используй resize(n, value)
905
+ arr.resize(200, 0); // ok — увеличить, новые слоты = 0, реаллоцирует если нужно
906
+ arr.resize(5, 0); // ok — уменьшить, value игнорируется
907
+ ```
908
+ - `arr.length` — количество элементов (доступны индексы `0..length-1`), readonly;
909
+ присвоение `arr.length = n` — ошибка компилятора с подсказкой: `use arr.resize(n) instead`
910
+ ```typescript
911
+ let arr: i32[] = new Array(100); // capacity=100, length=0
912
+ arr.push(1);
913
+ arr.push(2); // capacity=100, length=2
914
+
915
+ arr[0]; // ok → 1
916
+ arr[1]; // ok → 2
917
+ arr[2]; // runtime error: index 2 out of bounds (length=2)
918
+ arr[99]; // runtime error: index 99 out of bounds (length=2)
919
+ arr[-1]; // ok → 2 (последний элемент)
920
+ arr[-3]; // runtime error: index -3 out of bounds (length=2)
921
+
922
+ arr.length = 10; // ошибка компилятора: use arr.resize(10) instead
923
+ ```
924
+ - `arr.capacity` — заранее выделенная память, читать и записывать:
925
+ ```typescript
926
+ let arr: i32[] = new Array(100); // capacity=100, length=0
927
+ arr.fill(0); // capacity=100, length=100
928
+
929
+ // увеличить capacity — length не меняется
930
+ arr.capacity = 200; // capacity=200, length=100
931
+ console.log(arr.capacity); // 200
932
+ console.log(arr.length); // 100
933
+
934
+ // уменьшить capacity ниже length — length обрезается
935
+ arr.capacity = 50; // capacity=50, length=50 (было length=100, обрезано)
936
+ console.log(arr.capacity); // 50
937
+ console.log(arr.length); // 50
938
+
939
+ arr.capacity = 30; // capacity=30, length=30 (было length=50, обрезано)
940
+ console.log(arr.capacity); // 30
941
+ console.log(arr.length); // 30
942
+ ```
943
+
944
+ ##### Структуры данных под капотом
945
+
946
+ | Тип | Реализация в C | Ключи |
947
+ |-----|----------------|-------|
948
+ | `{}` объектный литерал | `struct` | известны на этапе компиляции |
949
+ | `Map<K, V>` | хеш-таблица | известны только в runtime |
950
+ | `Set<T>` | хеш-множество | известны только в runtime |
951
+
952
+ `Object.keys(obj)` — компилятор знает ключи статически и генерирует их как массив констант. В отличие от JS, `{}` в TSC **не является** хеш-таблицей.
953
+
954
+ ##### Map
955
+
956
+ Инициализация:
957
+ ```typescript
958
+ // Универсальный — любой тип ключа
959
+ let m = new Map<string, i32>([["a", 1], ["b", 2]]);
960
+
961
+ // Объектный литерал — только string ключи
962
+ let m: Map<string, i32> = { "a": 1, "b": 2 };
963
+
964
+ // Пустая Map
965
+ let m = new Map<string, i32>();
966
+ ```
967
+
968
+ Методы:
969
+ ```typescript
970
+ m.set(key, value) // key: move (сложный тип) / copy (примитив); value: move — Map владеет обоими
971
+ m.get(key) // key: Ref<K>, возвращает Ref<V> | null (не V | undefined как в JS)
972
+ m.has(key) // key: Ref<K>, boolean
973
+ m.delete(key) // key: Ref<K>, возвращает V | null (owned) — элемент удалён из Map
974
+ m.clear() // void
975
+ m.size // number, readonly
976
+
977
+ // ?. и ?? с Map
978
+ const len = m.get("key")?.length ?? 0; // Ref<string> | null → i32
979
+ const val = m.delete("key") ?? fallback; // V | null → V
980
+ ```
981
+
982
+ Примеры ownership:
983
+ ```typescript
984
+ let m = new Map<string, User>();
985
+ let user = new User();
986
+ m.set("alice", user); // "alice" — литерал, копируется; user — move
987
+ console.log(user); // ошибка: user перемещён
988
+
989
+ let key = "alice";
990
+ m.set(key, user2); // key — move
991
+ console.log(key); // ошибка: key перемещён
992
+
993
+ let u = m.get("alice"); // Ref<User> | null — borrow из Map
994
+ let u = m.delete("alice"); // User | null — owned, элемент удалён
995
+
996
+ // примитивы — всегда copy
997
+ let m = new Map<string, i32>();
998
+ m.set("x", 42); // 42 скопирован
999
+ m.get("x"); // i32 | null — copy (примитив)
1000
+ ```
1001
+
1002
+ Итерация — `k: Ref<K>`, `v: Ref<V>` для сложных типов, copy для примитивов:
1003
+ ```typescript
1004
+ for (const [k, v] of m) {
1005
+ v.doSomething(); // ok — immutable метод
1006
+ v.mutMethod(); // ошибка — v это Ref
1007
+ m.set("x", val); // ошибка — m заимствован
1008
+ }
1009
+ m.forEach((k, v) => { ... });
1010
+ for (const k of m.keys()) { ... }
1011
+ for (const v of m.values()) { ... }
1012
+ for (const [k, v] of m.entries()) { ... }
1013
+ ```
1014
+
1015
+ ##### Set
1016
+
1017
+ Инициализация:
1018
+ ```typescript
1019
+ let s = new Set<i32>([1, 2, 3]);
1020
+ let s = new Set<string>();
1021
+ ```
1022
+
1023
+ Методы:
1024
+ ```typescript
1025
+ s.add(value) // move — Set становится владельцем; бросает при OOM
1026
+ s.has(value) // Ref<T> — только для сравнения, владение не меняется; boolean
1027
+ s.delete(value) // Ref<T> для поиска, возвращает T | null (owned) — элемент удалён из Set
1028
+ s.clear() // void
1029
+ s.size // number, readonly
1030
+
1031
+ // ?. и ?? с Set
1032
+ const deleted = s.delete(user);
1033
+ deleted?.cleanup(); // вызвать метод если элемент был в Set
1034
+ const u = s.delete(user) ?? fallback; // дефолт если элемента не было
1035
+ ```
1036
+
1037
+ Примеры ownership:
1038
+ ```typescript
1039
+ let s = new Set<User>();
1040
+ let user = new User();
1041
+ s.add(user); // move — user перешёл во владение Set
1042
+ console.log(user); // ошибка: user перемещён
1043
+
1044
+ // примитивы — всегда copy
1045
+ let s = new Set<i32>();
1046
+ let x = 42;
1047
+ s.add(x); // copy
1048
+ console.log(x); // ok
1049
+ ```
1050
+
1051
+ Теоретико-множественные операции — доступны для примитивов, `string` и `Shared<T>`:
1052
+ ```typescript
1053
+ s.union(other) // новый owned Set — все элементы из s и other
1054
+ s.intersection(other) // новый owned Set — только общие элементы
1055
+ s.difference(other) // новый owned Set — элементы s которых нет в other
1056
+ s.symmetricDifference(other) // новый owned Set — элементы только в одном из двух
1057
+ s.isSubsetOf(other) // boolean
1058
+ s.isSupersetOf(other) // boolean
1059
+ s.isDisjointFrom(other) // boolean
1060
+ ```
1061
+
1062
+ Для `Shared<T>` — union это просто retain на каждый элемент, без копирования объектов:
1063
+ ```typescript
1064
+ let user1: Shared<User> = new User();
1065
+ let user2: Shared<User> = new User();
1066
+
1067
+ let a = new Set<Shared<User>>([user1, user2]);
1068
+ let b = new Set<Shared<User>>([user2]);
1069
+ let c = a.union(b); // ok — retain на элементы, refcount растёт
1070
+ ```
1071
+
1072
+ Для `string` — элементы клонируются в новый Set:
1073
+ ```typescript
1074
+ let morphemes = new Set<string>(["бег", "ать"]);
1075
+ let suffixes = new Set<string>(["ать", "ить"]);
1076
+ let common = morphemes.intersection(suffixes); // new Set<string> {"ать"}
1077
+ ```
1078
+
1079
+ Для owned сложных типов — ошибка компилятора:
1080
+ ```typescript
1081
+ let a = new Set<User>([user1, user2]);
1082
+ let b = new Set<User>([user2]);
1083
+ let c = a.union(b);
1084
+ // ошибка: union requires Set<primitive>, Set<string> or Set<Shared<T>>
1085
+ // hint: use Set<Shared<User>> instead
1086
+ ```
1087
+
1088
+ Итерация — `v` это `Ref<T>` для сложных типов, copy для примитивов:
1089
+ ```typescript
1090
+ for (const v of s) {
1091
+ v.doSomething(); // ok — immutable метод
1092
+ v.mutMethod(); // ошибка — v это Ref
1093
+ s.add(other); // ошибка — s заимствован
1094
+ }
1095
+ s.forEach((v) => { ... });
1096
+ for (const v of s.values()) { ... }
1097
+ ```
1098
+
1099
+ ##### Object
1100
+
1101
+ Статические методы для работы с объектами. Ключи — compile-time константы, возвращаются как копии. Значения — Ref для сложных типов, copy для примитивов:
1102
+
1103
+ ```typescript
1104
+ const obj = { a: user1, b: user2 };
1105
+ Object.keys(obj) // string[] — копии ключей
1106
+ Object.values(obj) // Ref<User>[] — borrow значений
1107
+ Object.entries(obj) // [string, Ref<User>][] — ключи copy, значения Ref
1108
+
1109
+ const obj = { x: 1, y: 2 };
1110
+ Object.keys(obj) // string[] — копии ключей
1111
+ Object.values(obj) // i32[] — copy (примитивы)
1112
+ Object.entries(obj) // [string, i32][] — всё copy
1113
+ ```
1114
+
1115
+ Итерация:
1116
+ ```typescript
1117
+ for (const k of Object.keys(obj)) { ... }
1118
+ for (const v of Object.values(obj)) { ... }
1119
+ for (const [k, v] of Object.entries(obj)) { ... }
1120
+ ```
1121
+
1122
+ ### Memory Model
1123
+
1124
+ **Гибридная модель:** статический ownership/borrow checker + опциональный ARC. Нет GC, нет ручного `free`.
1125
+
1126
+ #### Типы владения
1127
+
1128
+ | Тип | Семантика |
1129
+ |-----|-----------|
1130
+ | `T` | **Owner** — владеет объектом, move при передаче |
1131
+ | `Ref<T>` | **Immutable borrow** — только чтение |
1132
+ | `Mut<T>` | **Mutable borrow** — чтение и запись |
1133
+ | `Shared<T>` | **ARC** — strong ref, увеличивает refcount |
1134
+ | `Weak<T>` | **Weak ref** — не увеличивает refcount, разрывает циклы |
1135
+
1136
+ #### Базовые правила
1137
+
1138
+ - **Примитивы** (`i8`..`i64`, `u8`..`u64`, `f32`, `f64`, `boolean`) — всегда **копируются**, borrow checker не применяется; `T | null` компилируется в struct с флагом
1139
+ - **Сложные типы** (массивы, объекты, строки, классы) — управляются ownership системой
1140
+
1141
+ #### Owner (T) — владение
1142
+
1143
+ ##### Move при присвоении
1144
+
1145
+ ```typescript
1146
+ let a = new User();
1147
+ let b = a; // MOVE: a теперь invalid
1148
+ // console.log(a); // ошибка: a перемещён
1149
+ ```
1150
+
1151
+ ##### Move при передаче в функцию
1152
+
1153
+ ```typescript
1154
+ function addToCache(cache: Mut<Cache>, data: User[]) {
1155
+ cache.items.push(data); // ok — data принадлежит функции
1156
+ }
1157
+
1158
+ addToCache(myCache, myData);
1159
+ console.log(myData); // ошибка: myData перемещён
1160
+ ```
1161
+
1162
+ #### Ref\<T\> — immutable borrow
1163
+
1164
+ Только чтение, без изменения и удаления.
1165
+
1166
+ ```typescript
1167
+ function sum(arr: Ref<i32[]>): i32 { ... }
1168
+
1169
+ const data = [1, 2, 3];
1170
+ sum(data);
1171
+ console.log(data); // ok — data не перемещён
1172
+ ```
1173
+
1174
+ #### Mut\<T\> — mutable borrow
1175
+
1176
+ Чтение и запись, только один `Mut` за раз.
1177
+
1178
+ ```typescript
1179
+ function push(arr: Mut<i32[]>, val: i32) {
1180
+ arr.push(val);
1181
+ }
1182
+
1183
+ let data = [1, 2, 3];
1184
+ push(data, 4);
1185
+ console.log(data); // [1, 2, 3, 4]
1186
+ ```
1187
+
1188
+ #### Shared\<T\> — ARC
1189
+
1190
+ Для графов, циклов, неопределённого времени жизни.
1191
+
1192
+ Объект становится `Shared` через аннотацию типа — компилятор автоматически оборачивает в ARC:
1193
+
1194
+ ```typescript
1195
+ let node: Shared<Node> = new Node(); // ARC
1196
+ let node = new Node(); // Owner — move семантика
1197
+ ```
1198
+
1199
+ Цикл разрывается через `Weak<T>` — одна из сторон держит слабую ссылку:
1200
+
1201
+ ```typescript
1202
+ class Node {
1203
+ next: Shared<Node>;
1204
+ prev: Weak<Node>;
1205
+ }
1206
+
1207
+ let node1: Shared<Node> = new Node();
1208
+ let node2: Shared<Node> = new Node();
1209
+ node1.next = node2; // retain(node2)
1210
+ node2.prev = node1; // weak — refcount не растёт, цикл разорван
1211
+ ```
1212
+
1213
+ При обращении к `Weak<T>` — тип всегда `T | null` (объект мог быть освобождён). Используй `?.` и `??`:
1214
+
1215
+ ```typescript
1216
+ node2.prev?.doSomething(); // вызов только если объект жив
1217
+ const name = node2.prev?.name ?? ""; // дефолт если объект освобождён
1218
+ if (node2.prev != null) {
1219
+ // narrowing — здесь prev: Weak<Node> (жив)
1220
+ }
1221
+ ```
1222
+
1223
+ Генерируемый C:
1224
+ ```c
1225
+ Node* node1 = Node_new();
1226
+ RC_retain(node1); // refcount = 1
1227
+ Node* node2 = Node_new();
1228
+ RC_retain(node2); // refcount = 1
1229
+ node1->next = node2;
1230
+ RC_retain(node2); // refcount = 2
1231
+ node2->prev = node1; // weak — RC_retain не вызывается
1232
+ ```
1233
+
1234
+ #### Правила Borrow Checker
1235
+
1236
+ 1. **Нельзя два Mut одновременно**
1237
+ ```typescript
1238
+ let a = [1, 2, 3];
1239
+ let r1: Mut<i32[]> = a;
1240
+ let r2: Mut<i32[]> = a; // ошибка: уже есть активный Mut
1241
+ ```
1242
+
1243
+ 2. **Нельзя Mut + Ref одновременно**
1244
+ ```typescript
1245
+ let a = [1, 2, 3];
1246
+ let r1: Ref<i32[]> = a;
1247
+ let r2: Mut<i32[]> = a; // ошибка: a уже заимствован как Ref
1248
+ ```
1249
+
1250
+ 3. **Можно несколько Ref одновременно**
1251
+ ```typescript
1252
+ let a = [1, 2, 3];
1253
+ let r1: Ref<i32[]> = a;
1254
+ let r2: Ref<i32[]> = a; // ok
1255
+ ```
1256
+
1257
+ #### Правила передачи аргументов в функцию
1258
+
1259
+ Тип параметра в сигнатуре **полностью диктует semantics на callsite** — явных `&` или `*` не нужно.
1260
+
1261
+ **Примитивы — всегда copy**, независимо от типа параметра:
1262
+ ```typescript
1263
+ function foo(x: i32): void { ... }
1264
+ let n = 42;
1265
+ foo(n); // copy — n жив после вызова
1266
+ ```
1267
+
1268
+ **Сложные типы — 4 варианта параметра:**
1269
+ ```typescript
1270
+ function toRef(x: Ref<User>): void { ... } // borrow
1271
+ function toMut(x: Mut<User>): void { ... } // mutable borrow
1272
+ function toOwned(x: User): void { ... } // move
1273
+ function toShared(x: Shared<User>): void { ... } // retain
1274
+
1275
+ let u = new User();
1276
+ const c = new User();
1277
+ let s: Shared<User> = new User();
1278
+
1279
+ toRef(u); // ok — auto borrow, u жив
1280
+ toRef(c); // ok — auto borrow, c жив
1281
+ toMut(u); // ok — auto mutable borrow
1282
+ toMut(c); // ошибка: нельзя Mut<T> из const
1283
+ toOwned(u); // ok — move, u недоступен после вызова
1284
+ toOwned(c); // ошибка: нельзя move из const
1285
+ toShared(s); // ok — retain (refcount++)
1286
+ toShared(u); // ошибка: u не является Shared<T>
1287
+ ```
1288
+
1289
+ **Передача через промежуточный тип (Ref/Mut/Shared как источник):**
1290
+ ```typescript
1291
+ function bar(u: Ref<User>): void {
1292
+ toRef(u); // ok — re-borrow
1293
+ toMut(u); // ошибка: Ref → Mut запрещено
1294
+ toOwned(u); // ошибка: нельзя move из Ref
1295
+ // hint: clone если User implements Clone
1296
+ }
1297
+
1298
+ function baz(u: Mut<User>): void {
1299
+ toRef(u); // ok — Mut → Ref разрешено (понижение)
1300
+ toMut(u); // ok — re-borrow как Mut
1301
+ toOwned(u); // ошибка: нельзя move из Mut
1302
+ }
1303
+
1304
+ function qux(u: Shared<User>): void {
1305
+ toRef(u); // ok — borrow из Shared
1306
+ toMut(u); // ошибка: Shared не даёт Mut (нет эксклюзивного владения)
1307
+ toOwned(u); // ошибка: нельзя move из Shared
1308
+ toShared(u); // ok — retain
1309
+ }
1310
+ ```
1311
+
1312
+ **Матрица совместимости:**
1313
+
1314
+ | Источник ↓ \ Параметр → | `Ref<T>` | `Mut<T>` | `T` (owned) | `Shared<T>` |
1315
+ |--------------------------|----------|----------|-------------|-------------|
1316
+ | `let T` | ✅ auto borrow | ✅ auto mut borrow | ✅ move | ❌ |
1317
+ | `const T` | ✅ auto borrow | ❌ | ❌ | ❌ |
1318
+ | `Ref<T>` | ✅ re-borrow | ❌ | ❌ | ❌ |
1319
+ | `Mut<T>` | ✅ понижение | ✅ re-borrow | ❌ | ❌ |
1320
+ | `Shared<T>` | ✅ borrow | ❌ | ❌ | ✅ retain |
1321
+
1322
+ #### Scope Constraint (без lifetime аннотаций)
1323
+
1324
+ Два простых правила:
1325
+
1326
+ 1. **Ref/Mut нельзя в глобал**
1327
+ ```typescript
1328
+ let global: Ref<User>; // ошибка
1329
+
1330
+ function foo(u: Ref<User>) {
1331
+ global = u; // ошибка: borrow не может пережить функцию
1332
+ }
1333
+ ```
1334
+
1335
+ 2. **Нельзя вернуть ссылку на локал**
1336
+ ```typescript
1337
+ function bad(): Ref<User> {
1338
+ const u = new User();
1339
+ return u; // ошибка: u умрёт в конце функции
1340
+ }
1341
+ ```
1342
+
1343
+ #### Автоматический Drop
1344
+
1345
+
1346
+ Компилятор вставляет `free()` в конце scope владельца:
1347
+
1348
+ ```c
1349
+ {
1350
+ User* b = User_new();
1351
+ // ... логика ...
1352
+ User_free(b); // вставлено автоматически
1353
+ }
1354
+ ```
1355
+
1356
+ При множественных `return` — единая точка очистки:
1357
+
1358
+ ```c
1359
+ void process(User* u) {
1360
+ if (!u) goto cleanup;
1361
+ if (error) goto cleanup;
1362
+ // ... работа ...
1363
+ cleanup:
1364
+ if (u_is_owned) User_free(u);
1365
+ }
1366
+ ```
1367
+
1368
+ #### Clone
1369
+
1370
+ `Clone` — интерфейс для deep copy. Два синтаксиса, одна семантика:
1371
+
1372
+ ```typescript
1373
+ interface Clone {
1374
+ clone(): this;
1375
+ }
1376
+
1377
+ class User implements Clone {
1378
+ name: string;
1379
+ age: i32;
1380
+
1381
+ clone(): User {
1382
+ return new User(this.name, this.age);
1383
+ }
1384
+ }
1385
+
1386
+ let u1 = new User("Alice", 30);
1387
+ let u2 = structuredClone(u1); // функциональный стиль
1388
+ let u3 = u1.clone(); // метод — то же самое
1389
+ console.log(u1); // ok — u1 жив
1390
+ ```
1391
+
1392
+ - Примитивы и `string` — auto-implement Clone
1393
+ - Массивы — `clone()` / `structuredClone` работают если элементы реализуют `Clone`
1394
+ - `Shared<T>` — `structuredClone` создаёт новый независимый объект (deep copy, не retain)
1395
+ - Spread для pure-primitive структур = неявный clone; для сложных полей = move
1396
+
1397
+ ```typescript
1398
+ // массивы
1399
+ let arr = [1, 2, 3];
1400
+ let arr2 = arr.clone(); // ok — примитивы
1401
+
1402
+ let users = [user1, user2];
1403
+ let users2 = users.clone(); // ok — User implements Clone
1404
+
1405
+ let items = [item1, item2];
1406
+ let items2 = items.clone(); // ошибка: Item does not implement Clone
1407
+ // hint: implement Clone on Item
1408
+ ```
1409
+
1410
+ #### Type Aliases
1411
+
1412
+ `type` — compile-time алиас, не генерирует новый тип в C:
1413
+
1414
+ ```typescript
1415
+ // 1. Алиас примитива — читабельность
1416
+ type UserId = i32;
1417
+ type Timestamp = i64;
1418
+
1419
+ function getUser(id: UserId): User { ... } // UserId = i32 в C
1420
+
1421
+ // 2. Алиас объекта — эквивалентен data-only interface, генерирует typedef struct
1422
+ type Point = { x: f64, y: f64 }; // → typedef struct { double x; double y; } Point;
1423
+ let p: Point = { x: 1.0, y: 2.0 }; // ok — Point struct
1424
+
1425
+ // 3. Union тип
1426
+ type StringOrInt = string | i32;
1427
+ type Nullable<T> = T | null; // generic алиас
1428
+
1429
+ function process(x: StringOrInt): void { ... }
1430
+
1431
+ // 4. Тип функции — для колбэков
1432
+ type Callback = (x: i32) => void;
1433
+ type Comparator<T> = (a: Ref<T>, b: Ref<T>) => i32;
1434
+
1435
+ function sort(arr: Mut<i32[]>, cmp: Comparator<i32>): void { ... }
1436
+ ```
1437
+
1438
+ - `type Point = { ... }` = `interface Point { ... }` для data-only структур — оба генерируют `typedef struct Point` в C
1439
+ - `type UserId = i32` — compile-time алиас примитива, нового C типа нет
1440
+ - `type StringOrInt = string | i32` — compile-time union, нового C типа нет
1441
+ - Номинальная типизация: `type UserId = i32` и `i32` — разные типы
1442
+
1443
+ #### Enum
1444
+
1445
+ ##### Числовой enum
1446
+
1447
+ ```typescript
1448
+ enum Direction { North, South, East, West } // 0, 1, 2, 3
1449
+ enum Color { Red = 1, Green = 2, Blue = 4 } // явные значения (битовые флаги)
1450
+ ```
1451
+
1452
+ C-output:
1453
+ ```c
1454
+ typedef enum { North, South, East, West } Direction;
1455
+ static const Direction Direction_values[] = { North, South, East, West };
1456
+ static const char* Direction_names[] = { "North", "South", "East", "West" };
1457
+ ```
1458
+
1459
+ ##### Строковый enum
1460
+
1461
+ ```typescript
1462
+ enum Status { Ok = "OK", Fail = "FAIL", Pending = "PENDING" }
1463
+ ```
1464
+
1465
+ C-output:
1466
+ ```c
1467
+ typedef enum { Status_Ok, Status_Fail, Status_Pending } Status;
1468
+ static const char* Status_strings[] = { "OK", "FAIL", "PENDING" };
1469
+ ```
1470
+
1471
+ ##### const enum
1472
+
1473
+ Только C enum, без runtime таблиц. Используется когда важен размер бинаря (embedded).
1474
+
1475
+ ```typescript
1476
+ const enum Pin { PA0 = 0, PA1 = 1, PB0 = 8, PB1 = 9 }
1477
+ ```
1478
+
1479
+ C-output:
1480
+ ```c
1481
+ typedef enum { PA0 = 0, PA1 = 1, PB0 = 8, PB1 = 9 } Pin;
1482
+ // больше ничего — нет таблиц
1483
+ ```
1484
+
1485
+ Утилиты на `const enum` недоступны — ошибка компилятора:
1486
+ ```typescript
1487
+ Pin.values() // error: const enum has no runtime table
1488
+ Pin.fromValue(0) // error: const enum has no runtime table
1489
+ Pin.PA0.toString() // error: const enum has no runtime table
1490
+ ```
1491
+
1492
+ ##### Утилиты enum (только обычный enum)
1493
+
1494
+ ```typescript
1495
+ enum Direction { North, South, East, West }
1496
+
1497
+ Direction.values() // Direction[] — все значения: [North, South, East, West]
1498
+ Direction.fromValue(2) // Direction | null — Direction.East | null если не найдено
1499
+ Direction.North.toString() // string — "North"
1500
+
1501
+ // использование
1502
+ for (const d of Direction.values()) {
1503
+ console.log(d.toString());
1504
+ }
1505
+
1506
+ const d = Direction.fromValue(userInput);
1507
+ if (d != null) {
1508
+ console.log(d.toString());
1509
+ }
1510
+ ```
1511
+
1512
+ ##### enum в switch / match
1513
+
1514
+ ```typescript
1515
+ // switch — компилятор выдаёт warning если не все значения покрыты
1516
+ switch (dir) {
1517
+ case Direction.North: ...; break;
1518
+ case Direction.South: ...; break;
1519
+ case Direction.East: ...; break;
1520
+ case Direction.West: ...; break;
1521
+ }
1522
+
1523
+ // match — ошибка компилятора если не все значения покрыты (exhaustiveness)
1524
+ const label = match (dir) {
1525
+ Direction.North => "вверх",
1526
+ Direction.South => "вниз",
1527
+ Direction.East => "вправо",
1528
+ Direction.West => "влево",
1529
+ // _ не нужен — все случаи покрыты
1530
+ };
1531
+ ```
1532
+
1533
+ ##### enum vs const enum
1534
+
1535
+ | | `enum` | `const enum` |
1536
+ |---|---|---|
1537
+ | C-output | `typedef enum` + таблицы | только `typedef enum` |
1538
+ | `.values()` | ✅ | ❌ |
1539
+ | `.fromValue()` | ✅ | ❌ |
1540
+ | `.toString()` | ✅ | ❌ |
1541
+ | Размер бинаря | больше | минимальный |
1542
+ | Применение | общий случай | embedded, флаги, константы |
1543
+
1544
+ #### Интерфейсы
1545
+
1546
+ Два назначения:
1547
+
1548
+ **1. Данные без методов** — компилируется в `typedef struct`:
1549
+ ```typescript
1550
+ interface Point {
1551
+ x: f64;
1552
+ y: f64;
1553
+ }
1554
+
1555
+ let p: Point = { x: 10.5, y: 20.3 };
1556
+ ```
1557
+ ```c
1558
+ typedef struct { double x; double y; } Point;
1559
+ ```
1560
+
1561
+ **2. Контракт с методами** — компилируется в vtable (fat pointer, как `dyn Trait` в Rust):
1562
+ ```typescript
1563
+ interface Drawable {
1564
+ draw(): void;
1565
+ mut resize(factor: f64): void;
1566
+ }
1567
+
1568
+ class Circle implements Drawable {
1569
+ draw(): void { ... }
1570
+ mut resize(factor: f64): void { ... }
1571
+ }
1572
+
1573
+ let shape: Drawable = new Circle(); // fat pointer: self + vtable
1574
+ shape = new Rect(); // ok — другой тип, та же переменная
1575
+ shape.draw(); // ok — immutable метод
1576
+ shape.resize(2.0); // ok — mut метод, shape это let
1577
+
1578
+ const shape2: Drawable = new Circle();
1579
+ shape2.draw(); // ok
1580
+ shape2.resize(2.0); // ошибка: нельзя вызвать mut метод на const
1581
+ ```
1582
+ ```c
1583
+ typedef struct {
1584
+ void (*draw)(void* self);
1585
+ void (*resize)(void* self, double factor);
1586
+ } Drawable_vtable;
1587
+
1588
+ typedef struct {
1589
+ void* self;
1590
+ Drawable_vtable* vtable;
1591
+ } Drawable;
1592
+ ```
1593
+
1594
+ - Класс может реализовывать несколько интерфейсов: `class Foo implements A, B`
1595
+ - `mut` методы интерфейса подчиняются тем же правилам что и `mut` методы класса: `const` переменная запрещает вызов, `let` — разрешает
1596
+ ```typescript
1597
+ interface Drawable {
1598
+ draw(): void;
1599
+ }
1600
+
1601
+ interface Resizable {
1602
+ mut resize(factor: f64): void;
1603
+ }
1604
+
1605
+ class Circle implements Drawable, Resizable {
1606
+ draw(): void { ... }
1607
+ mut resize(factor: f64): void { ... }
1608
+ }
1609
+
1610
+ let shape: Drawable = new Circle(); // ok
1611
+ let resizable: Resizable = new Circle(); // ok
1612
+ ```
1613
+ - Если класс не реализует все методы интерфейса — ошибка компилятора
1614
+
1615
+ #### Классы
1616
+
1617
+ **Наследования нет** — только композиция. `extends` отсутствует.
1618
+
1619
+ ```typescript
1620
+ // вместо наследования — композиция
1621
+ class Animal {
1622
+ name: string;
1623
+ mut speak(): string { ... }
1624
+ }
1625
+
1626
+ class Dog {
1627
+ animal: Animal; // композиция
1628
+ breed: string;
1629
+ }
1630
+ ```
1631
+
1632
+ `mut` определяет семантику `this`. Модификаторы методов и полей:
1633
+
1634
+ | Модификатор | Описание |
1635
+ |-------------|----------|
1636
+ | `public` | виден везде (по умолчанию) |
1637
+ | `private` | виден только внутри класса |
1638
+ | `static` | метод на классе, нет `this` |
1639
+ | `mut` | `this` — `Mut<Self>`, иначе `Ref<Self>` |
1640
+ | `move` | `this` — `Self` (owned), объект перемещается в метод при вызове |
1641
+
1642
+ ```typescript
1643
+ class Counter {
1644
+ private value: i32 = 0;
1645
+
1646
+ public get(): i32 { // this — Ref<Counter>
1647
+ return this.value;
1648
+ }
1649
+
1650
+ public mut increment(): void { // this — Mut<Counter>
1651
+ this.value++;
1652
+ }
1653
+
1654
+ private mut reset(): void { // private mutable
1655
+ this.value = 0;
1656
+ }
1657
+
1658
+ static create(): Counter { // static — нет this
1659
+ return new Counter();
1660
+ }
1661
+
1662
+ private static default(): Counter { // private static
1663
+ return new Counter();
1664
+ }
1665
+ }
1666
+
1667
+ const c = new Counter();
1668
+ c.get(); // ok
1669
+ c.increment(); // ошибка: нельзя вызвать mut метод на const
1670
+
1671
+ let c2 = new Counter();
1672
+ c2.increment(); // ok
1673
+ ```
1674
+
1675
+ - `static` + `mut` — недопустимо, ошибка компилятора (нет `this`)
1676
+ - `protected` — отсутствует (нет наследования)
1677
+
1678
+ #### Семантика `this` и доступ к полям
1679
+
1680
+ Тип `this` определяет тип `this.field`. Затем применяются **те же правила передачи аргументов** что и для обычных функций — см. матрицу совместимости в разделе "Правила передачи аргументов в функцию":
1681
+
1682
+ | Вид метода | `this` тип | `this.field` тип (сложный) | `this.field` тип (примитив) |
1683
+ |-----------|------------|---------------------------|---------------------------|
1684
+ | обычный | `Ref<Self>` | `Ref<T>` | copy |
1685
+ | `mut` | `Mut<Self>` | `Mut<T>` | copy |
1686
+ | `move` | `Self` (owned) | `T` (owned) | copy |
1687
+
1688
+ Тип `this.field` определяется типом `this`. Затем применяются **те же правила из матрицы совместимости** (раздел "Правила передачи аргументов в функцию"):
1689
+
1690
+ ```typescript
1691
+ function sendEmail(to: string): void { ... } // ожидает owned string
1692
+ function printRef(s: Ref<string>): void { ... } // ожидает borrow
1693
+
1694
+ class QueryBuilder {
1695
+ query: string;
1696
+ params: i32[];
1697
+
1698
+ // обычный метод — this: Ref<Self>, this.query: Ref<string>
1699
+ preview(): void {
1700
+ printRef(this.query); // ok — Ref<string> → Ref<string> ✅
1701
+ sendEmail(this.query); // ошибка — Ref<string> → string ❌
1702
+ // матрица: Ref<T> → T (owned) = запрещено
1703
+ // hint: clone если string implements Clone
1704
+ sendEmail(this.query.clone()); // ok ✅
1705
+ console.log(this.params[0]); // ok — i32 всегда copy ✅
1706
+ }
1707
+
1708
+ // mut метод — this: Mut<Self>, this.query: Mut<string>
1709
+ mut setQuery(q: string): void {
1710
+ this.query = q; // ok — Mut разрешает запись ✅
1711
+ sendEmail(this.query); // ошибка — Mut<string> → string ❌
1712
+ // матрица: Mut<T> → T (owned) = запрещено
1713
+ sendEmail(this.query.clone()); // ok ✅
1714
+ }
1715
+
1716
+ // move метод — this: Self (owned), this.query: string (owned)
1717
+ move build(): Query {
1718
+ return new Query(this.query, this.params); // ok — T → T, move ✅
1719
+ }
1720
+ }
1721
+
1722
+ let b = new QueryBuilder("SELECT *", [1, 2]);
1723
+ b.preview(); // ok — b жив ✅
1724
+ b.setQuery("INSERT"); // ok — b жив ✅
1725
+ const q = b.build(); // ok — b moved в метод
1726
+ console.log(b); // ошибка: b перемещён ❌
1727
+
1728
+ const b2 = new QueryBuilder("SELECT *", []);
1729
+ b2.build(); // ошибка: нельзя вызвать move метод на const ❌
1730
+ ```
1731
+
1732
+ `readonly` поле можно записать только в конструкторе:
1733
+
1734
+ ```typescript
1735
+ class User {
1736
+ readonly id: i32;
1737
+ name: string;
1738
+
1739
+ constructor(id: i32, name: string) {
1740
+ this.id = id; // ok
1741
+ this.name = name;
1742
+ }
1743
+
1744
+ mut rename(newName: string) {
1745
+ this.name = newName; // ok
1746
+ this.id = 99; // ошибка: readonly
1747
+ }
1748
+ }
1749
+ ```
1750
+
1751
+ `mut` метод может менять обычные поля, но не `readonly`.
1752
+
1753
+ `move` метод передает поля объекта наружу без лишнего копирования, когда исходный объект больше не нужен. Паттерн `Builder`:
1754
+
1755
+ ```typescript
1756
+ class QueryBuilder {
1757
+ query: string;
1758
+ params: i32[];
1759
+
1760
+ // без move — this: Ref<Self>, поля нельзя move, нужен clone:
1761
+ build(): Query {
1762
+ return new Query(this.query.clone(), this.params.clone()); // лишняя копия данных
1763
+ }
1764
+
1765
+ // с move — this: Self (owned), поля можно move, clone не нужен
1766
+ move build(): Query {
1767
+ return new Query(this.query, this.params); // move полей — экономия памяти
1768
+ }
1769
+ }
1770
+
1771
+ let b = new QueryBuilder("SELECT *", [1, 2, 3]);
1772
+ const q = b.build(); // b перемещён в метод, данные переданы в Query без копии
1773
+ console.log(b); // ошибка: b перемещён — компилятор ловит
1774
+ ```
1775
+
1776
+ Конструктор — поля забирают владение (move):
1777
+
1778
+ ```typescript
1779
+ class Line {
1780
+ start: Point;
1781
+ end: Point;
1782
+
1783
+ constructor(start: Point, end: Point) {
1784
+ this.start = start; // move
1785
+ this.end = end; // move
1786
+ }
1787
+ }
1788
+
1789
+ const p1 = new Point(0, 0);
1790
+ const p2 = new Point(1, 1);
1791
+ const line = new Line(p1, p2);
1792
+ console.log(p1); // ошибка: p1 перемещён в line
1793
+ ```
1794
+
1795
+ Автогенерация конструктора — если конструктор не написан, компилятор генерирует его из полей:
1796
+
1797
+ - Поля **с дефолтом** → параметр со значением по умолчанию
1798
+ - Поля **без дефолта** → обязательный параметр (в порядке объявления)
1799
+
1800
+ ```typescript
1801
+ class User {
1802
+ name: string; // нет дефолта → обязательный параметр
1803
+ age: i32 = 0; // есть дефолт → необязательный параметр
1804
+ active: boolean = true;
1805
+ }
1806
+ // компилятор генерирует:
1807
+ // constructor(name: string, age: i32 = 0, active: boolean = true)
1808
+
1809
+ new User("Alice"); // ok — name="Alice", age=0, active=true
1810
+ new User("Alice", 30); // ok — name="Alice", age=30, active=true
1811
+ new User("Alice", 30, false); // ok
1812
+ new User(); // ошибка: name обязателен
1813
+
1814
+ class Point {
1815
+ x: f64 = 0.0;
1816
+ y: f64 = 0.0;
1817
+ // все поля с дефолтом → генерируется конструктор без обязательных параметров
1818
+ }
1819
+
1820
+ let p = new Point(); // ok — x=0.0, y=0.0
1821
+ let p2 = new Point(1.0); // ok — x=1.0, y=0.0
1822
+ ```
1823
+
1824
+ Если написан явный `constructor` — автогенерация не происходит.
1825
+
1826
+ Дефолтные параметры конструктора — вместо перегрузки по количеству:
1827
+ ```typescript
1828
+ class Point {
1829
+ x: f64;
1830
+ y: f64;
1831
+
1832
+ constructor(x: f64 = 0.0, y: f64 = 0.0) {
1833
+ this.x = x;
1834
+ this.y = y;
1835
+ }
1836
+ }
1837
+
1838
+ let p1 = new Point(); // x=0.0, y=0.0
1839
+ let p2 = new Point(1.0); // x=1.0, y=0.0
1840
+ let p3 = new Point(1.0, 2.0); // x=1.0, y=2.0
1841
+ ```
1842
+
1843
+ `private` конструктор — для singleton/factory паттернов:
1844
+ ```typescript
1845
+ class Config {
1846
+ private constructor() { ... }
1847
+
1848
+ static create(): Config {
1849
+ return new Config(); // ok — внутри класса
1850
+ }
1851
+ }
1852
+
1853
+ let c = new Config(); // ошибка: конструктор private
1854
+ let c = Config.create(); // ok
1855
+ ```
1856
+
1857
+ #### `const` vs `let`
1858
+
1859
+ - `const obj` — нельзя вызывать `mut` методы, нельзя передать как `Mut`, нельзя move
1860
+ - `let obj` — можно всё
1861
+
1862
+ ```typescript
1863
+ function foo(c: Mut<Counter>) { c.increment(); }
1864
+
1865
+ const c = new Counter();
1866
+ foo(c); // ошибка: const нельзя передать как Mut
1867
+
1868
+ let c2 = new Counter();
1869
+ foo(c2); // ok
1870
+
1871
+ // move из const — запрещён
1872
+ const arr = [user1, user2];
1873
+ let b = arr; // ошибка: cannot move out of const
1874
+ // hint: use Shared<T> if shared ownership is needed
1875
+
1876
+ const arr2: Shared<User[]> = [user1, user2];
1877
+ let b2 = arr2; // ok — retain, не move
1878
+ ```
1879
+
1880
+ #### For-of цикл
1881
+
1882
+ Тип loop-переменной определяется **объявлением** (`const`/`let`), а не источником:
1883
+
1884
+ - `for (const item of ...)` — **всегда** `Ref<T>` для сложных типов, copy для примитивов
1885
+ - `for (let item of ...)` — `Mut<T>`, **только если источник `let`**; если источник `const` — ошибка компилятора
1886
+
1887
+ ```typescript
1888
+ const arr = [obj1, obj2, obj3];
1889
+
1890
+ for (const item of arr) { // ok — item: Ref<Obj>
1891
+ item.doSomething(); // ok — immutable метод
1892
+ item.mutMethod(); // ошибка — item это Ref
1893
+ }
1894
+
1895
+ for (let item of arr) { // ошибка: источник const, используй for (const item of arr)
1896
+ }
1897
+ ```
1898
+
1899
+ ```typescript
1900
+ let arr = [obj1, obj2, obj3];
1901
+
1902
+ for (const item of arr) { // ok — item: Ref<Obj>
1903
+ item.doSomething(); // ok
1904
+ item.mutMethod(); // ошибка — item это Ref
1905
+ }
1906
+
1907
+ for (let item of arr) { // ok — item: Mut<Obj>
1908
+ item.mutMethod(); // ok — изменения попадают в arr
1909
+ arr.push(obj4); // ошибка — arr заимствован во время итерации
1910
+ }
1911
+ ```
1912
+
1913
+ Примитивы — `item` всегда копируется независимо от `let`/`const`:
1914
+
1915
+ ```typescript
1916
+ let nums = [1, 2, 3];
1917
+ for (let item of nums) {
1918
+ item++; // warning: изменение копии не имеет эффекта
1919
+ }
1920
+
1921
+ // чтобы изменить элементы — используй индекс:
1922
+ for (let i = 0; i < nums.length; i++) {
1923
+ nums[i]++; // ok
1924
+ }
1925
+ ```
1926
+
1927
+ #### while / do-while
1928
+
1929
+ ```typescript
1930
+ // while — проверка условия до итерации
1931
+ let i = 0;
1932
+ while (i < 10) {
1933
+ console.log(i);
1934
+ i++;
1935
+ }
1936
+
1937
+ // do-while — проверка условия после итерации (тело выполняется минимум 1 раз)
1938
+ let input: string;
1939
+ do {
1940
+ input = readLine();
1941
+ } while (input === "");
1942
+
1943
+ // break и continue работают как в JS
1944
+ while (true) {
1945
+ const line = readLine();
1946
+ if (line === "quit") break;
1947
+ if (line === "") continue;
1948
+ process(line);
1949
+ }
1950
+ ```
1951
+
1952
+ - `break` — выход из цикла
1953
+ - `continue` — переход к следующей итерации
1954
+ - Labeled break/continue для вложенных циклов:
1955
+
1956
+ ```typescript
1957
+ outer: while (true) {
1958
+ while (true) {
1959
+ if (done) break outer; // выход из внешнего цикла
1960
+ if (skip) continue outer; // следующая итерация внешнего цикла
1961
+ }
1962
+ }
1963
+ ```
1964
+
1965
+ ##### async/await в циклах
1966
+
1967
+ `await` разрешён внутри любого цикла (`for`, `for-of`, `while`, `do-while`) при условии что функция `async`. Итерации выполняются **последовательно** — следующая итерация начинается только после завершения `await`.
1968
+
1969
+ ```typescript
1970
+ // for-of
1971
+ async function processAll(ids: i32[]): void {
1972
+ for (const id of ids) {
1973
+ const user = await fetchUser(id); // ждём каждый запрос по очереди
1974
+ console.log(user.name);
1975
+ }
1976
+ }
1977
+
1978
+ // while
1979
+ async function pollUntilReady(id: i32): Status {
1980
+ while (true) {
1981
+ const status = await checkStatus(id);
1982
+ if (status !== Status.Pending) return status;
1983
+ await delay(500);
1984
+ }
1985
+ }
1986
+
1987
+ // для параллельного выполнения — Promise.all
1988
+ async function processAllParallel(ids: i32[]): void {
1989
+ const users = await Promise.all(ids.map(id => fetchUser(id))); // все запросы параллельно
1990
+ }
1991
+ ```
1992
+
1993
+ #### switch / case
1994
+
1995
+ Синтаксис как в JS/TS. **Implicit fallthrough запрещён** — забытый `break` или `return` это ошибка компилятора.
1996
+
1997
+ ```typescript
1998
+ switch (status) {
1999
+ case 200:
2000
+ handleOk();
2001
+ break;
2002
+ case 404:
2003
+ handleNotFound();
2004
+ break;
2005
+ case 500:
2006
+ case 503: // группировка case — ok (оба ведут к одному телу)
2007
+ handleError();
2008
+ break;
2009
+ default:
2010
+ handleUnknown();
2011
+ }
2012
+ ```
2013
+
2014
+ - `break` или `return` обязательны в каждом `case` — иначе ошибка компилятора
2015
+ - Группировка пустых `case` (`case 500: case 503:`) разрешена
2016
+ - `default` необязателен, но компилятор выдаёт warning если не покрыты все значения enum
2017
+ - Switch работает на: числовых типах, `string`, `boolean`, enum
2018
+
2019
+ #### match
2020
+
2021
+ > Синтаксис соответствует [TC39 Pattern Matching proposal](https://github.com/tc39/proposal-pattern-matching) и ожидаемому TypeScript 7.
2022
+
2023
+ Expression-based pattern matching. Возвращает значение, exhaustiveness проверяется компилятором.
2024
+
2025
+ ```typescript
2026
+ // литералы
2027
+ const label = match (x) {
2028
+ 0 => "zero",
2029
+ 1..10 => "small",
2030
+ 11..100 => "medium",
2031
+ _ => "large",
2032
+ };
2033
+
2034
+ // null
2035
+ const msg = match (user) {
2036
+ null => "not found",
2037
+ _ => `Hello, ${user.name}`,
2038
+ };
2039
+
2040
+ // enum
2041
+ const desc = match (direction) {
2042
+ Direction.North => "вверх",
2043
+ Direction.South => "вниз",
2044
+ Direction.East => "вправо",
2045
+ Direction.West => "влево",
2046
+ // _ не нужен — компилятор проверяет полноту
2047
+ };
2048
+
2049
+ // деструктуризация struct / class
2050
+ const area = match (shape) {
2051
+ { kind: "circle", r } => Math.PI * r * r,
2052
+ { kind: "rect", w, h } => w * h,
2053
+ };
2054
+
2055
+ // несколько паттернов для одной ветки
2056
+ const sign = match (n) {
2057
+ 0 => "zero",
2058
+ 1 | 2 | 3 => "small positive",
2059
+ _ => "other",
2060
+ };
2061
+ ```
2062
+
2063
+ **Правила match:**
2064
+
2065
+ - `_` — wildcard, совпадает с чем угодно; обязателен если паттерны не исчерпывающие
2066
+ - Паттерны проверяются сверху вниз, срабатывает первый совпавший
2067
+ - Exhaustiveness: если компилятор видит что все случаи покрыты (enum, null + non-null) — `_` не нужен; если не покрыты — ошибка компилятора
2068
+ - `|` — несколько паттернов для одной ветки
2069
+ - Диапазон `a..b` — от `a` включительно до `b` не включительно (как везде в TSC)
2070
+ - Деструктуризация в паттерне — те же правила borrow что и в обычной деструктуризации (только borrow)
2071
+
2072
+ **match vs switch:**
2073
+
2074
+ | | `switch` | `match` |
2075
+ |---|---|---|
2076
+ | Тип | statement | expression (возвращает значение) |
2077
+ | Exhaustiveness | warning | ошибка компилятора |
2078
+ | Паттерны | только равенство | литералы, диапазоны, деструктуризация, `\|` |
2079
+ | Fallthrough | запрещён | нет (каждая ветка — отдельное выражение) |
2080
+
2081
+ #### Доступ к полям и деструктуризация
2082
+
2083
+ ##### Доступ к полю — borrow по умолчанию
2084
+
2085
+ Обращение к полю сложного типа без аннотации возвращает `Ref`:
2086
+
2087
+ ```typescript
2088
+ const user = new User("Alice", [1, 2, 3]);
2089
+
2090
+ const name = user.name; // Ref<string> — borrow, user жив
2091
+ const age = user.age; // i32 — copy (примитив)
2092
+
2093
+ console.log(user); // ok — user не тронут
2094
+ console.log(user.name); // ok
2095
+ ```
2096
+
2097
+ Чтобы переместить поле — явная аннотация типа владельца:
2098
+
2099
+ ```typescript
2100
+ const name: string = user.name; // string (T) — move
2101
+ console.log(user.name); // ошибка: поле перемещено
2102
+ console.log(user.age); // ok — остальные поля живы
2103
+ console.log(user); // ошибка: нельзя использовать user целиком после move поля
2104
+ ```
2105
+
2106
+ ##### Деструктуризация — сахар для borrow-доступа к полям
2107
+
2108
+ Деструктуризация **всегда** делает borrow для сложных типов и copy для примитивов. Move через деструктуризацию невозможен.
2109
+
2110
+ ```typescript
2111
+ const { name, age } = user;
2112
+ // эквивалентно:
2113
+ // const name = user.name; → Ref<string> (borrow, не move)
2114
+ // const age = user.age; → i32 (copy)
2115
+ ```
2116
+
2117
+ `user` остаётся жив после деструктуризации:
2118
+
2119
+ ```typescript
2120
+ const user = new User("Alice", 30, [1, 2, 3]);
2121
+ const { name, age, scores } = user;
2122
+ // name: Ref<string>, age: i32, scores: Ref<i32[]>
2123
+
2124
+ console.log(user); // ok — ничего не перемещено
2125
+ console.log(name); // ok
2126
+ console.log(scores); // ok
2127
+ ```
2128
+
2129
+ Для move — только явное присвоение с аннотацией типа (не деструктуризация):
2130
+
2131
+ ```typescript
2132
+ const scores: i32[] = user.scores; // move — явная аннотация
2133
+ const name = user.name; // Ref<string> — borrow
2134
+ console.log(user.scores); // ошибка: поле перемещено
2135
+ console.log(user.name); // ok
2136
+ ```
2137
+
2138
+ #### Срезы
2139
+
2140
+ По умолчанию срез — borrow (`Ref`), исходный массив остаётся жив. Явная аннотация типа даёт owned копию:
2141
+
2142
+ ```typescript
2143
+ const arr = [1, 2, 3, 4, 5];
2144
+
2145
+ const s = arr[1..3]; // Ref<i32[]> — borrow, arr жив
2146
+ const s: i32[] = arr[1..3]; // i32[] — owned копия [2, 3]
2147
+ ```
2148
+
2149
+ Borrow-срез блокирует мутацию источника пока жив:
2150
+
2151
+ ```typescript
2152
+ let arr = [1, 2, 3, 4, 5];
2153
+ const s = arr[1..3]; // Ref — arr заимствован
2154
+ arr.push(6); // ошибка: arr заимствован
2155
+ ```
2156
+
2157
+ Отрицательные индексы и открытые срезы:
2158
+
2159
+ ```typescript
2160
+ const last = arr[-1]; // последний элемент (copy — примитив)
2161
+ const tail = arr[1..]; // Ref<i32[]> — с 1 до конца
2162
+ const init = arr[..-1]; // Ref<i32[]> — всё кроме последнего
2163
+ const last2 = arr[-2..]; // Ref<i32[]> — последние два
2164
+ ```
2165
+
2166
+ #### Move из массива по индексу
2167
+
2168
+ ```typescript
2169
+ let ref: User;
2170
+ {
2171
+ const users = [user1, user2, user3];
2172
+ ref = users[0]; // попытка move из массива
2173
+ } // users умирает → ref dangling
2174
+ ```
2175
+
2176
+ ```
2177
+ error: cannot move out of array by index
2178
+ --> main.tsc:4
2179
+ hint: use users.remove(0) to take ownership
2180
+ ```
2181
+
2182
+ Исправление:
2183
+
2184
+ ```typescript
2185
+ let ref: User;
2186
+ {
2187
+ let users = [user1, user2, user3];
2188
+ ref = users.remove(0); // move с удалением — ok
2189
+ }
2190
+ ```
2191
+
2192
+ #### Мутация коллекции при активном borrow
2193
+
2194
+ Borrow элемента = borrow коллекции.
2195
+
2196
+ ```typescript
2197
+ let users = [user1, user2, user3];
2198
+ let u: Ref<User> = users[0]; // borrow на users
2199
+ users.push(user4); // ошибка: mut на заимствованном
2200
+ ```
2201
+
2202
+ #### Возврат borrow из метода
2203
+
2204
+ Возвращаемый `Ref<T>`/`Mut<T>` неявно привязан к `this`:
2205
+
2206
+ ```typescript
2207
+ class Config {
2208
+ data: string[];
2209
+
2210
+ getFirst(): Ref<string> {
2211
+ return this.data[0]; // привязан к this
2212
+ }
2213
+ }
2214
+
2215
+ const config = new Config();
2216
+ const s = config.getFirst(); // ok — s привязан к config
2217
+ console.log(s); // ok
2218
+ ```
2219
+
2220
+ ```typescript
2221
+ let s: Ref<string>;
2222
+ {
2223
+ const config = new Config();
2224
+ s = config.getFirst(); // borrow привязан к config
2225
+ } // config умер
2226
+ console.log(s); // ошибка: config умер, s dangling
2227
+ ```
2228
+
2229
+ #### Borrows в полях класса — запрещено
2230
+
2231
+ ```typescript
2232
+ class View {
2233
+ data: Ref<User[]>; // ошибка: нельзя хранить borrow в поле
2234
+ }
2235
+ ```
2236
+
2237
+ Альтернативы:
2238
+
2239
+ ```typescript
2240
+ // Владеем данными
2241
+ class View {
2242
+ data: User[]; // owned
2243
+ }
2244
+
2245
+ // Или Shared
2246
+ class View {
2247
+ data: Shared<User[]>; // ARC
2248
+ }
2249
+
2250
+ // Временный доступ — через параметр метода
2251
+ function renderView(data: Ref<User[]>) { ... }
2252
+ ```
2253
+
2254
+ #### Замыкания
2255
+
2256
+ По умолчанию захватывают сложные типы по `Ref`:
2257
+
2258
+ ```typescript
2259
+ const items = [1, 2, 3];
2260
+ const fn = (): i32 => items.length; // fn держит Ref<items>
2261
+ fn(); // ok — items жив
2262
+ ```
2263
+
2264
+ ```typescript
2265
+ let fn: () => i32;
2266
+ {
2267
+ const items = [1, 2, 3];
2268
+ fn = (): i32 => items.length; // захватывает Ref<items>
2269
+ }
2270
+ fn(); // ошибка: items мёртв
2271
+ ```
2272
+
2273
+ Для явного управления захватом используется список `[var: Type]` перед параметрами — те же типы что везде:
2274
+
2275
+ ```typescript
2276
+ [data: Data]() // T — move, замыкание становится владельцем
2277
+ [data: Ref<Data>]() // Ref — immutable borrow (явно, то же что по умолчанию)
2278
+ [data: Mut<Data>]() // Mut — mutable borrow
2279
+ ```
2280
+
2281
+ Move-захват решает проблему когда замыкание переживает источник:
2282
+
2283
+ ```typescript
2284
+ // ошибка без явного захвата — Ref не может пережить функцию
2285
+ function makeGreeter(): () => void {
2286
+ const name = "Alice";
2287
+ return (): void => console.log(name); // ошибка: name умрёт
2288
+ }
2289
+
2290
+ // ok — name перемещён в замыкание, живёт пока живёт замыкание
2291
+ function makeGreeter(): () => void {
2292
+ const name = "Alice";
2293
+ return [name: string](): void => console.log(name); // ok
2294
+ }
2295
+ ```
2296
+
2297
+ ```typescript
2298
+ // Mut-захват — замыкание мутирует внешний объект
2299
+ let counter = new Counter();
2300
+ const inc = [counter: Mut<Counter>]() => counter.increment();
2301
+ inc();
2302
+ inc();
2303
+ ```
2304
+
2305
+ #### Spread оператор
2306
+
2307
+ Spread **потребляет** источник — move. Работает для массивов и объектов. Spread на `const` — ошибка компилятора. **Исключение**: `const Shared<T>` — разрешено, это retain, не move.
2308
+
2309
+ **Массивы:**
2310
+ ```typescript
2311
+ let admins = [admin1, admin2];
2312
+ const users = [...admins, ...guests]; // ok — move из let
2313
+ sendEmail(admins); // ошибка: admins перемещён
2314
+
2315
+ const admins = [admin1, admin2];
2316
+ const users = [...admins];
2317
+ // ошибка: cannot spread const
2318
+ // hint: use let, Shared<T>, or [...admins.clone()] if Admin implements Clone
2319
+ ```
2320
+
2321
+ **Объекты:**
2322
+ ```typescript
2323
+ let base = { x: 1, name: "Alice" };
2324
+ const extended = { ...base, extra: 42 }; // ok — move из let
2325
+ console.log(base); // ошибка: base перемещён
2326
+
2327
+ const base = { x: 1, name: "Alice" };
2328
+ const extended = { ...base, extra: 42 };
2329
+ // ошибка: cannot spread const
2330
+ // hint: use let, Shared<T>, or { ...base.clone(), extra: 42 } if type implements Clone
2331
+ ```
2332
+
2333
+ **`Shared<T>` — const разрешён** (retain, не move):
2334
+
2335
+ ```typescript
2336
+ const base: Shared<Item[]> = [item1, item2];
2337
+ const listA = [...base, itemA]; // ok — retain
2338
+ const listB = [...base, itemB]; // ok — retain
2339
+
2340
+ const obj: Shared<Config> = { x: 1 };
2341
+ const a = { ...obj, y: 2 }; // ok — retain
2342
+ const b = { ...obj, z: 3 }; // ok — retain
2343
+ ```
2344
+
2345
+ ### Compiler Architecture
2346
+
2347
+ #### Фазы компиляции
2348
+
2349
+ ```
2350
+ Parse → AST → Typecheck → Lower to IR → Ownership Analysis → Codegen
2351
+ ↑ ↑
2352
+ Flatten CFG Borrow checker / ARC injection
2353
+ ```
2354
+
2355
+ #### IR (Intermediate Representation)
2356
+
2357
+ IR — linear представление между AST и C. Flattens вложенность, делает порядок выполнения явным.
2358
+
2359
+ **Операции:**
2360
+
2361
+ | Операция | Описание |
2362
+ |----------|----------|
2363
+ | `alloc x, value` | Создать переменную, владелец |
2364
+ | `borrow x, source, imm/mut` | Заимствовать (`Ref`/`Mut`) |
2365
+ | `retain x` | Увеличить refcount (`Shared`) |
2366
+ | `release x` | Уменьшить refcount |
2367
+ | `call fn, args` | Вызов функции |
2368
+ | `assign x, value` | Присвоение |
2369
+ | `drop x` | Конец жизни переменной |
2370
+ | `return value` | Возврат |
2371
+ | `branch cond, label1, label2` | Условный переход |
2372
+ | `jump label` | Безусловный переход |
2373
+
2374
+ **Пример трансформации:**
2375
+
2376
+ TypeScript:
2377
+ ```typescript
2378
+ let users = [user1, user2, user3]
2379
+ const first = users[0]
2380
+ push(users, user4)
2381
+ ```
2382
+
2383
+ IR:
2384
+ ```
2385
+ alloc users, [user1, user2, user3]
2386
+ borrow first, users[0], imm // first = Ref<User>
2387
+ call push, [users, user4] // ← ошибка: users заимствован
2388
+ drop first
2389
+ drop users
2390
+ ```
2391
+
2392
+ **Почему IR:**
2393
+
2394
+ - Явный порядок операций (не как в AST)
2395
+ - Простые проверки для borrow checker
2396
+ - Легко вставлять `retain`/`release` для `Shared<T>`
2397
+ - Почти 1:1 с C — кодоген тривиальный
2398
+
2399
+ ### Module System
2400
+
2401
+ - Синтаксис как в TypeScript: именованные `export` / `import { } from ""`
2402
+ - Один файл = один модуль
2403
+ - `export default` — отсутствует, только именованные экспорты
2404
+ - **Циклические импорты разрешены** — компилятор автоматически генерирует forward declarations в C
2405
+
2406
+ #### Export
2407
+
2408
+ ```typescript
2409
+ export class User { ... }
2410
+ export interface Drawable { ... }
2411
+ export type UserId = i32;
2412
+ export type Nullable<T> = T | null;
2413
+ export function helper(): void { ... }
2414
+ export const MAX: i32 = 100;
2415
+
2416
+ // реэкспорт
2417
+ export { User, helper } from "./user";
2418
+ ```
2419
+
2420
+ #### Import
2421
+
2422
+ ```typescript
2423
+ // обычный импорт — runtime, генерирует #include "user.h" в C
2424
+ import { User, createUser } from "./user";
2425
+
2426
+ // type-only импорт — только compile-time, генерирует forward declaration в C
2427
+ import type { UserId, Drawable } from "./user";
2428
+ ```
2429
+
2430
+ `import type` важен для кодогена — позволяет избежать лишних `#include` в C:
2431
+ ```c
2432
+ // import { User } → в .c файле:
2433
+ #include "user.h"
2434
+
2435
+ // import type { UserId } → в .h файле:
2436
+ typedef int32_t UserId; // или forward declaration
2437
+ ```
2438
+
2439
+ #### Порядок инициализации модулей
2440
+
2441
+ Каждый модуль с module-level переменными получает `_init()` функцию в C. Порядок вызовов определяется **топологической сортировкой** графа импортов — зависимости инициализируются раньше.
2442
+
2443
+ Для правильного порядка компилятор строит граф зависимостей и делает топологическую сортировку. Результат — одна функция `tsc_init_all()` с правильным порядком:
2444
+
2445
+ ```c
2446
+ // сгенерировано компилятором
2447
+ static void tsc_init_all() {
2448
+ a_type_init(); // нет зависимостей — первый
2449
+ bar_init(); // зависит от a_type
2450
+ foo_init(); // зависит от a_type и bar
2451
+ }
2452
+
2453
+ int main() {
2454
+ tsc_init_all();
2455
+ // ... код пользователя
2456
+ }
2457
+ ```
2458
+
2459
+ Два случая циклических зависимостей:
2460
+
2461
+ - **Цикл через типы и функции** — разрешён, компилятор генерирует forward declarations в .h файлах
2462
+ - **Цикл через module-level переменные** — физически неразрешимо, ошибка компилятора:
2463
+ ```
2464
+ error: circular initialization dependency detected
2465
+ src/a.tsc:2 aValue depends on bValue
2466
+ src/b.tsc:2 bValue depends on aValue
2467
+ hint: move one of these values into a function
2468
+ ```
2469
+ Пример в коде:
2470
+ ```typescript
2471
+ a.tsc: const aVal = bFunc() // нужен b
2472
+ b.tsc: const bVal = aFunc() // нужен a
2473
+ // кто инициализируется первым?
2474
+ ```
2475
+
2476
+ #### Точка входа
2477
+
2478
+ Поле `"main"` в `tsc.packages.json` указывает главный файл проекта. Компилятор определяет тип проекта по содержимому файла:
2479
+
2480
+ - Есть top-level statements → **executable**, компилятор генерирует `main()` в C
2481
+ - Только `export` декларации → **library**, `main()` не генерируется
2482
+
2483
+ ```json
2484
+ {
2485
+ "name": "myapp",
2486
+ "main": "src/index.tsc"
2487
+ }
2488
+ ```
2489
+
2490
+ ```json
2491
+ {
2492
+ "name": "myapp",
2493
+ "builds": [
2494
+ { "name": "server", "main": "src/server.tsc" },
2495
+ { "name": "cli", "main": "src/cli.tsc" }
2496
+ ]
2497
+ }
2498
+ ```
2499
+
2500
+ `"main"` внутри build config переопределяет верхний уровень для конкретного билда.
2501
+
2502
+ **Без `tsc.packages.json`** (одиночный скрипт):
2503
+ - Один файл с top-level кодом → он entry point: `tsclang build hello.tsc`
2504
+ - Несколько файлов с top-level кодом → ошибка компилятора
2505
+
2506
+ **Правила:**
2507
+
2508
+ | Ситуация | Результат |
2509
+ |---------|-----------|
2510
+ | `"main"` + top-level код в файле | executable |
2511
+ | `"main"` + только exports | library |
2512
+ | Нет конфига + один файл с top-level кодом | он entry point |
2513
+ | Нет конфига + несколько файлов с top-level кодом | ошибка компилятора |
2514
+
2515
+ **Ошибки:**
2516
+
2517
+ - `"main"` не указан, несколько файлов с top-level кодом:
2518
+ ```
2519
+ error: multiple files with top-level statements, entry point is ambiguous
2520
+ hint: add "main" to tsc.packages.json
2521
+ ```
2522
+
2523
+ - `"main"` указан, файл не существует:
2524
+ ```
2525
+ error: main file not found: src/index.tsc
2526
+ ```
2527
+
2528
+ - Типы импортов по источнику:
2529
+ - `"./path"` — локальный файл
2530
+ - `"libc"`, `"libm"` и др. — встроенные декларации + генерирует `#include <...>` в C
2531
+ ```typescript
2532
+ import { printf } from "libc";
2533
+ // компилятор знает сигнатуру printf — есть встроенный libc.d.tsc
2534
+ // генерирует в C: #include <stdio.h>
2535
+ ```
2536
+ - остальное — внешние пакеты из реестра
2537
+ - **Файлы деклараций `.d.tsc`** — типизация внешнего кода:
2538
+ - Для C-библиотек без встроенных деклараций
2539
+ - Для `.tsc` модулей без исходников (бинарные пакеты)
2540
+ - Сообщество публикует `.d.tsc` для популярных C-либ в реестре
2541
+ - **Если деклараций нет** — тип `any`, компилятор не ругается
2542
+
2543
+ ### Build System & Package Manager
2544
+
2545
+ #### Build Profiles
2546
+
2547
+ Именованные профили сборки в `tsc.packages.json`:
2548
+
2549
+ ```json
2550
+ {
2551
+ "builds": {
2552
+ "desktop": {},
2553
+ "avr": {
2554
+ "target": "avr",
2555
+ "mcu": "atmega328p",
2556
+ "defaultNumber": "f32"
2557
+ },
2558
+ "release": {
2559
+ "optimize": "O2"
2560
+ }
2561
+ }
2562
+ }
2563
+ ```
2564
+
2565
+ #### Поля build конфига
2566
+
2567
+ | Поле | Описание | Дефолт |
2568
+ |------|----------|--------|
2569
+ | `"name"` | имя билда | обязательно |
2570
+ | `"main"` | entry point файл (override верхнего уровня) | наследует |
2571
+ | `"emit"` | тип вывода: `"c"`, `"binary"`, `"hex"`, `"lib"` | `"binary"` для desktop, `"hex"` для embedded |
2572
+ | `"outDir"` | директория вывода | `./build/<name>` |
2573
+ | `"target"` | целевая платформа | текущая платформа |
2574
+ | `"mcu"` | модель MCU (только для embedded) | — |
2575
+ | `"optimize"` | уровень оптимизации (`O0`..`O3`, `Os`) | `O0` |
2576
+ | `"defaultNumber"` | тип для `number` | `f64` |
2577
+
2578
+ #### Pipeline сборки
2579
+
2580
+ ```
2581
+ src/*.tsc → <outDir>/c/*.c + CMakeLists.txt → <outDir>/myapp (или .hex)
2582
+ ↑ ↑
2583
+ tsclang build (transpile) cmake + gcc/avr-gcc
2584
+ ```
2585
+
2586
+ Структура `outDir`:
2587
+ ```
2588
+ build/desktop/
2589
+ c/ ← сгенерированные .c и .h
2590
+ CMakeLists.txt
2591
+ myapp ← бинарь (emit: binary)
2592
+
2593
+ build/avr/
2594
+ c/
2595
+ CMakeLists.txt
2596
+ myapp.hex ← (emit: hex)
2597
+ ```
2598
+
2599
+ #### CLI команды
2600
+
2601
+ ```bash
2602
+ tsclang init # создать новый проект
2603
+ tsclang build # собрать проект
2604
+ tsclang install # установить зависимости из tsc.packages.json
2605
+ tsclang update # обновить зависимости, пересоздать lock-файл
2606
+ tsclang clean # удалить build артефакты (outDir)
2607
+ tsclang run # собрать дефолтный build + запустить бинарь
2608
+ ```
2609
+
2610
+ - Если build не указан — используется `"desktop"` или первый в списке
2611
+ - Параметры build переопределяют дефолтные настройки компилятора
2612
+
2613
+ #### `tsclang install` vs `tsclang update`
2614
+
2615
+ | | `tsclang install` | `tsclang update` |
2616
+ |---|---|---|
2617
+ | Lock-файл существует | использует точные версии из lock | игнорирует lock, ищет новые версии |
2618
+ | Lock-файл отсутствует | резолвит по constraints, создаёт lock | то же |
2619
+ | Результат | воспроизводимая установка | обновлённый lock-файл |
2620
+
2621
+ #### `tsclang update` подробно
2622
+
2623
+ Поведение по типу зависимости:
2624
+
2625
+ | Тип | Поведение |
2626
+ |-----|-----------|
2627
+ | semver `^1.0.0` | обновляет до последней версии в рамках constraint |
2628
+ | git `@main` (ветка) | pull latest commit, обновляет lock |
2629
+ | git `@1.0.0` (тег) | зафиксирован — пропускает, выводит предупреждение |
2630
+ | git `@a1b2c3d` (коммит) | зафиксирован — пропускает, выводит предупреждение |
2631
+ | url | нет реестра — пропускает, выводит предупреждение |
2632
+
2633
+ ```bash
2634
+ tsclang update # обновить всё что можно
2635
+ tsclang update <dep> # обновить конкретную зависимость
2636
+ tsclang update sdl2 # обновить только sdl2
2637
+ tsclang update sdl2 json # обновить несколько
2638
+ ```
2639
+
2640
+ После `tsclang update` необходимо запустить `tsclang install` для применения изменений.
2641
+
2642
+ #### `tsclang build` подробно
2643
+
2644
+ ```bash
2645
+ tsclang build # собрать дефолтный build
2646
+ tsclang build <name> # собрать конкретный build
2647
+ tsclang build hello.tsc # одиночный файл → binary
2648
+
2649
+ # флаги (override конфига)
2650
+ tsclang build --emit c # только генерация C
2651
+ tsclang build --emit binary # C + компиляция в бинарь
2652
+ tsclang build --emit hex # C + avr-gcc → .hex
2653
+ tsclang build --outDir ./dist # переопределить outDir
2654
+ ```
2655
+
2656
+ - Если build не указан — используется `"desktop"` или первый в списке
2657
+ - Параметры build переопределяют дефолтные настройки компилятора
2658
+
2659
+ #### `tsclang run` подробно
2660
+
2661
+ ```bash
2662
+ tsclang run # собрать дефолтный build + запустить бинарь
2663
+ tsclang run <name> # собрать конкретный build + запустить бинарь
2664
+ tsclang run -- --foo bar # передать аргументы в запускаемый бинарь
2665
+ ```
2666
+
2667
+ `tsclang run` = `tsclang build` + запуск скомпилированного бинаря. Только для `emit: "binary"`.
2668
+
2669
+ ```
2670
+ tsclang run
2671
+
2672
+ ├─ 1. tsclang build ← компилирует .tsc → .c → бинарь
2673
+ └─ 2. exec <outDir>/myapp ← запускает бинарь, stdout/stderr в терминал
2674
+ ```
2675
+
2676
+ - Если `emit` не `"binary"` — ошибка: `error: tsclang run requires emit: "binary"`
2677
+ - Код выхода бинаря пробрасывается как код выхода `tsclang run`
2678
+ - Аргументы после `--` передаются напрямую в бинарь:
2679
+ ```bash
2680
+ tsclang run -- --port 8080 --verbose
2681
+ # запускает: ./build/desktop/myapp --port 8080 --verbose
2682
+ ```
2683
+
2684
+ #### `tsclang init` подробно
2685
+
2686
+ ```bash
2687
+ tsclang init # создать проект в текущей директории
2688
+ tsclang init myapp # создать проект в новой директории myapp
2689
+ ```
2690
+
2691
+ `tsclang init` создаёт:
2692
+
2693
+ ```
2694
+ myapp/
2695
+ src/
2696
+ index.tsc
2697
+ tsc.packages.json
2698
+ ```
2699
+
2700
+ Минимальный `tsc.packages.json`:
2701
+
2702
+ ```json
2703
+ {
2704
+ "name": "myapp",
2705
+ "version": "1.0.0",
2706
+ "main": "src/index.tsc",
2707
+ "builds": [
2708
+ { "name": "desktop", "emit": "binary", "outDir": "build/desktop" }
2709
+ ]
2710
+ }
2711
+ ```
2712
+
2713
+ #### Быстрый старт
2714
+
2715
+ ```bash
2716
+ npm install -g tsclang # установить компилятор
2717
+ tsclang init myapp # создать проект
2718
+ cd myapp
2719
+ tsclang install # установить зависимости
2720
+ tsclang run # собрать и запустить
2721
+ ```
2722
+
2723
+ #### Источники зависимостей (все варианты вместе)
2724
+
2725
+ ```json
2726
+ {
2727
+ "dependencies": {
2728
+ "mylib": "^1.0.0",
2729
+ "sdl2": ">=2.28.0",
2730
+ "json": {
2731
+ "git": "github.com/nlohmann/json@3.11.0"
2732
+ },
2733
+ "libfoo": {
2734
+ "git": "github.com/someuser/libfoo@1.0.0",
2735
+ "build": "make PREFIX={install_dir}",
2736
+ "headers": "include/",
2737
+ "lib": "libfoo.a"
2738
+ },
2739
+ "libbaz": {
2740
+ "url": "https://some.site.com/download/lib_1.0.0.zip",
2741
+ "version": "1.0.0",
2742
+ "build": "make PREFIX={install_dir}",
2743
+ "headers": "include/",
2744
+ "lib": "libbaz.a"
2745
+ }
2746
+ }
2747
+ }
2748
+ ```
2749
+
2750
+ #### Версионирование
2751
+
2752
+ - **Semver строка** — полный semver: `^1.0.0`, `~1.2.0`, `>=1.0.0`, `1.0.0`
2753
+ - **Git** — только точный тег (`@2.28.0`), коммит (`@a1b2c3d`), или ветка (`@main`); semver операторы не поддерживаются
2754
+ - **URL** — версия задаётся обязательным полем `version:` (используется для кэша и lock-файла)
2755
+
2756
+ #### Резолюция semver-зависимостей
2757
+
2758
+ Для зависимостей заданных строкой компилятор ищет в следующем порядке:
2759
+
2760
+ 1. **Система** — `pkg-config` проверяет наличие и версию
2761
+ - Найдена и версия удовлетворяет constraint → используем, ничего не скачиваем
2762
+ - Не найдена или версия не подходит → переходим к шагу 2
2763
+ 2. **Реестр** (`tsc-lang.org`) — скачивает и собирает нужную версию
2764
+ - _(реестр не реализован)_ → ошибка компилятора с подсказкой:
2765
+ ```
2766
+ error: sdl2 >=2.28.0 not found
2767
+ hint: install it manually, e.g.:
2768
+ apt install libsdl2-dev
2769
+ brew install sdl2
2770
+ ```
2771
+
2772
+ #### URL-зависимости (zip-архив)
2773
+
2774
+ - Поле `url:` — прямая ссылка на `.zip` архив
2775
+ - Поле `version:` — **обязательно**, используется для именования кэша и lock-файла
2776
+ - Поддерживаемые форматы архивов: `.zip`, `.tar.gz`, `.tar.bz2`, `.tar.xz`
2777
+ - Flow:
2778
+
2779
+ ```bash
2780
+ # 1. Скачивает архив
2781
+ curl -L https://some.site.com/download/lib_1.0.0.zip \
2782
+ -o ~/.tsc/cache/libbaz@1.0.0.zip
2783
+
2784
+ # 2. Распаковывает
2785
+ unzip ~/.tsc/cache/libbaz@1.0.0.zip -d ~/.tsc/cache/libbaz@1.0.0/
2786
+ ```
2787
+
2788
+ - Дальше — тот же порядок инструкций что и для git:
2789
+ 1. **CMake** — есть `CMakeLists.txt` → auto-flow
2790
+ 2. **`tsc.build.json`** — есть в архиве → используем
2791
+ 3. **inline в `tsc.packages.json`** — описываем сами
2792
+ 4. Ничего → ошибка компилятора
2793
+ - В lock-файле фиксируется URL + `sha256` архива для воспроизводимости
2794
+
2795
+ #### Git-зависимости
2796
+
2797
+ - Версия по тегу (`@2.28.0`), ветке (`@main`) или коммиту (`@a1b2c3d4`)
2798
+ - Lock-файл `tsc.packages.lock` — фиксирует точные коммиты для воспроизводимости
2799
+ - Сборка скачанной либы — приоритет поиска инструкций:
2800
+ 1. **CMake** — есть `CMakeLists.txt` в репо → поддерживается автоматически
2801
+ 2. **`tsc.build.json`** — есть в репо библиотеки → используем его
2802
+ 3. **inline в `tsc.packages.json`** — описываем сборку прямо в своём проекте
2803
+ 4. Ничего из вышеперечисленного → ошибка компилятора
2804
+ - `tsc.build.json` в корне репо библиотеки (удобство для авторов либ, чтобы пользователи не описывали сборку вручную):
2805
+ ```json
2806
+ {
2807
+ "build": "make PREFIX={install_dir}",
2808
+ "headers": "include/",
2809
+ "lib": "libfoo.a"
2810
+ }
2811
+ ```
2812
+
2813
+ ##### CMake auto-flow
2814
+
2815
+ Когда в репо есть `CMakeLists.txt`, компилятор запускает стандартный cmake pipeline:
2816
+
2817
+ ```bash
2818
+ # 1. Клонирует репо в кэш
2819
+ git clone github.com/someuser/libfoo@1.0.0 ~/.tsc/cache/libfoo@1.0.0
2820
+
2821
+ # 2. Конфигурирует — cmake_options из tsc.packages.json пробрасываются как -D флаги
2822
+ cmake -S ~/.tsc/cache/libfoo@1.0.0 \
2823
+ -B ~/.tsc/cache/libfoo@1.0.0/_build \
2824
+ -DCMAKE_INSTALL_PREFIX=~/.tsc/cache/libfoo@1.0.0/_install \
2825
+ -DBUILD_SHARED_LIBS=OFF \
2826
+ -DCMAKE_BUILD_TYPE=Release \
2827
+ -DFOO_BUILD_TESTS=OFF \ # ← из cmake_options
2828
+ -DFOO_USE_SSL=ON # ← из cmake_options
2829
+
2830
+ # 3. Собирает
2831
+ cmake --build ~/.tsc/cache/libfoo@1.0.0/_build --parallel
2832
+
2833
+ # 4. Устанавливает в _install/
2834
+ cmake --install ~/.tsc/cache/libfoo@1.0.0/_build
2835
+ ```
2836
+
2837
+ После install — стандартная структура:
2838
+
2839
+ ```
2840
+ _install/
2841
+ include/ ← headers
2842
+ lib/ ← libfoo.a
2843
+ lib/cmake/ ← FooConfig.cmake (если есть)
2844
+ ```
2845
+
2846
+ Линковка в генерируемый `CMakeLists.txt` проекта — два варианта:
2847
+
2848
+ ```cmake
2849
+ # Вариант A: есть FooConfig.cmake / foo-config.cmake → используем find_package
2850
+ find_package(Foo REQUIRED
2851
+ PATHS ~/.tsc/cache/libfoo@1.0.0/_install
2852
+ NO_DEFAULT_PATH)
2853
+ target_link_libraries(myapp PRIVATE Foo::Foo)
2854
+
2855
+ # Вариант B: config-файла нет → прописываем пути напрямую
2856
+ target_include_directories(myapp PRIVATE ~/.tsc/cache/libfoo@1.0.0/_install/include)
2857
+ target_link_libraries(myapp PRIVATE ~/.tsc/cache/libfoo@1.0.0/_install/lib/libfoo.a)
2858
+ ```
2859
+
2860
+ ##### cmake_options в tsc.packages.json
2861
+
2862
+ Опциональное поле для передачи `-D` флагов при конфигурации:
2863
+
2864
+ ```json
2865
+ {
2866
+ "dependencies": {
2867
+ "libfoo": {
2868
+ "git": "github.com/someuser/libfoo@1.0.0",
2869
+ "cmake_options": {
2870
+ "FOO_BUILD_TESTS": false,
2871
+ "FOO_USE_SSL": true,
2872
+ "FOO_MAX_CONNECTIONS": 128
2873
+ }
2874
+ }
2875
+ }
2876
+ }
2877
+ ```
2878
+
2879
+ - `boolean` → `ON` / `OFF`
2880
+ - `number` / `string` → передаётся как есть
2881
+ - Компилятор всегда добавляет `BUILD_SHARED_LIBS=OFF`, `CMAKE_BUILD_TYPE=Release`, `CMAKE_INSTALL_PREFIX` — пользователь не переопределяет эти три
2882
+
2883
+ ##### Flow сборки для tsc.build.json / inline
2884
+
2885
+ ```bash
2886
+ # Запускает сборку, подставляет {install_dir}
2887
+ make PREFIX=~/.tsc/cache/libfoo@1.0.0/out
2888
+ # Забирает результат по путям из инструкций
2889
+ # headers: include/ → ~/.tsc/cache/libfoo@1.0.0/include/
2890
+ # lib: libfoo.a → ~/.tsc/cache/libfoo@1.0.0/libfoo.a
2891
+ # Прописывает пути в генерируемый CMakeLists.txt проекта
2892
+ target_include_directories(myapp PRIVATE ~/.tsc/cache/libfoo@1.0.0/include)
2893
+ target_link_libraries(myapp ~/.tsc/cache/libfoo@1.0.0/libfoo.a)
2894
+ ```
2895
+
2896
+ #### Реестр
2897
+
2898
+ - Централизованный реестр `tsc-lang.org`
2899
+ - Публикация `.tsc` пакетов и `.d.tsc` деклараций для C-либ
2900
+
2901
+ ### Error Handling
2902
+
2903
+ #### Принцип
2904
+
2905
+ Синтаксис как в TypeScript (`throw`, `try`/`catch`/`finally`), но под капотом компилируется в **Result-структуры в C** — без `setjmp`/`longjmp`. Это даёт:
2906
+
2907
+ - **Zero-cost**: нет сохранения регистров на каждом `try`-блоке
2908
+ - **Безопасный C interop**: нет `longjmp` через сторонний C-код
2909
+ - **Корректный ownership**: обычный control flow, компилятор знает все owned переменные
2910
+
2911
+ #### Объявление функции с ошибками
2912
+
2913
+ Функция объявляет `throws` в сигнатуре. Компилятор может вывести `throws` автоматически, если внутри есть `throw`, но явное объявление является документацией:
2914
+
2915
+ ```typescript
2916
+ function readFile(path: string): string throws IOError { ... }
2917
+ function fetch(url: string): Response throws IOError | NetworkError { ... }
2918
+ ```
2919
+
2920
+ Без `throws` — функция не может содержать `throw` (ошибка компилятора).
2921
+
2922
+ #### throw
2923
+
2924
+ Бросается экземпляр класса:
2925
+
2926
+ ```typescript
2927
+ class IOError {
2928
+ message: string;
2929
+ constructor(msg: string) { this.message = msg; }
2930
+ }
2931
+
2932
+ function readFile(path: string): string throws IOError {
2933
+ if (!exists(path)) {
2934
+ throw new IOError(`file not found: ${path}`);
2935
+ }
2936
+ return read(path);
2937
+ }
2938
+ ```
2939
+
2940
+ #### try / catch / finally
2941
+
2942
+ ```typescript
2943
+ try {
2944
+ const content = readFile("data.txt");
2945
+ console.log(content);
2946
+ } catch (e: IOError) {
2947
+ console.log(e.message);
2948
+ } finally {
2949
+ cleanup(); // выполняется всегда
2950
+ }
2951
+ ```
2952
+
2953
+ Несколько `catch`-блоков — диспатч по типу:
2954
+
2955
+ ```typescript
2956
+ try {
2957
+ const r = fetch("https://...");
2958
+ process(r);
2959
+ } catch (e: IOError) {
2960
+ console.log("IO:", e.message);
2961
+ } catch (e: NetworkError) {
2962
+ console.log("Network:", e.message);
2963
+ } finally {
2964
+ closeConnection();
2965
+ }
2966
+ ```
2967
+
2968
+ Union catch — обработка нескольких типов в одном блоке:
2969
+
2970
+ ```typescript
2971
+ try {
2972
+ fetch("https://...");
2973
+ } catch (e: IOError | NetworkError) {
2974
+ console.log("error:", e.message); // тип e = IOError | NetworkError
2975
+ }
2976
+ ```
2977
+
2978
+ #### Union errors
2979
+
2980
+ Функция может бросать несколько типов ошибок:
2981
+
2982
+ ```typescript
2983
+ function process(path: string): Response throws IOError | NetworkError {
2984
+ const content = readFile(path); // throws IOError
2985
+ return fetch(content); // throws NetworkError
2986
+ }
2987
+ ```
2988
+
2989
+ Компилятор объединяет `throws`-типы автоматически при вызове функций внутри тела.
2990
+
2991
+ #### Оператор `?` — propagate
2992
+
2993
+ `expr?` — если функция вернула ошибку, немедленно вернуть её из текущей функции. Текущая функция обязана иметь совместимый `throws`:
2994
+
2995
+ ```typescript
2996
+ function process(path: string): string throws IOError | NetworkError {
2997
+ const content = readFile(path)?; // propagate IOError
2998
+ const r = fetch(content)?; // propagate NetworkError
2999
+ return r.body;
3000
+ }
3001
+ ```
3002
+
3003
+ Несовместимый `throws` — ошибка компилятора:
3004
+
3005
+ ```typescript
3006
+ function main(): void {
3007
+ const data = readFile("x")?;
3008
+ // ошибка: main не объявляет throws, нельзя использовать ?
3009
+ }
3010
+ ```
3011
+
3012
+ #### Оператор `!` — unwrap или panic
3013
+
3014
+ `expr!` — если функция вернула ошибку, вызвать `abort()` (runtime panic). Не требует `throws` у текущей функции:
3015
+
3016
+ ```typescript
3017
+ function main(): void {
3018
+ const content = readFile("config.txt")!; // panic если ошибка
3019
+ console.log(content);
3020
+ }
3021
+ ```
3022
+
3023
+ #### C-output
3024
+
3025
+ `throws` меняет C-сигнатуру функции: возвращаемый тип оборачивается в Result-структуру. Для `throws IOError | NetworkError`:
3026
+
3027
+ ```c
3028
+ // Генерируется компилятором
3029
+ typedef enum { _ERR_IO, _ERR_NETWORK } _fetch_err_kind;
3030
+
3031
+ typedef struct {
3032
+ bool ok;
3033
+ union {
3034
+ Response value;
3035
+ struct {
3036
+ _fetch_err_kind _kind;
3037
+ union {
3038
+ IOError io;
3039
+ NetworkError net;
3040
+ } _err;
3041
+ };
3042
+ };
3043
+ } _Result_Response_IOError_NetworkError;
3044
+
3045
+ _Result_Response_IOError_NetworkError fetch(String url) { ... }
3046
+ ```
3047
+
3048
+ `try/catch` компилируется в `if/else` по полю `ok` и `_kind`:
3049
+
3050
+ ```c
3051
+ _Result_Response_IOError_NetworkError _r = fetch(str("https://..."));
3052
+ if (_r.ok) {
3053
+ Response r = _r.value;
3054
+ process(r);
3055
+ Response_free(r);
3056
+ } else if (_r._kind == _ERR_IO) {
3057
+ IOError e = _r._err.io;
3058
+ printf("IO: %s\n", e.message.data);
3059
+ IOError_free(e);
3060
+ } else if (_r._kind == _ERR_NETWORK) {
3061
+ NetworkError e = _r._err.net;
3062
+ printf("Network: %s\n", e.message.data);
3063
+ NetworkError_free(e);
3064
+ }
3065
+ // finally
3066
+ closeConnection();
3067
+ ```
3068
+
3069
+ Оператор `?`:
3070
+ ```c
3071
+ _Result_String_IOError _r = readFile(str("x"));
3072
+ if (!_r.ok) return (_Result_String_NetworkError){ .ok = false, ._err = ... };
3073
+ String content = _r.value;
3074
+ ```
3075
+
3076
+ Оператор `!`:
3077
+ ```c
3078
+ _Result_String_IOError _r = readFile(str("config.txt"));
3079
+ if (!_r.ok) { fprintf(stderr, "panic\n"); abort(); }
3080
+ String content = _r.value;
3081
+ ```
3082
+
3083
+ #### Ownership при ошибках
3084
+
3085
+ Компилятор отслеживает все owned переменные в `try`-блоке. Если выбрасывается ошибка, все уже инициализированные owned переменные корректно освобождаются через обычный control flow — никаких специальных механизмов не нужно, так как это просто `if/else` в C:
3086
+
3087
+ ```typescript
3088
+ function process(): void throws IOError {
3089
+ const a = new Foo(); // owned
3090
+ const b = new Bar(); // owned
3091
+ riskyOp()?; // если ошибка → a и b освобождаются в else-ветке
3092
+ use(a, b);
3093
+ }
3094
+ ```
3095
+
3096
+ Генерируется:
3097
+ ```c
3098
+ // try-ветка
3099
+ Foo* a = Foo_new();
3100
+ Bar* b = Bar_new();
3101
+ _Result_void_IOError _r = riskyOp();
3102
+ if (!_r.ok) {
3103
+ Foo_free(a); // компилятор генерирует cleanup
3104
+ Bar_free(b);
3105
+ return (_Result_void_IOError){ .ok = false, ._err = _r._err };
3106
+ }
3107
+ use(a, b);
3108
+ Foo_free(a);
3109
+ Bar_free(b);
3110
+ ```
3111
+
3112
+ #### Ограничения
3113
+
3114
+ - `throw` запрещён в функциях без `throws` — ошибка компилятора
3115
+ - `?` запрещён в функции без `throws` — ошибка компилятора
3116
+ - Исключения нельзя бросать через C interop границы — функции, объявленные как `extern "C"`, не могут содержать `throws`
3117
+ - `finally` не может содержать `throw` или `return` — ошибка компилятора (неопределённое поведение)
3118
+
3119
+ ### Concurrency
3120
+
3121
+ #### Уровни модели
3122
+
3123
+ TSC разделяет конкурентность на три независимых механизма:
3124
+
3125
+ | Механизм | Платформа | Уровень |
3126
+ |----------|-----------|---------|
3127
+ | `async/await` | все | стандартный |
3128
+ | `std/thread` | OS (desktop/server) | продвинутый |
3129
+ | `@interrupt` | embedded (AVR/Cortex) | системный |
3130
+
3131
+ ---
3132
+
3133
+ #### 1. Async/Await — стандартный способ
3134
+
3135
+ Единственный event loop, один поток исполнения. `Shared<T>` и `Weak<T>` **не атомарны** — никаких накладных расходов. Narrowing через `if (x != null)` безопасен — между проверкой и использованием никакой другой код не выполняется.
3136
+
3137
+ ```typescript
3138
+ async function fetchUser(id: i32): User throws NetworkError {
3139
+ const conn = await connect("https://api.example.com");
3140
+ const data = await conn.get(`/users/${id}`);
3141
+ return User.parse(data);
3142
+ }
3143
+
3144
+ async function main(): void {
3145
+ const user = await fetchUser(42);
3146
+ console.log(user.name);
3147
+ }
3148
+ ```
3149
+
3150
+ На **embedded** `async fn` компилируется в state machine в C — без runtime, без heap:
3151
+
3152
+ ```c
3153
+ // async fn → конечный автомат
3154
+ typedef struct { int _state; /* захваченные переменные */ } FetchUserTask;
3155
+ bool FetchUserTask_poll(FetchUserTask* t) { switch (t->_state) { ... } }
3156
+ ```
3157
+
3158
+ ##### Promise<T>
3159
+
3160
+ Тип возвращаемого значения `async` функции — `Promise<T>`. Обе записи эквивалентны:
3161
+
3162
+ ```typescript
3163
+ async function fetchUser(id: i32): User { ... } // компилятор выводит Promise<User>
3164
+ async function fetchUser(id: i32): Promise<User> { ... } // то же самое явно
3165
+ ```
3166
+
3167
+ Создать `Promise<T>` вручную (для оборачивания callback-based API):
3168
+
3169
+ ```typescript
3170
+ function delay(ms: i32): Promise<void> {
3171
+ return new Promise((resolve, reject) => {
3172
+ setTimeout(() => resolve(), ms);
3173
+ });
3174
+ }
3175
+
3176
+ function readFile(path: string): Promise<string> {
3177
+ return new Promise((resolve, reject) => {
3178
+ if (!fileExists(path)) reject(new IOError("not found"));
3179
+ else resolve(fs.readSync(path));
3180
+ });
3181
+ }
3182
+ ```
3183
+
3184
+ - `resolve(value)` — завершает Promise успехом, передаёт значение
3185
+ - `reject(error)` — завершает Promise ошибкой; тип ошибки должен совпадать с `throws`
3186
+ - Вызов `resolve` или `reject` после первого вызова — no-op
3187
+
3188
+ С error handling:
3189
+
3190
+ ```typescript
3191
+ async function fetch(url: string): string throws NetworkError {
3192
+ return new Promise((resolve, reject) => {
3193
+ httpGet(url, (err, data) => {
3194
+ if (err) reject(new NetworkError(err));
3195
+ else resolve(data);
3196
+ });
3197
+ });
3198
+ }
3199
+ ```
3200
+
3201
+ ##### Promise.all
3202
+
3203
+ Запуск нескольких async задач параллельно:
3204
+
3205
+ ```typescript
3206
+ const [users, posts] = await Promise.all([
3207
+ fetchUsers(), // Promise<User[]>
3208
+ fetchPosts(), // Promise<Post[]>
3209
+ ]);
3210
+
3211
+ // с error handling — если любая задача бросает, вся группа бросает
3212
+ const [a, b, c] = await Promise.all([taskA(), taskB(), taskC()]);
3213
+ ```
3214
+
3215
+ - Все задачи запускаются одновременно, ждём завершения всех
3216
+ - Если любая задача завершается ошибкой — `Promise.all` бросает эту ошибку, остальные отменяются
3217
+ - Типы элементов выводятся компилятором из переданных Promise
3218
+
3219
+ ##### Правила await
3220
+
3221
+ - `await` только внутри `async` функции — иначе ошибка компилятора
3222
+ - `await` только на `Promise<T>` — `await` на обычном значении ошибка компилятора
3223
+
3224
+ ```typescript
3225
+ // ✅ ok
3226
+ async function foo(): i32 {
3227
+ return await bar(); // bar(): Promise<i32>
3228
+ }
3229
+
3230
+ // ❌ await вне async функции
3231
+ function bad(): void {
3232
+ await foo(); // error: await outside async function
3233
+ }
3234
+
3235
+ // ❌ await на не-Promise
3236
+ async function bad2(): void {
3237
+ const x: i32 = 42;
3238
+ await x; // error: cannot await i32, expected Promise<T>
3239
+ }
3240
+ ```
3241
+
3242
+ ##### async main
3243
+
3244
+ Entry point может быть `async` — компилятор запускает event loop автоматически:
3245
+
3246
+ ```typescript
3247
+ async function main(): void {
3248
+ const user = await fetchUser(42);
3249
+ console.log(user.name);
3250
+ }
3251
+ ```
3252
+
3253
+ На desktop/server — стандартный event loop (libuv или аналог).
3254
+ На embedded — poll loop, скомпилированный в state machine без heap.
3255
+
3256
+ ---
3257
+
3258
+ #### 2. Threads (std/thread) — продвинутый уровень
3259
+
3260
+ Только там где есть OS. Потоки работают как **изоляты** — без общей памяти. Связь исключительно через каналы с передачей владения.
3261
+
3262
+ ```typescript
3263
+ import { Thread, channel } from "std/thread";
3264
+
3265
+ async function main(): void {
3266
+ const [tx, rx] = channel<i32[]>();
3267
+
3268
+ const t = Thread.spawn(() => {
3269
+ // тяжёлые вычисления в отдельном потоке
3270
+ const result = heavyComputation();
3271
+ tx.send(result); // move — передача владения через канал
3272
+ });
3273
+
3274
+ const result = await rx.recv(); // ждём результат из async-кода
3275
+ t.join();
3276
+ console.log(result);
3277
+ }
3278
+ ```
3279
+
3280
+ **Правила передачи в Thread.spawn:**
3281
+
3282
+ | Тип | Разрешено | Поведение |
3283
+ |-----|-----------|-----------|
3284
+ | Owned `T` | ✅ | неявный move |
3285
+ | Примитив | ✅ | copy |
3286
+ | `Ref<T>` / `Mut<T>` | ❌ | ошибка компилятора |
3287
+ | `Shared<T>` / `Weak<T>` | ❌ | ошибка компилятора |
3288
+ | `Readonly<T>` | ➡️ | v2 (не реализовано) |
3289
+
3290
+ **Global State в контексте потоков:**
3291
+
3292
+ ```typescript
3293
+ const CONFIG = { maxRetries: 3 }; // const — ok, читать из потоков можно
3294
+ let counter = 0; // ошибка компилятора если Thread.spawn захватывает
3295
+ const ac = new Atomic<i32>(0); // Atomic<T> — ok из потоков
3296
+
3297
+ class Server {
3298
+ static count: i32 = 0; // mutable static — ошибка при захвате в Thread.spawn
3299
+ static readonly MAX: i32 = 100; // const static — ok
3300
+ }
3301
+ ```
3302
+
3303
+ Компилятор проверяет захваченные переменные **на границе `Thread.spawn`**:
3304
+ - Мутабельный `let` или глобаль → ошибка компилятора
3305
+ - `Shared<T>` или `Weak<T>` → ошибка компилятора
3306
+ - Owned `T` → неявный move
3307
+ - Примитив → copy
3308
+
3309
+ ---
3310
+
3311
+ #### 3. @interrupt — только Embedded
3312
+
3313
+ ISR — аппаратное прерывание. Не поток, не closure. Никакого захвата контекста, только работа с `@volatile` регистрами и `Atomic<T>`:
3314
+
3315
+ ```typescript
3316
+ @interrupt("TIMER0_OVF")
3317
+ function onTimer(): void {
3318
+ PORTB ^= 0x01; // toggle LED — только hardware registers
3319
+ }
3320
+
3321
+ @interrupt("USART_RX")
3322
+ function onReceive(): void {
3323
+ const byte = UDR0; // volatile register read
3324
+ rxBuffer.push_atomic(byte); // Atomic<T> — ok
3325
+ }
3326
+ ```
3327
+
3328
+ - Доступ к обычным переменным программы — ошибка компилятора
3329
+ - Доступ к `Shared<T>`, `Ref<T>`, owned объектам — ошибка компилятора
3330
+ - Доступ к `@volatile` и `Atomic<T>` — разрешён
3331
+
3332
+ `std/thread` на embedded targets — ошибка компилятора (нет OS).
3333
+
3334
+ ---
3335
+
3336
+ #### Итоговая картина
3337
+
3338
+ ```
3339
+ ┌─────────────────────────────────────────────────────┐
3340
+ │ TSC Concurrency Model │
3341
+ │ │
3342
+ │ async/await ──── event loop ──── все платформы │
3343
+ │ │ │
3344
+ │ └── Shared<T>/Weak<T> не атомарны │
3345
+ │ └── Weak narrowing безопасен │
3346
+ │ │
3347
+ │ std/thread ───── isolates ────── OS only │
3348
+ │ │ │
3349
+ │ └── только owned/primitive через каналы │
3350
+ │ └── компилятор проверяет на Thread.spawn │
3351
+ │ │
3352
+ │ @interrupt ────── ISR ─────────── embedded only │
3353
+ │ │ │
3354
+ │ └── только @volatile + Atomic<T> │
3355
+ │ └── нет захвата контекста │
3356
+ └─────────────────────────────────────────────────────┘
3357
+ ```
3358
+
3359
+ ### Standard Library
3360
+
3361
+ > TBD
3362
+
3363
+ ---
3364
+
3365
+ ## Open Questions
3366
+
3367
+ - **Стандартная библиотека — что входит:**
3368
+ - `std/io` — консоль, файлы
3369
+ - `std/fs` — файловая система
3370
+ - `std/net` — сеть, HTTP
3371
+ - `std/math` — математика
3372
+ - `std/string` — утилиты строк
3373
+ - `std/collections` — дополнительные коллекции
3374
+ - `std/thread` — потоки (уже есть)
3375
+ - `std/env` — переменные окружения, аргументы
3376
+ - `std/time` — работа со временем
3377
+ - **Concurrency (детальное обсуждение):**
3378
+ - Async/await runtime — libuv или своя event loop реализация?
3379
+ - `Atomic<T>` — какие операции? (`load`, `store`, `fetch_add`, CAS?)
3380
+ - `channel<T>` — bounded vs unbounded? backpressure?
3381
+ - `@interrupt` — как именно объявляются `@volatile` регистры?
3382
+ - `Readonly<T>` (v2) — глубокая иммутабельность для передачи между потоками
3383
+ - Structured concurrency — нужны ли `TaskGroup` / `withTaskGroup`?
3384
+ - Отмена задач — `AbortSignal` или другой механизм?
3385
+
3386
+ ---
3387
+
3388
+ ## Roadmap
3389
+
3390
+ ### Этап 0: Подготовка
3391
+
3392
+ Дальше используется термин `Кодовая база` - это файл, в котором описывается:
3393
+ - `реализация` на JS, включая методы
3394
+ - `реализация` на C, вплоть до каждого метода
3395
+ - `реализация` на TSC, вплоть до каждого метода
3396
+
3397
+ Для каждой `реализации`:
3398
+ - пример кода
3399
+ - результат, который код должен выдавать
3400
+ - пример кода с ошибкой
3401
+ - какую ошибку должен выдавать
3402
+ - пример исправления ошибки
3403
+ - результат, который должен быть после исправления
3404
+
3405
+ Примерная структура документации к проекту:
3406
+
3407
+ ```
3408
+ doc/
3409
+ index.md
3410
+ 1_перегрузка_функций/
3411
+ 1_1_перегрузка_по_типам.md
3412
+ 1_2_перегрузка_по_количеству.md
3413
+ ```
3414
+
3415
+ Примерная структура файла `index.md`:
3416
+
3417
+ ```
3418
+ # Раздел 1. Перегрузка функций
3419
+
3420
+ ## Правило 1. Перегрузка по типам.
3421
+ ## Правило 2. Перегрузка по количеству.
3422
+
3423
+ ...
3424
+ ```
3425
+
3426
+ Примерная структура файла `1_1_перегрузка_по_типам.md`:
3427
+
3428
+ ```
3429
+ # Раздел 1. Перегрузка функций
3430
+
3431
+ ## Правило 1
3432
+
3433
+ Перегрузка по типам.
3434
+
3435
+ Код на typescript
3436
+
3437
+ ```typescript
3438
+ ...
3439
+ ```
3440
+
3441
+ Код на c
3442
+
3443
+ ```c
3444
+ ...
3445
+ ```
3446
+
3447
+ Код на tsc
3448
+
3449
+ ```
3450
+ ...
3451
+ ```
3452
+
3453
+ Результат выполнения
3454
+
3455
+ ```
3456
+ ...
3457
+ ```
3458
+
3459
+ Ошибка, если перегрузка будет содержать необязательный параметр
3460
+
3461
+ Код на tsc
3462
+
3463
+ ```
3464
+ ...
3465
+ ```
3466
+
3467
+ Вывод с подсказкой
3468
+
3469
+ ```
3470
+ ...
3471
+ ```
3472
+
3473
+ Чтобы исправить, нужно сделать ...
3474
+
3475
+ Код на tsc
3476
+
3477
+ ```
3478
+ ...
3479
+ ```
3480
+
3481
+ Результат выполнения
3482
+
3483
+ ```
3484
+ ...
3485
+ ```
3486
+
3487
+ - [ ] Создать каталог `doc` в котором будет хранится документация по проекту
3488
+ - [ ] Создать внутри файл `index.md`
3489
+ - [ ] Разбить весь концепт на ключевые разделы
3490
+ - [ ] Для каждого раздела создать свой каталог в `doc`
3491
+ - [ ] Записать раздел в файл `index.md`
3492
+ - [ ] Разделы разбить на правила и операторы
3493
+ - [ ] Каждое правило и оператор записать в каталог раздела, в файл вида `[номер_раздела]_[название_раздела].md`
3494
+ - [ ] Собрать под каждое правило и оператор кодовую базу и записать в тот же файл
3495
+
3496
+ ### Этап 1: Инфраструктура
3497
+ - [ ] Парсер (свой или swc)
3498
+ - [ ] AST → IR lowering
3499
+ - [ ] ScopeManager: ALIVE, MOVED, DROPPED
3500
+
3501
+ ### Этап 2: Кодогенерация структур
3502
+ - [ ] class/interface → struct в C
3503
+ - [ ] `_free` деструкторы
3504
+
3505
+ ### Этап 3: Ownership анализ
3506
+ - [ ] Move при присваивании
3507
+ - [ ] Вставка `free()` в конце блоков
3508
+ - [ ] Эпилог cleanup
3509
+
3510
+ ### Этап 4: Borrow Checker
3511
+ - [ ] `Ref<T>` / `Mut<T>` проверки
3512
+ - [ ] Scope Constraint
3513
+
3514
+ ### Этап 5: ARC
3515
+ - [ ] Runtime: `RC_retain` / `RC_release`
3516
+ - [ ] `Shared<T>` тип
3517
+ - [ ] `Weak<T>` тип — weak ref, optional chaining при доступе