Исполняемые спецификации на flang Инструментарий: CLI, библиотека, MCP и канонический JSON
0%

Инструментарий: CLI, библиотека, MCP и канонический JSON

Инструментарий: CLI, библиотека, MCP и канонический JSON

Код в этой главе записан в прежней поверхности языка — со словами категория, объект, утилита. Сегодняшний компилятор её слова читает, но программой такой файл не считает: файл, где есть только утилиты, flang check отклоняет. Разбор задачи в главе верен; синтаксис переносится по таблице из главы «Старые модели».

Эта глава — про инструменты вокруг языка, а не про синтаксис: как установить компилятор, какими командами проверять модель и что именно летит между процессами, если считает не только Node.js, а ещё React в браузере и агент через MCP.

Минимальный пример

категория «Продажи»

  объект Покупка
    сумма является деньгами
    «постоянный клиент» является признаком

  утилита «Рассчитать скидку»
    принимает Покупка
    возвращает деньги
    начинает с 0

    правило «Большая покупка»
      если сумма не меньше 10000
      и сумма не больше 100000
      то добавить 10 процентов от поля сумма

    правило «Постоянный клиент»
      если «постоянный клиент» равен да
      и сумма больше 0
      и сумма не больше 100000
      то добавить 5 процентов от поля сумма

    правило «Очень крупная покупка»
      если сумма больше 100000
      то добавить 15000

    свойство «Скидка ограничена»
      результат не больше 15000

    пример «Обычная покупка»
      дано сумма равна 5000
      дано «постоянный клиент» равен нет
      ожидается результат равен 0

    пример «Постоянный клиент на пять тысяч»
      дано сумма равна 5000
      дано «постоянный клиент» равен да
      ожидается результат равен 250

    пример «Большая покупка постоянного клиента»
      дано сумма равна 20000
      дано «постоянный клиент» равен да
      ожидается результат равен 3000

    пример «Покупка на потолок скидки»
      дано сумма равна 100000
      дано «постоянный клиент» равен да
      ожидается результат равен 15000

    пример «Очень крупная покупка»
      дано сумма равна 200000
      дано «постоянный клиент» равен нет
      ожидается результат равен 15000

Дальше в этой главе разберём, во что это компилируется и какими командами это проверяется.

Что делает компилятор

CLI, MCP и библиотека не дублируют семантику — они вызывают один и тот же compile/validate/testUtilities/generateTypeScript/prove/certify. cli.ts и mcp.ts не содержат языковой логики: они читают исходник или канонический JSON, вызывают функцию ядра и печатают результат. Поэтому поведение fts check model.fts в терминале и fts_check в агенте совпадает буквально — это один код, а не два похожих.

Установка

Язык открыт — github.com/digitable-lol/flang — и ставится одной командой:

$ brew install digitable-lol/tap/flang
$ flang --version
flang 0.7.3

Из клона тоже работает, и нужен для этого только компилятор C — ни Node, ни Python:

git clone https://github.com/digitable-lol/flang && cd flang
make -C bootstrap -j8
./bootstrap/flang check flang/stdlib/lists.flang

Обе дороги дают один и тот же двоичный файл, собранный из C99. У него двенадцать команд, девять целей печати и четыре равноправных расширения входного файла (.flang, .fp, .фп, .фланг).

Чего в нём нет: прежнего npm-пакета

Инструментарий, на котором писалась эта глава, ехал через npm и назывался шестью командами — fts, ftsc, ftsvm, ftspec, fts-mcp, flang-lsp. Канал закрыт, пакет снят, и та реализация осталась в теге v0.4.7. Две команды переехали под новыми именами: служба для ИИ-помощника — это flang --mcp-mode, языковой сервер — flang lsp --stdio.

Ловушка, которую надо знать до первого запуска

Сегодняшний компилятор принимает файл прежней поверхности и отвечает зелёным, ничего при этом не проверив:

$ flang check discount.fts
модуль «Продажи»: функций 0, из них с доказанным завершением 0; типов 1
discount.fts: проверено — разбор, типы, завершаемость, ядро и примеры; замечаний нет

Читать этот ответ надо по числам, а не по последней строке. Категория стала модулем, объект Покупкатипом, а утилита функцией не стала: функций 0. Проверять было нечего — потому и «замечаний нет», код 0. Прогнано на 35 моделях этого сайта: 28 отвечают так же.

Что в файле на самом деле не доказано, показывает flang check --proof: «нет объявленных функций», «нет объявленных законов». Отсюда практическое правило: на файлах прежней поверхности верить нулям в первой строке, а не слову «проверено» во второй.

Как перенести модель в сегодняшний синтаксис — таблицей, в главе «Старые модели».

Один конвейер, три поверхности

Библиотечный API подходит Node.js-приложению и работает так же в браузере (без Node-модуля сертификатов):

import { compile, validate, testUtilities } from '../../../static/js/vendor/fts/browser.js'

const document = compile(source)
const report = validate(document)
if (!report.valid) throw new Error(JSON.stringify(report.diagnostics))

CLI подходит CI и любому языку, который умеет запускать процесс. Полный список команд:

