diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0dbf0ce..88027ec 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -113,6 +113,30 @@ snapshot price) plus the mirror list in `src-tauri/src/main.rs`. - The registry is rebuilt never; extensions mutate it (add phrases/functions) at startup. +## Cross-sheet references + +`@Sheet.key` is lexed directly in `lexer.ts` (not through the phrase +registry — sheet titles are arbitrary user text, not vocabulary), producing +an `xref` lexeme carrying `{ sheet, key }`. The engine only knows how to +*ask* for a value: `EvalCtx.resolveXRef?.(sheet, key)` returns +`{ ok: true; value } | { ok: false; reason }`; when no resolver is supplied +(tests, extensions, `evaluateExpression`) an xref is simply unresolved. +Unresolved xrefs throw `XRefError` internally, caught in +`SumEngine.evaluateDocument` and surfaced as `LineResult.error` (rendered as +`#ref?` in the results column) — every other evaluation error still resolves +to a silent blank line, unchanged. + +The engine has no concept of "other sheets" — that lives entirely in +`src/workspace.ts`. `Workspace` reads all sheet texts from the app's +in-memory `data.contents`, caches each sheet's exports (its assigned +variables, `total`, `last`) after first evaluating it, and invalidates a +sheet's cache plus every transitive dependent (found via a text scan for +`@Name.`) when it's edited or rates refresh. `Workspace.evaluateSheet(id, +text)` pushes `id` onto a resolving-stack for the duration of the call, so a +sheet that references itself — directly or through another sheet — hits an +already-on-the-stack check and resolves to `circular reference` instead of +recursing forever. + ## UI notes - CodeMirror injects its base theme at runtime *after* `app.css`, so anything diff --git a/ARCHITECTURE.ru.md b/ARCHITECTURE.ru.md index ca3c993..f6bb973 100644 --- a/ARCHITECTURE.ru.md +++ b/ARCHITECTURE.ru.md @@ -111,6 +111,29 @@ src-tauri/ - Реестр не пересобирается никогда; расширения мутируют его (добавляют фразы/функции) при запуске. +## Ссылки между листами + +`@Sheet.key` лексится напрямую в `lexer.ts` (не через реестр фраз — названия +листов это произвольный текст пользователя, а не словарь), давая лексему +`xref` с `{ sheet, key }`. Движок умеет только *спросить* значение: +`EvalCtx.resolveXRef?.(sheet, key)` возвращает +`{ ok: true; value } | { ok: false; reason }`; без резолвера (тесты, +расширения, `evaluateExpression`) ссылка просто не резолвится. Нерешённые +ссылки бросают `XRefError` внутри, он ловится в `SumEngine.evaluateDocument` +и превращается в `LineResult.error` (рисуется как `#ref?` в колонке +результатов) — все прочие ошибки вычисления по-прежнему тихо дают пустую +строку, это не изменилось. + +Движок не знает про "другие листы" — это целиком живёт в `src/workspace.ts`. +`Workspace` читает тексты всех листов из `data.contents` приложения, +кэширует экспорт каждого листа (его присвоенные переменные, `total`, `last`) +после первого вычисления, и сбрасывает кэш листа плюс всех его транзитивных +зависимых (находятся сканированием текста на `@Name.`) при правке листа или +обновлении курсов. `Workspace.evaluateSheet(id, text)` кладёт `id` в стек +резолвинга на всё время вызова, поэтому лист, ссылающийся сам на себя — +напрямую или через другой лист — сразу натыкается на проверку "уже в +стеке" и получает `circular reference` вместо бесконечной рекурсии. + ## Особенности UI - CodeMirror встраивает свою базовую тему во время выполнения *после* `app.css`, diff --git a/README.md b/README.md index 76dbdfe..f015c1a 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ plus `in fraction` (0.75 → 3/4) and `in roman` (2026 → MMXXVI). `//` comments. The status bar always shows the sheet total; select a few lines to see just their sum. Add `chart` at the end of a block to draw a sparkline. +**Cross-sheet references** — pull a value from another sheet with `@`: +`@Budget.total`, `@Budget.rent`, `@[Trip to Lisbon].food`. A sheet made of +nothing but references becomes a live dashboard: +`@Assets.total - @Debts.total`. Give the source sheet a fixed name (not an +auto-derived one) so the reference doesn't break when its first line changes. + ## Handy things - **Ctrl+Alt+N** (configurable — the settings field records keys you press) diff --git a/README.ru.md b/README.ru.md index ded68da..c9c3260 100644 --- a/README.ru.md +++ b/README.ru.md @@ -85,6 +85,12 @@ AAPL в USD → $213.55 выдели несколько строк — увидишь их сумму. Добавь `график` в конце блока чтобы нарисовать спарклайн. +**Ссылки между листами** — тяни значение из другого листа через `@`: +`@Бюджет.total`, `@Бюджет.аренда`, `@[Поездка в Лиссабон].еда`. Лист из одних +ссылок становится живым дашбордом: `@Активы.total - @Долги.total`. Дай листу- +источнику постоянное имя (не автоматическое), иначе ссылка сломается при +правке первой строки. + ## Удобные мелочи - **Ctrl+Alt+N** (настраивается — поле в настройках записывает нажатые клавиши)