## Специфікація формату rombik (astJSON)

Формат **rombik** — це компактне JSON-дерево керування, з якого детермінований рушій rombik
креслить блок-схему за ДСТУ 19.701-90 (ISO 5807). Ти описуєш **лише логіку** (послідовність
дій, розгалуження, цикли) і **текст вузлів**. Обрамлення додається САМО: овали Початок/Кінець
навколо функції, мітки Так/Ні на ромбах, самі фігури, напрямок ліній, нумерація «Рисунок N».
Їх вручну **не пиши**.

Формат потрібен, коли: (а) мову коду автомат не парсить (асемблер, Rust, псевдокод…), або
(б) коду нема — лише словесний опис. Готове дерево встав у редактор rombik з обраною мовою
«rombik» (Pro-функція) або надішли через API з `lang:"rombik"`.

### 1. Верхній рівень

Документ — **масив функцій**; кожна функція рендериться в окрему схему:

```json
[ { "name": "ім'я", "main": true, "block": { "kind": "block", "stmts": [ /* вузли */ ] } } ]
```

- `name` — назва функції (заголовок схеми).
- `main` — `true` для головної функції (решта — допоміжні).
- `block` — кореневий блок-вузол; його `stmts` — послідовність вузлів зверху вниз.

### 2. Вузли

Загальна форма: `{ "kind": …, "text"?, "cond"?, "then"?, "else"?, "body"?, "stmts"?, "jump"?, "depth"? }`.
Гілки `then` / `else` / `body` — це **блок-вузли** виду `{ "stmts": [ … ] }`; порожня гілка — `{ "stmts": [] }`.

| kind | Фігура ДСТУ | Обовʼязкові поля | Призначення |
|------|-------------|------------------|-------------|
| `process` | прямокутник (Процес) | `text` | одна елементарна дія: присвоєння, обчислення |
| `io` | паралелограм (Дані) | `text` | ввід/вивід; текст починай з «Ввід»/«Вивід» |
| `call` | прямокутник з подвійними боками (Визначений процес) | `text` | **самостійний** виклик підпрограми |
| `terminal` | овал (Термінатор) | — | явний вихід із функції: `return` / `raise` |
| `if` | ромб (Рішення) | `cond`, `then`, `else` | розгалуження |
| `for` | шестикутник (Підготовка) | `cond`, `body` (`else` — опц.) | лічильний цикл; `cond` — специфікація |
| `while` | ромб (передумова) | `cond`, `body` (`else` — опц.) | цикл з передумовою |
| `dowhile` | ромб (постумова) | `cond`, `body` | цикл з постумовою (`else` заборонено) |
| `infloop` | цикл | `body` | нескінченний цикл |
| `break` | — | `depth` | вихід із циклу (лише всередині циклу) |
| `continue` | — | `depth` | наступна ітерація (лише всередині циклу) |
| `connector` | коло (Зʼєднувач) | — | розрив лінії; `jump:true` — goto |

**Поля за призначенням:**
- `text` — напис усередині фігури (`process` / `io` / `call` / `terminal` / `connector`).
- `cond` — умова для `if` / `while` / `dowhile`; для `for` — **специфікація лічильника** у форматі
  `змінна := старт, кінець[, крок]` (напр. `i := 1, n`). Саме в `cond`, **не** в `text`.
- `depth` (`break` / `continue`) — скільки циклів угору: `0` — найближчий, `1` — на рівень вище
  (labeled break). Діапазон: `0 … (кількість охоплюючих циклів − 1)`.
- `jump` (`connector`) — `true` означає перехід (goto).

### 3. Правила моделювання (дотримуйся точно)

- **Параметри функції → перший вузол `io`.** Якщо функція має параметри, найпершим у `stmts` став
  `io` з текстом `Ввід <параметри через кому>` (без двокрапки): `{"kind":"io","text":"Ввід a, b"}`.
  Це стосується КОЖНОЇ функції з параметрами — навіть допоміжної, навіть якщо явного вводу в коді нема.
- **`call` — лише окрема інструкція-виклик** (виклик підпрограми самостійним рядком: `sort(a)`).
  Виклик УСЕРЕДИНІ виразу — не окремий вузол. `total = total + sum(a, n)` → один `process`
  (не `call` + `process`). Виклик в умові (`if f(x)`, `while f(x)`) → просто в `cond` ромба.
  `return n * fact(n-1)` → один `terminal` (не `call` + `terminal`).
- **`terminal` — лише явний вихід** (`return` / `Повернути` / `raise` у джерелі). Присвоєння —
  завжди `process`, навіть якщо воно задає результат функції (Pascal `Імʼя := значення`). Не додавай
  власний `terminal` «на кінець», якщо явного return нема — фінальний овал Кінець додає рушій.
- **`for/else` і `while/else`** (Python): гілку, що виконується при НОРМАЛЬНОМУ завершенні циклу,
  клади в поле `else` самого циклу, а не окремими вузлами після нього.