fts compile model.fts               # source -> канонический JSON
fts check model.fts --pretty        # компиляция + validate
fts test model.fts --pretty         # выполнить примеры утилит
fts run model.fts --utility "Рассчитать скидку" --input input.json
fts generate model.fts --out generated   # TypeScript + node:test
fts prove model.fts --context ctx.json    # символьный вывод теоремы
fts certify model.fts --context ctx.json --pretty > proof.json
fts verify model.fts --context ctx.json --certificate proof.json
fts visualize model.fts --mode proof
fts pipeline model.fts --context ctx.json --mode all
fts mcp                             # поднять MCP-сервер по stdio

Успешная команда печатает JSON в stdout; check и test при провале печатают диагностику в stderr и завершаются ненулевым кодом.

MCP подходит агенту. Сервер объявляет себя как fts (Formal Type Surface, 0.3.0) и публикует read-only инструменты: fts_compile, fts_check, fts_test, fts_generate, fts_execute, fts_prove, fts_visualize, fts_certify, fts_verify, fts_pipeline. Агент не получает доступ к файловой системе через эти инструменты: source или уже скомпилированный document, а также context и certificate передаются как явные JSON- аргументы вызова, а не путь к файлу.

Канонический JSON

Все поверхности приходят к одному FtsDocument. Так реально выглядит fts compile минимального примера этой главы (сокращено — полный список rules/examples длиннее):

{
  "category": "Продажи",
  "structures": [
    {
      "name": "Покупка",
      "fields": [
        { "name": "сумма", "type": "Деньги" },
        { "name": "постоянный клиент", "type": "Признак" }
      ]
    }
  ],
  "functors": [],
  "proposition": null,
  "ts_compat": {},
  "utilities": [
    {
      "name": "Рассчитать скидку",
      "input": "Покупка",
      "output": "Деньги",
      "initial": 0,
      "rules": [
        {
          "name": "Большая покупка",
          "when": [
            { "field": "сумма", "operator": "gte", "value": { "kind": "value", "value": 10000 } },
            { "field": "сумма", "operator": "lte", "value": { "kind": "value", "value": 100000 } }
          ],
          "action": { "kind": "add", "value": { "kind": "percent", "percent": 10, "field": "сумма" } }
        }
      ],
      "properties": [
        {
          "name": "Скидка ограничена",
          "operator": "lte",
          "value": { "kind": "value", "value": 15000 }
        }
      ],
      "examples": [
        { "name": "Обычная покупка", "input": { "сумма": 5000, "постоянный клиент": false }, "expected": 0 }
      ]
    }
  ]
}

Это главный интеграционный контракт. React не обязан понимать русские ключевые слова: он получает structures и строит форму. Python не обязан встраивать TypeScript-парсер: он читает JSON из stdout CLI. Агент не получает право читать произвольный файл: он передаёт исходник как JSON-аргумент MCP. Поле functors — внутреннее имя раздела для морфизм; это унаследованное имя из первой версии wire-формата fts/1, а не намёк на функторы теории категорий в пользовательском синтаксисе. ts_compat резервируется под явные подсказки TypeScript-совместимости для полей — в моделях этого курса он обычно пуст.

JSON-first дисциплина

Не парсите красивый текст и не определяйте успех по наличию слова ok — ориентируйтесь на exit code и JSON-поле valid:

if fts test policy.fts > result.json; then
  node publish-report.mjs result.json
else
  echo "FTS policy failed" >&2
  exit 1
fi

Практика в песочнице

Прочитайте JSON-ответ check. Найдите в нём поле valid и убедитесь, что diagnostics — пустой массив.

Вызовите утилиту «Рассчитать доставку» с весом 20 и расстоянием 100. Посчитайте ожидаемое число по правилам вручную и сверьте с результатом.

Откройте сгенерированный TypeScript. Найдите в нём тест, соответствующий примеру «Постоянный клиент на пять тысяч», и сравните числа с исходной .fts-моделью.

Типичные ошибки

FTS_NO_UTILITY_EXAMPLESfts test вызван на документе, где ни у одной утилиты нет ни одного пример. Модель может быть синтаксически верной и даже проходить fts check, но test откажется что-либо подтверждать: диагностика буквально «утилиты не содержат примеров». Правка — добавить хотя бы один пример с дано и ожидается.

FTS_UTILITY_INPUT_TYPEfts run или executeUtility получили JSON-вход, где значение поля не совпадает с объявленным типом структуры. Если передать сумму строкой "20000" вместо числа, компилятор ответит поле «сумма» не соответствует типу «Деньги». Это осознанный выбор ядра: HTTP-сервис из examples/spec/discount-api возвращает такую ошибку клиенту как 400, а не как 500, потому что причина — в данных запроса, а не в логике сервера. Правка — приводить типы на границе перед вызовом утилиты.

Чек-лист

  • Я знаю, какую команду CLI использовать для проверки, для тестов утилит и для запуска одного вычисления.
  • Я умею объяснить разницу между library API, CLI-процессом и MCP- инструментом одной фразой: один компилятор, три интерфейса.
  • Я нахожу в каноническом JSON структуры, правила и примеры без чтения исходного .fts.
  • Я проверяю результат CLI по exit code и полю valid, а не по тексту.

Кейсы каталога по этой теме

Дальше: русский и English

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов
Дальше