[2026-09-17] eagle: personal/projects/zont-config-compiler.md
This commit is contained in:
@@ -100,14 +100,24 @@ related:
|
||||
⚠️ **Whole-float (`1.0`) остаётся float** — иначе энкодер напечатает `1` вместо `1.0`.
|
||||
5. Раскладывает объекты по секциям. Вложенное прячет под родителя: тип 52 → внутрь своего 51.
|
||||
6. `system_settings` (`#S…`) — **в начало файла**.
|
||||
7. **Свип орфанов** (`_is_body_inline`): любой объект 47/48/49/50/59 и **любой объект 46** засевается
|
||||
в `referenced`; если его id не встречается в теле сценария, он уезжает в `scenario_orphans` с `raw`.
|
||||
🔴 Поднимая id шага куда-либо (напр. в `trigger.step_id`), **сразу** добавить ключ в `_is_body_inline`,
|
||||
иначе шаг падает в орфаны (§21.3, питфолл 40).
|
||||
|
||||
### `yml-to-config.py` — YAML → TXT
|
||||
|
||||
1. Загружает **YAML (UTF-8)**, собирает строки `#Z<id>=…`, в порядке типов.
|
||||
2. Формат: числа без кавычек, строки в `'…'`, bool → `0/1`, пустая строка → `''`, списки → `[…]`.
|
||||
3. Разворачивает вложенное: тип 52 → отдельными строками после своего устройства.
|
||||
4. `validate_config()` проверяет структуру **до вывода**. Ошибки → `❌ VALIDATION ERRORS`, **exit 4**, файл не отдаётся.
|
||||
5. Вывод: **windows-1251**, переводы строк **CRLF**.
|
||||
4. **Сборка trigger-сценария:** из `trigger:` вынимается `step_id` (+ `flag`), шаг `46` собирается
|
||||
обратно, `steps` уходит в его `then`.
|
||||
5. `validate_config()` проверяет структуру **до вывода**. Ошибки → `❌ VALIDATION ERRORS`, **exit 4**, файл не отдаётся.
|
||||
6. Вывод: **windows-1251**, переводы строк **CRLF**.
|
||||
|
||||
> 🔴 **Правило двусторонней правки:** любая правка декодера **требует** парной правки энкодера, иначе
|
||||
> круг даёт потерю объектов или мусорный `raw` (питфоллы 72, 81). Гонять круг **только** после того,
|
||||
> как обе стороны готовы, — одиночная правка почти всегда красная.
|
||||
|
||||
### Коды возврата
|
||||
|
||||
@@ -222,14 +232,14 @@ python3 test_roundtrip.py zont_config/config_X.txt
|
||||
| Тип | Роль | Формат строки | В YAML |
|
||||
|---|---|---|---|
|
||||
| `11` | сценарий | `[11, name, [step_ids], days, time, f5, interval_ms, 0]` | `scenarios` |
|
||||
| `46` | шаг | `[46, flag, cond_id, [then_ids], [else_ids]]` | инлайн в `steps[]` |
|
||||
| `46` | шаг | `[46, flag, cond_id, [then_ids], [else_ids]]` | **если условие поднято в `trigger:`** — шаг в `steps` не пишется, его id едет в `trigger.step_id`, `steps` = тела действий (§21.3) ‖ иначе инлайн в `steps[]` |
|
||||
| `49` | условие | `[49, object, f3, value]` — f3=`1` событие ‖ f3=`0` параметр | `trigger` / `steps[].if` / `set_var` |
|
||||
| `45` | пауза | `[45, ms]` | `wait: ms` |
|
||||
| `47` | сравнение | `[47, op, left, value|right]` | `{op, left, value}` |
|
||||
| `48` | группа | `[48, logic, [ids]]` | `{group, children}` |
|
||||
| `59` | мини-скрипт | `[59, '<код>', arg1, arg2, flag]` | `{descr, args[, set_var]}` |
|
||||
| — | **тела 59** | `set var<N>` → `set_var` ‖ `puts` ‖ `storeev I/A` ‖ `objcmd` (+`target`/`value`) | §5.10 |
|
||||
| — | **тело цели `set_var`** | `49` → `type: condition` + `event`/`param`; `50` → `time_condition`/`days_mask` | §10.1 / §10.5 / §10.6 |
|
||||
| `59` | мини-скрипт | `[59, '<код>', поле2, поле4, поле5]` | `set_var` / `log` / `storeenv` / `pickle` / `descr`+`args`+`target` (`objcmd`) |
|
||||
| — | **тела 59** | все разобраны (круг 38): `set var` → `set_var` ‖ `puts` → `log` ‖ `storeev` → `storeenv` ‖ `objcmd` → `descr`+`args`+`target` ‖ `expr`/`pickle`/`pickle_value`/`objstate`/`var` | неразобранных нет |
|
||||
| — | **тело цели `set_var`** | `49` → `type: param` + `event`/`param`; `50` → `time_condition`/`days_mask` | §10.1 / §10.5 / §10.6 |
|
||||
| `5` | действие над выходом | `[5, '<descr>', output_ref, value, …]` | `{descr, target, value, params?}` |
|
||||
| `9` | команда (реле/контур/режим) | `[9, '<descr>', target, '<value>']` | `{descr, target, value}` |
|
||||
|
||||
@@ -237,6 +247,8 @@ python3 test_roundtrip.py zont_config/config_X.txt
|
||||
**Логика type 48:** `0`=И, `1`=ИЛИ, `2`=НЕ.
|
||||
**Тип 50**: `109 = 0b1101101` = пн, ср, чт, сб, вс. Бит 0 = ПН.
|
||||
**Поле 1 записи 46** (`#Z8862=46,1,…`) → ключ `flag`, пишется только если ≠ 0. Семантика **неизвестна** (1 из 69).
|
||||
🔴 **Поле 5 записи 59 = признак литерала** — «в предыдущем поле число, а не id объекта»: `1` у
|
||||
`set`/`objcmd`, `2` у `expr`. **Производное**, в YAML не пишется, энкодер ставит сам (§21.2.1).
|
||||
|
||||
---
|
||||
|
||||
@@ -254,8 +266,8 @@ python3 test_roundtrip.py zont_config/config_X.txt
|
||||
| `'storeev I "<текст>"'` | `storeenv: {level: info, text: …}` | `I`→info, `A`→alert, `W`→warning, `E`→error |
|
||||
| `'puts "<текст>"'` | `log: <текст>` | плоский ключ, без `args` |
|
||||
| `'3'` (голый литерал) | `pickle: 3` | **числом**, не строкой (подтверждено Alex) |
|
||||
| `'objcmd <id> "<fmt>"'` | ⏳ `descr` + `args` | форма вызова **не согласована** — см. §13 |
|
||||
| `'expr "%0 + %1"'` | `expr: '%0 + %1'` + `args: [операнды]` | операнды — id объектов |
|
||||
| `'objcmd <id> "<fmt>"'` | `descr` + `args` + **`target: <id>`** | ✅ **РАЗОБРАН** (круг 38): `10029`/`10033`/`10036`/`10053`. `args` остаётся **только** у `objcmd` |
|
||||
| `'expr "%0 + %1"'` | `type: expr` + `op` + `left`/`right` | операнды — телами, не голыми id (§18) |
|
||||
| `'objstate <id> 0 0'` | `type: objstate` + `object` | |
|
||||
|
||||
### 5.1. Trigger-сценарий
|
||||
@@ -664,6 +676,11 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z<id>=<тип>'
|
||||
| 34 | 🔴 **Признак формы — по составу ключей, а не по `type`** | Энкодер проверял `sv.get('type') == 'var'`, но декодер для «цель — объект 59» ставит `var:` + `id:`, **без `type`** → падение `set_var needs 'value', 'id' or 'target'` (§16.3) |
|
||||
| 35 | ⚠️ **Мёртвая ветка после сужения условий маскирует поведение** | Остался недостижимый `if 'value' in sv: return […, sv.get('value', 0), …]` (выше уже отсечён `type: var` и `_script_value_row`). Читается как живой код со старым ключом. Сносить (§16.3, питфолл 69) |
|
||||
| 36 | 🔴 **`patch` со слиянием строк → `SyntaxError`** | Патч, где `new_string` кончается без перевода строки, склеил `return […]` со следующим `row = …` → `invalid syntax (line 717)`. После каждой правки смотреть `lint.status`, а не только `success: true` |
|
||||
| 37 | 🔴 **«В коммите только это» = приказ откатывать — но откат не в пустоту** | На «откатывай нахуй значит и делай как просил» я сделал `git revert` и вернул **отвергнутую** форму. Откат требовался как снос коммита, а целевая форма была **новой**. Сперва назвать таблицей, что откат уничтожит, потом дать выбор `revert` ‖ `reset --hard 0cbbeb8`. Перед откатом — `git stash push -m` (`checkout --` затирает незакоммиченное без возврата), §21.1 |
|
||||
| 38 | 🔴 **Прямой ответ на заданный вопрос = ЦЕЛЕВОЕ СОСТОЯНИЕ, не толковать** | Спросил «что должно стоять вместо `- id: 10099`», ответ был «unresolved - конец сценария». Я переинтерпретировал это как «флаг снят верно, делать нечего» и вернул пустую строку → шесть раундов мата («не беси меня» · «мозг не еби» · «ты конченый» · «КОНЕЦ СУКА СЦЕНАРИЯ»). Ответ на вопрос **не переспрашивать и не переинтерпретировать**, §21.1 |
|
||||
| 39 | 🔴 **Один вопрос про форму — потом править ВСЕ точки сразу** | Четыре круга на форму `steps` триггер-сценария: каждая попытка правила одно место, ломала предыдущее, откатывалась. Правильно: выяснить форму **одним вопросом**, затем править декодер + `_is_body_inline` + энкодер в одном заходе, и только потом круг, §21.3 |
|
||||
| 40 | 🔴 **Зелёный круг ≠ приёмка — проверять и артефакт** | Круг был 🟢 при **70 орфанах**, потому что энкодер молча собирал `raw` обратно. Критерий приёмки: круг 9/9 **И** число орфанов **И** отсутствие `raw`, §21.3 |
|
||||
| 41 | 🔴 **«Остаток полей» может быть ПРОИЗВОДНЫМ, а не хвостом** | `args: [0, 1]` у `#Z10069` рождался трижды: `0` = поле 4 (всегда ноль), `1` = поле 5. Поле 5 — **признак литерала** («в поле 2 число, а не id»): `1` у `set`/`objcmd`, `2` у `expr`. Все 7 записей с полем 5 ≠ 0 — ровно те, где поле 4 не id. Прежде чем именовать «остаток» — проверить, не восстанавливается ли он из разобранных полей, §21.2.1 |
|
||||
|
||||
### 7.2. Код — парсер/энкодер
|
||||
|
||||
@@ -2297,17 +2314,18 @@ comm -13 <(sort orig) <(sort back) # → пусто (ничего лишнег
|
||||
| # | Вопрос | Где | Почему ждёт |
|
||||
|---|---|---|---|
|
||||
| 1 | ✅ **ЗАКРЫТ** — имя ключа для Pickle-литерала: `pickle: 3` (числом). Внесено в код | `9933` | — |
|
||||
| 2 | форма для **Pickle-вызова**: `objcmd <id> "<fmt>"` — ключ `pickle: 'objcmd 8700 "1 %0"'` (строкой) или своё? | `9925`, `9929`, `9931`, `9948` | форма дана только для литерала |
|
||||
| 3 | раскрывать ли **тип 50** прямо в шаге (`#Z8548` → `days: [mon,wed,fri,sat]`)? | `8548` | правка в коде есть, но путь `trigger:` её минует |
|
||||
| 4 | «завершить сценарий» (тип 11 без объекта) — оставить `unresolved: true` или дать имя (`end`)? | `9986`, `9990`, `8601` | в конфиге объекта нет |
|
||||
| 5 | удалять ли секцию `scenario_orphans` (21 запись — разделяемые операнды)? | низ YAML | требует правки энкодера, риск `#Z9964` |
|
||||
| 6 | ✅ **ЗАКРЫТ** — круг 30 закоммичен (`375d01a`). Push — отдельной командой | оба конвертера | — |
|
||||
| 2 | ✅ **ЗАКРЫТ** (круг 38) — форма `objcmd`: `descr` + `args` + `target: <id>`. `args` живёт **только** здесь | `10029`/`10033`/`10036`/`10053` | — |
|
||||
| 3 | ✅ **ЗАКРЫТ** (круг 36) — тип 50 в `trigger:` раскрыт: `type: days_mask` + `days: [mon, wed, thu, sat, sun]` | `8548` | — |
|
||||
| 4 | ✅ **ЗАКРЫТ** (круг 37) — имя дано: **`end: true`** («unresolved - конец сценария»), `unresolved` снят | `10099`, `10103`, `8601` | — |
|
||||
| 5 | ✅ **ЗАКРЫТ** (круги 38/39) — `scenario_orphans` разгребена: **6 → 4**, `raw` в ней **0**. Оставшиеся 4 не привязаны ни к одному сценарию | низ YAML | — |
|
||||
| 6 | ✅ **ЗАКРЫТ** — круг 30 закоммичен (`375d01a`). Push сделан (круг 40) | оба конвертера | — |
|
||||
| 7 | ✅ **ЗАКРЫТ** — `const` → **`pickle_value`** (круг 31, §10.12) | `10068` | — |
|
||||
| 8 | ✅ **ЗАКРЫТ** — операнды `expr` раскрываются телами + `op:` отдельным ключом (круг 31, §10.12) | `10077…10085` | — |
|
||||
| 9 | ⚠️ **ЧАСТИЧНО** — хвост полей 3..n = ключ **`args`** (круг 32, §15.2), `flag`/`kind`/`tail` снесены. Но имя `args` **не подтверждено Alex** — ждёт | `10069` | — |
|
||||
| 10 | что значит **поле 4 = `2`** у expr-записи (`0` = второй операнд объект, `2` = литерал)? своего имени нет — в YAML не выводится | `10077…10085` | ✅ энкодер собирает (`0 if isinstance(right_raw, dict) else 2`), но имя полю не дано |
|
||||
| 9 | ⛔ **ОТМЕНЁН** (круг 40, `4b99d8e`) — `args` у `set_var` признан **выдумкой**: `0` = поле 4, `1` = поле 5 (**производное**). Энкодер ставит поле 5 сам (§21.2.1) | `10069` | — |
|
||||
| 10 | ⚠️ **ОТКРЫТ** — что значит **поле 4 = `2`** у expr-записи (`0` = второй операнд объект, `2` = литерал)? своего имени нет — в YAML не выводится | `10077…10085` | ✅ энкодер собирает (`0 if isinstance(right_raw, dict) else 2`), имя полю не дано. **Единственный открытый пункт** |
|
||||
| 11 | ✅ **ЗАКРЫТ** — `id` в `set_var`/операндах идёт **первым** ключом; `target` в `set_var` убран (дублировал id тела) | все `set_var` | — |
|
||||
| 12 | ⛔ **ОТМЕНЁН** — поле 2 тела `var` **снесено из YAML**, а не переименовано (круг 34, §17). Ключа `from`/`value` у `var` нет | `8472` и var-операнды | — |
|
||||
| 13 | ✅ **ЗАКРЫТ** (круг 39) — форма `steps` триггер-сценария: шаг 46 в `steps` **не пишется**, его id едет в **`trigger.step_id`**, `steps` = **тела действий**. Ключ `action` отменён | `8547`, все trigger-сценарии | — |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user