Две поверхности: русская и английская запись одного правила
Код в этой главе записан в прежней поверхности языка — со словами
категория,объект,утилита. Сегодняшний компилятор её слова читает, но программой такой файл не считает: файл, где есть только утилиты,flang checkотклоняет. Разбор задачи в главе верен; синтаксис переносится по таблице из главы «Старые модели».
Эта глава закрывает вопрос, который обычно возникает первым: если модель пишут и русскоязычный аналитик, и разработчик, читающий английские тексты привычнее, значит ли это два разных языка и два парсера? Нет — один и тот же результат, две поверхности.
Минимальный пример
категория «Продажи»
объект Покупка
сумма является деньгами
«постоянный клиент» является признаком
утилита «Рассчитать скидку»
принимает Покупка
возвращает деньги
начинает с 0
правило «Большая покупка»
если сумма не меньше 10000
и сумма не больше 100000
то добавить 10 процентов от поля сумма
правило «Постоянный клиент»
если «постоянный клиент» равен да
и сумма больше 0
и сумма не больше 100000
то добавить 5 процентов от поля сумма
правило «Очень крупная покупка»
если сумма больше 100000
то добавить 15000
свойство «Скидка ограничена»
результат не больше 15000
пример «Обычная покупка»
дано сумма равна 5000
дано «постоянный клиент» равен нет
ожидается результат равен 0
пример «Постоянный клиент на пять тысяч»
дано сумма равна 5000
дано «постоянный клиент» равен да
ожидается результат равен 250
пример «Большая покупка постоянного клиента»
дано сумма равна 20000
дано «постоянный клиент» равен да
ожидается результат равен 3000
пример «Покупка на потолок скидки»
дано сумма равна 100000
дано «постоянный клиент» равен да
ожидается результат равен 15000
пример «Очень крупная покупка»
дано сумма равна 200000
дано «постоянный клиент» равен нет
ожидается результат равен 15000
Что делает компилятор
Перед разбором компилятор смотрит на первую непустую, некомментарную строку
файла. Если она начинается с категория или category и не содержит {,
весь файл идёт в отступный парсер естественного синтаксиса
(looksLikeNaturalSurface). Если строка начинается с category, компилятор
сначала построчно переписывает зарезервированные фразы на русские — if в
если, then add в то добавить, is at least в не меньше, percent of field в процентов от поля» и так далее — и только потом отдаёт получившийся текст тому же самому parseNaturalSurface. Это не второй интерпретатор и не перевод через LLM: фиксированный набор regex-подстановок по позиции в строке, который не трогает содержимое кавычек и сохраняет отступы и номера строк — поэтому ошибка в английской записи указывает на ту же строку файла, что и в русской. Если первая строка не подходит ни под одно из условий (например, в файле остался старый скобочный category “X” { … }`), компилятор переходит
на легаси-парсер с фигурными скобками — он принимается для обратной
совместимости, но не является рекомендуемым авторским форматом.
English surface
category "Sales"
object Purchase
amount is money
"loyal customer" is boolean
utility "Calculate discount"
accepts Purchase
returns money
starts with 0
rule "Large purchase"
if amount is at least 10000
and amount is at most 100000
then add 10 percent of field amount
rule "Loyal customer"
if "loyal customer" equals true
and amount is greater than 0
and amount is at most 100000
then add 5 percent of field amount
rule "Very large purchase"
if amount is greater than 100000
then add 15000
property "Discount is capped"
result is at most 15000
example "Regular purchase"
given amount equals 5000
given "loyal customer" equals false
expected result equals 0
example "Large loyal purchase"
given amount equals 20000
given "loyal customer" equals true
expected result equals 3000
example "Purchase at the discount cap"
given amount equals 100000
given "loyal customer" equals true
expected result equals 15000
Сравните эти два блока: правила, проценты и числа в примерах совпадают один
в один, но Продажи/Sales, Покупка/Purchase, сумма/amount — разные
слова, не автоперевод. Английская запись независимо называет предметные
сущности; переводятся только зарезервированные служебные фразы (category,
if, then, is at least, percent of field и подобные), а имена полей,
объектов и категорий остаются такими, какими их написал автор. Это значит,
что русский и английский документы вольны описывать разные ubiquitous
languages, если это два разных предметных словаря — но раз оба компилируются
одним парсером в одну и ту же каноническую модель, поведение утилиты и типы
полей будут идентичны при идентичной структуре правил.
Структура равноправна объекту
структура «Строка счёта»
номер является строкой
сумма является деньгами
комментарий иногда является строкой
структура и объект — синонимы на уровне синтаксиса: оба варианта строят
одинаковый узел FtsStructure в JSON. Выбор слова — вопрос стиля команды, а
не семантики: некоторые предпочитают «объект» для сущностей с состоянием и
«структура» для чистых DTO, но компилятор эту границу не проверяет.
Имена и кавычки
Имя из одного слова можно писать без кавычек: Заказ, amount, номер.
Пробел делает кавычки обязательными — и, что важно, объявление обязано иметь
отступ относительно строки категория, иначе компилятор решит, что документ
закончился на первой же строке после заголовка:
категория «Обслуживание клиентов»
объект "Строка счёта"
номер является строкой
Обычные кавычки и русские ёлочки в этом примере намеренно перемешаны —
«Обслуживание клиентов» и "Строка счёта" — оба вида равноправны для
компилятора и не создают двух разных синтаксисов внутри одного файла.
Имя читается функцией, которая всегда завершает разбор вызовом
.normalize("NFC"). Поэтому визуально одинаковая буква «й», собранная как
одна кодовая точка или как «и» с отдельным комбинирующим знаком, превращается
в одно и то же имя поля, а не в два разных.
CamelCase не требуется. В предметном тексте предпочтительнее
«готов к отгрузке», чем ReadyForShipment, если модель читают
русскоязычные аналитики и разработчики вперемешку.
Значения
Русская запись принимает да, нет, ничто; обе поверхности принимают
true, false, null, числа и строки в кавычках. Имена полей при этом не
переводятся при переключении поверхности — это ответственность автора
модели, а не компилятора.
Практика в песочнице
Откройте тот же model для order-discount.en.fts (если песочница даёт
выбор модели, сравните оба JSON вручную по исходникам в
static/spec/models/). Убедитесь, что rules, properties и examples
совпадают числами и операторами, а различаются только строки category,
structures[].name и structures[].fields[].name.
Смените первую строку "Sales" на Sales без кавычек (одно слово — кавычки
не обязательны) и убедитесь, что check по-прежнему возвращает valid: true
— кавычки нужны только там, где в имени есть пробел.
Переименуйте объект «Строка счёта» в "Строка счёта, версия 2" (добавьте
пробел и запятую), но не трогайте теорема, которая ссылается на старое
имя. Запустите check и найдите диагностику FTS_UNKNOWN_FIELD — она
покажет, что имя нужно поменять во всех местах вручную: компилятор не
переименовывает ссылки за автора.
Типичные ошибки
FTS_NATURAL_INDENT — объявление верхнего уровня (объект, структура,
морфизм, теорема, утилита) написано без отступа относительно строки
категория. Компилятор считает, что документ закончился, и сообщает:
«объявление должно иметь отступ относительно категории». Именно эта ошибка
была в блоке «Имена и кавычки» этой главы до правки — объект "Строка счёта" стоял на той же колонке, что и категория. Правка — минимум один
пробел или таб отступа, и дальше вложенность считается уже от первой такой
строки.
FTS_EXPECTED_KEYWORD — первая строка не опознана ни как категория,
ни как category, и компилятор молча переключается на легаси-парсер с
фигурными скобками, который тут же требует буквально слово category.
Опечатка катргория «Продажи» (без «о») даёт диагностику expected 'category' — на английском, даже если весь остальной файл на русском. Это
не баг локализации, а следствие автоопределения поверхности по первой
строке: если она не распознана как естественный синтаксис, документ
интерпретируется как устаревший скобочный формат. Правка — проверить
написание первого слова файла.
Чек-лист
- Я объясняю, что русская и английская запись — один парсер и одна каноническая модель, а не перевод.
- Я знаю, что переводятся только зарезервированные фразы, а не имена объектов, полей и категорий.
- Я пишу многословные имена в кавычках и не путаю обязательный отступ верхнеуровневых объявлений.
- Я знаю, что имена нормализуются в NFC, и не полагаюсь на визуальное совпадение символов.