- **Один `process` = одна елементарна дія.** Не зливай кілька присвоєнь в один вузол і не розбивай
  одне присвоєння на кілька.
- **Не додавай Початок/Кінець і не пиши Так/Ні** — це робить рушій.

### 4. Значення форм за ДСТУ 19.701-90 / ISO 5807

Обирай `kind` за СУТТЮ дії (фігуру малює рушій):
- **Процес** (прямокутник) → `process`: операція, що змінює значення, форму чи розташування даних.
- **Дані** (паралелограм) → `io`: ввід або вивід даних (носій не визначається).
- **Рішення** (ромб) → `if` / `while` / `dowhile`: один вхід і кілька альтернативних виходів;
  після перевірки умови активується рівно один. Мітки Так/Ні додає рушій — ти даєш лише `cond`.
- **Визначений процес** (прямокутник з подвійними боками) → `call`: процес, визначений окремо
  (підпрограма, модуль) — самостійний виклик.
- **Підготовка** (шестикутник) → `for`: ініціалізація, що впливає на подальшу дію (лічильник циклу).
- **Зʼєднувач** (коло) → `connector`: розрив лінії з продовженням деінде; `jump:true` — перехід.
- **Термінатор** (овал) → `terminal` і Початок/Кінець, які додає рушій: вхід/вихід у зовнішнє
  середовище (return / raise).
- **Потік** (рисує рушій): стандартний напрямок — зверху вниз і зліва направо; лінії Рішення
  підписуються результатом. Напрямок / стрілки / підписи писати не треба.

### 5. Контракт валідації

Рушій валідує дерево й повертає людиночитні помилки — виправ і повтори:
- невідомий `kind` (дозволені: `process, io, call, terminal, if, for, while, dowhile, infloop, break, continue, connector`);
- `if` / `while` / `dowhile` без `cond`; `for` без специфікації `cond`;
- `process` / `io` / `call` без `text`;
- `dowhile` з `else` (не підтримується — прибери поле або зроби `while`);
- `break` / `continue` поза циклом; `depth` поза діапазоном;
- порожній документ (потрібна щонайменше одна функція).

**Ліміти:** ≤ 100 функцій, ≤ 2000 вузлів сумарно, вкладеність блоків ≤ 100, ≤ 2000 символів у `text`/`cond`.

### 6. Приклади

**`if` / `elif` / `else`** (elif = вкладений `if` усередині `else`):
```json
{ "kind": "if", "cond": "x > 0",
  "then": { "stmts": [ { "kind": "io", "text": "Вивід: плюс" } ] },
  "else": { "stmts": [
    { "kind": "if", "cond": "x < 0",
      "then": { "stmts": [ { "kind": "io", "text": "Вивід: мінус" } ] },
      "else": { "stmts": [ { "kind": "io", "text": "Вивід: нуль" } ] } } ] } }
```

**Цикл `for` (діапазон) і `while`:**
```json
{ "kind": "for", "cond": "i := 0, n-1",
  "body": { "stmts": [ { "kind": "call", "text": "обробити(i)" } ] } }

{ "kind": "while", "cond": "b <> 0",
  "body": { "stmts": [ { "kind": "process", "text": "t := b" } ] } }
```

**Опис словами → astJSON.** «Ввести число n. Якщо парне — вивести „парне“, інакше „непарне“.»
```json
[
  { "name": "parity", "main": true, "block": { "kind": "block", "stmts": [
    { "kind": "io", "text": "Ввід n" },
    { "kind": "if", "cond": "n mod 2 = 0",
      "then": { "stmts": [ { "kind": "io", "text": "Вивід: парне" } ] },
      "else": { "stmts": [ { "kind": "io", "text": "Вивід: непарне" } ] } }
  ] } }
]
```

**Повний приклад — пошук максимуму в масиві:**
```json
[
  { "name": "findMax", "main": true, "block": { "kind": "block", "stmts": [
    { "kind": "io", "text": "Ввід a, n" },
    { "kind": "process", "text": "m := a[0]" },
    { "kind": "for", "cond": "i := 1, n-1", "body": { "stmts": [
      { "kind": "if", "cond": "a[i] > m",
        "then": { "stmts": [ { "kind": "process", "text": "m := a[i]" } ] },
        "else": { "stmts": [] } }
    ] } },
    { "kind": "io", "text": "Вивід m" },
    { "kind": "terminal", "text": "Повернути m" }
  ] } }
]
```

### Генерація з опису чи коду

Якщо тобі дали опис словами або код будь-якою мовою — побудуй дерево за цією специфікацією й
поверни **лише підсумковий JSON-масив** у блоці ` ```json ` , без пояснень навколо. Неоднозначність
у описі — реалізуй найпростіше розумне тлумачення, не вигадуй зайвих гілок.
