В CraftHub теперь можно писать формулы. Не «редактор с табличкой», а таблица, которая считает сама: пишешь в ячейку =, и всё, что дальше, — это уже формула. =@[price] * @[qty], =SUM([total]), =EDATE(@[orderedAt], 1). Меняешь исходные числа — результат пересчитывается сам.

Синтаксис я честно списал с Excel: незачем заставлять людей учить ещё один язык ради того, что все и так знают. А вот с типами так не вышло — про это будет отдельный разговор ниже, потому что именно там самое спорное и самое, на мой взгляд, правильное решение.

Как это выглядит

Ставишь в начале ячейки = — ячейка становится формулой. Enter — применить, Escape — передумать. После этого в углу появляется маленький маркер, а по клику формула снова открывается для правки: и прямо в ячейке, и в строке формул над таблицей.

Из операторов есть всё, что ожидаешь: + - * / ^, & чтобы склеить текст, = <> < > <= >= для сравнений, скобки для группировки. Ничего необычного.

Всё подсказывается по ходу. Написал = — выпадает список функций с сигнатурой и короткой строчкой о том, что она делает. Открыл [ — список колонок. Список функций, кстати, не захардкожен в интерфейсе, а собирается из того же реестра, который использует движок, — так что рассинхрона «в подсказке одно, работает другое» быть не может.

Ячейка становится формулой, как только её текст начинается с `=`
Ячейка становится формулой, как только её текст начинается с `=`

Как сослаться на другие данные

В обычной таблице ячейки безымянные, поэтому там всё через буквы и цифры. Здесь колонки — это named-поля со своими типами, так что и ссылаться удобнее по имени.

@[поле] — это значение поля в текущей строке. Самая ходовая штука: =@[price] * @[qty]. Куда бы формула потом ни попала — протянул вниз, применил на весь столбец — @[поле] всё равно означает «моя строка».

[поле] без собачки — это уже весь столбец целиком, вместе со строками, которые добавят потом. Нужно оно внутри функций, которые едят много значений: =SUM([total]), =AVERAGE([price]). Разница с @ небольшая на вид, но существенная: =ROUND(@[total] / SUM([total]) * 100, 1) — это доля конкретной строки в общей сумме.

Ещё можно по-старому, через A1. B2 — вторая колонка, вторая строка. D1:D10 — диапазон. Так формула из одной строки дотягивается до другой: посчитать нарастающий итог, взять ставку из первой строки. Одна тонкость: буквы A, B, C привязаны не к самим полям, а к их порядку на экране. Перетащил поле A в конец — оно стало последней буквой, а бывшее C уехало в начало и стало A. Формулы после такой перестановки продолжают указывать на те же данные, просто буквы в них другие.

И $. Голый номер строки — относительный: =D1 из второй строки читается как «строка надо мной», и если протянуть формулу в пятую строку, она возьмёт D4. Поставил $ — зафиксировал: $D$1 из любой строки смотрит ровно в первую. Это то, что нужно для какого-нибудь курса или итога, который лежит в одном месте. Буквы колонок фиксированы всегда — «протягивания вправо», как в экселе, тут нет, поля-то разные.

Внутри `[` подсказываются имена столбцов документа
Внутри `[` подсказываются имена столбцов документа

Формула сразу на весь столбец

Тут есть два похожих жеста, которые легко перепутать.

Первый — Ctrl+Enter в строке формул (или кнопкой рядом, или из правого клика). Это применяет формулу ко всему столбцу разом, включая будущие строки. Получается «вычисляемая колонка».

Второй — потянуть за квадратик в углу ячейки. Это просто копирование формулы в те строки, по которым протащил, и не более.

Если в ячейку вычисляемой колонки вбить обычное значение руками, она из этой колоночной формулы выпадает — ровно как в экселе — и следующий пересчёт её не затрёт.

[result]` — это все значения колонки, включая будущие строки
[result]` — это все значения колонки, включая будущие строки

Функций больше восьмидесяти

Разбиты на шесть групп:

  • Математика: SUM, PRODUCT, ROUND / ROUNDUP / ROUNDDOWN, CEILING, FLOOR, INT, TRUNC, MOD, POWER, SQRT, ABS, SIGN.

  • Статистика: AVERAGE, MEDIAN, MIN / MAX, COUNT / COUNTA / COUNTBLANK, COUNTIF / SUMIF / AVERAGEIF, STDEV, LARGE / SMALL, RANK.

  • Логика: IF, IFS, SWITCH, AND / OR / NOT / XOR, IFERROR, ISBLANK, ISNULL, ISNUMBER, ISTEXT, ISERROR.

  • Текст: CONCAT / TEXTJOIN, LEFT / RIGHT / MID, LEN, TRIM, UPPER / LOWER, FIND / SEARCH, REPLACE / SUBSTITUTE, SPLIT, REPT, TEXT, VALUE.

  • JSON: TOJSON / PARSEJSON, JSONPATH, KEYS, HASKEY, ARRAYLEN, TYPEOF, FLATTEN.

  • Даты: TODAY / NOW, DATE / DATETIME, YEAR / MONTH / DAY, HOUR / MINUTE / SECOND, WEEKDAY, EDATE / EOMONTH, DAYS / DATEDIF, DATEADD, ISDATE.

А вот с типами я с Excel не согласился

Синтаксис у экселя хороший, а вот с тем, как он обращается с типами, я жить не захотел. Приведение типов в экселе придумывали под сетку, где в ячейке может лежать что угодно. А тут в документе уже есть нормальная система типов — сам JSON. И когда эти две логики расходятся, я выбираю JSON.

Ничего не превращается само по себе. Строка "123" — это не число 123. Хочешь число — пиши VALUE("123"). TRUE — это не 1, для перехода есть IF(x, 1, 0). Сложить текст с числом, или null с числом — это ошибка (#VALUE! или #TYPE!), а не молчаливая догадка о том, что ты имел в виду.

«Нет ключа», null и пустая строка — это три разные вещи, и движок их не смешивает:

Что это

ISBLANK

ISNULL

В SUM / AVERAGE / COUNT

Missing

ключа в объекте вообще нет

TRUE

FALSE

пропускается

Null

ключ есть, значение — null

FALSE

TRUE

пропускается

""

ключ есть, значение — пустая строка

FALSE

FALSE

пропускается

Обрати внимание: SUM и компания null и Missing пропускают, а не считают за ноль. В экселе пустая ячейка — это и ноль в арифметике, и «пропустить» в сумме одновременно, что довольно шизофренично. В JSON null — это осмысленные данные, а не «тут пока пусто».

Числа считаются на decimal, а не на double, поэтому деньги не уплывают в двадцатом знаке. Целое так и запишется целым: 5, а не 5.0. И 19.90 сохранит свой хвостовой ноль, а не схлопнется в 19.9.

Даты — это просто ISO-строки вида 2024-03-15 или 2024-03-15T14:30:00, а не серийные числа. Что на входе за формат — то и на выходе: та же точность, тот же часовой сдвиг. Складывать даты через + и - нельзя, для этого есть DATEADD, EDATE, DAYS, DATEDIF. И никаких «перекруток»: DATE(2024, 13, 1) — это ошибка, а не тихо превратившийся январь 2025-го.

Если результат не лезет в тип колонки — скажем, получилась дробь, а колонка Int — это #TYPE!. Формула не станет ничего округлять за тебя и уж точно не поменяет тип колонки.

Сумма посчитанного столбца `result` (17 + 45 + 89). Поправишь любое слагаемое — пересчитается и строка, и итог
Сумма посчитанного столбца `result` (17 + 45 + 89). Поправишь любое слагаемое — пересчитается и строка, и итог

Пересчёт и ошибки

Формулы знают, кто от кого зависит, — внутри это граф. Поменял одну ячейку — движок пройдёт по графу и пересчитает только то, что реально задето, а не весь документ. Если формулы зациклились друг на друга — это #CYCLE!, а не подвисший интерфейс.

Ошибки называются понятно: #DIV/0! — деление на ноль, #VALUE! — кривой аргумент, #TYPE! — результат не влезает в колонку, #REF! — ссылка ведёт в никуда, #CYCLE! — цикл. Ячейка с ошибкой остаётся пустой, а уголок краснеет; наведёшь — увидишь, что случилось. Ожидаемые обломы можно ловить самому через IFERROR.

В сам JSON ошибка попадает как null. Не строкой "#DIV/0!" — иначе бы она поломала тип колонки. А код и текст ошибки лежат отдельно, поэтому при повторном открытии файла всё сразу видно, без прогона пересчёта.

Файл остаётся обычным JSON

Это было для меня принципиально. В самом документе лежат только посчитанные значения — обычные числа и строки. Открыл его без CraftHub — там нормальный JSON, никаких следов формул.

Формулы живут в отдельном файле рядом: имя.formulas.json. Пишется он в тот же момент, что и документ. Переносишь проект — переноси оба.

Отдать данные наружу можно тремя способами: просто экспорт JSON (одни значения), «отвязать формулы» (значения остаются, файл с формулами удаляется), или бандл .crhb — это когда документ и формулы едут одним файлом, чтобы отдать кому-то всё целиком.

Немного про кишки

Движок формул вынесен в отдельные сборки и про интерфейс ничего не знает. Внутри всё как в учебнике: лексер, парсер, вычислитель. Добавить функцию — это одна запись в реестре, парсер и вычислитель трогать не надо. Отдельно живёт граф зависимостей и слой адресации, который переводит @[поле] в JSON-путь и обратно. Считается всё на decimal, даты гоняются через один-единственный ISO-парсер, чтобы не расползались форматы.

Попробовать

CraftHub — бесплатный редактор JSON под Windows, Linux и macOS, плюс расширение для VS Code. Формулы уже в свежей сборке, ничего доустанавливать не надо.

Если вычисляемый JSON — это то, чего вам не хватало, закиньте звезду на GitHub. Мне это прямо помогает понять, что в фичу стоит вкладываться дальше: в планах формулы внутри вложенных объектов и массивов, именованные диапазоны и ещё функции.

Комментарии (12)


  1. XelaNimed
    31.08.2026 06:52

    У меня возник вопрос: зачем, где, для решения каких задач это используется? Я думал, что JSON должен делать то, что находится в его имени - описывать объекты. Если у объекта есть поле/свойство с формулой - ОК, но зачем что-то высчитывать в самом JSON?


    1. c3n9 Автор
      31.08.2026 06:52

      JSON тут описывает не результат, а правило. В игровом балансе куча производных характеристик: урон зависит от силы и уровня, HP — от телосложения и перков. Если хранить готовые числа, то правка одного коэффициента заставляет вручную переписывать десятки полей у сотен персонажей и предметов.


      1. XelaNimed
        31.08.2026 06:52

        JSON тут описывает не результат, а правило

        Я это и имел ввиду, когда писал, что формула может быть свойством объекта.
        Я хочу сказать, что зачем что-то вычислять, если это может и не понадобиться потребителю, а если это 100% нужно, то зачем данные из которых вычисляется результат? Не знаю... Я в полной растерянности...


        1. c3n9 Автор
          31.08.2026 06:52

          Разные ситуации бывают, столкнулся с тем, что не было возможности менять свойства у объекта, но по своей сути формулы тут не главное, главное табличный вид JSON для удобства его редактирования. Вложенность, поиск, дублирование - максимально упрощается


    1. c3n9 Автор
      31.08.2026 06:52

      как один из примеров


  1. impwx
    31.08.2026 06:52

    Сам по себе проект любопытный, но с используемой вами терминологией я не согласен. JSON - это формат хранения данных: он не может быть “вычисляемым” или “считать сам себя”. Вычислениями занимается ваш движок формул, который просто хранит данные в виде JSON, и, насколько я понял, поддерживает не произвольный JSON, а один конкретный формат - массив структур. Если движок написан как следует, то не должно составить огромного труда добавить в него поддержку других форматов хранения, будь то XML, или CSV, или что либо еще.


    1. c3n9 Автор
      31.08.2026 06:52

      Конечно, но никто не запрещает сделать его изменяемым и удобным, чтобы проще было редактировать данные.
      Конечно, в будущем планирую расширять функционал на XML и CSV, дело времени.
      Почему JSON? Проект был создан для товарищей и личных нужд, люди решили поддержать, решил расширять функционал.
      Буду благодарен если создадите issue для CSV, я обязательно этим займусь, спасибо Вам!


  1. CadMan
    31.08.2026 06:52

    Мне таких таблиц не хватает в *.md формат. Именно когда свойство является формулой.

    Приходится назначать свойству ключ, а потом отдельно ещё и описывать разные состояния ключа.


    1. c3n9 Автор
      31.08.2026 06:52

      Добрый день, хм, интересно, создайте, пожалуйста issue и опишите с какими файлами вы работаете, чтобы я мог подумать о реализации, пока плохо представляю


  1. redfox0
    31.08.2026 06:52

    Потыкал эту поделку, сырая:

    • открыл json на 2.5 КБ, в простое жрёт 20% CPU;

    • нельзя сменить тип колонки, нельзя поставить null;

    • вкладка JSON, панелька “Поиск” - не нажимаются кнопки “Вперёд”/“Назад”/“Закрыть”.


    1. c3n9 Автор
      31.08.2026 06:52

      Спасибо за Ваш отзыв!
      Можете подробнее описать проблему с 20% CPU, приложить скриншоты в issue


    1. shai_hulud
      31.08.2026 06:52

      раз уж тестите редакторы игровых конфигов, пытыкайте мою поделку, может понравится

      https://gamedevware.github.io/charon/