FTS: читаем ошибки компилятора
Сообщение компилятора FTS — не препятствие, а результат проверки, которую вы сами заказали. Компилятор не угадывает намерение: он называет код, формулирует расхождение и, если может, указывает место. Дальше нужен только перевод с языка диагностики на язык модели.
Ниже каждая диагностика приведена в том виде, в котором её выдаёт компилятор: код, сообщение слово в слово, минимальный пример и починка.
Как устроена диагностика
Диагностика — объект с четырьмя полями:
{
"code": "FTS_UTILITY_FIELD",
"message": "unknown utility input field 'стоимость'",
"path": "$.utilities[0].rules[0].when[0].field",
"severity": "error"
}
code стабилен и предназначен для программ: по нему пишутся обработчики в
редакторе и в CI. message предназначен человеку и может уточняться между
версиями. severity в текущем ядре всегда error: тип warning объявлен, но
ни одна проверка его пока не выдаёт — модель либо принимается, либо нет.
Место ошибки приходит в одном из двух полей.
span — координаты в исходном тексте: start.line, start.column и
смещения. Его выставляет лексер и скобочный парсер, то есть слой, который
работает с символами.
path — адрес, а не координата. У естественной поверхности это номер строки
в виде "строка 12"; у проверки модели — JSON-путь вроде
"$.utilities[0].examples[0].input.сумма". Такой путь ведёт не к месту в
файле, а к узлу документа: он одинаково работает и для .fts, и для
сгенерированного JSON. Диагностики этапа выполнения места не имеют вовсе — они
относятся к вызову, а не к тексту.
Ошибки разбора и выполнения выбрасываются исключением FtsError со списком
diagnostics. Проверка модели ничего не выбрасывает: validate(document)
возвращает { valid, diagnostics } и собирает сразу все замечания.
Ошибки разбора
Стадия compile. Пока текст не разобран, никаких проверок смысла не будет.
FTS_EXPECTED_KEYWORD — документ не опознан как естественная поверхность.
Первая значимая строка обязана начинаться со слова категория в нижнем
регистре и без фигурных скобок; иначе текст уходит в старый скобочный парсер.
объект Покупка
сумма является деньгами
FTS_EXPECTED_KEYWORD | expected 'category'
span: строка 1, колонка 1
Это единственная частая диагностика со span, и сообщение у неё английское —
признак, что вы провалились в другой парсер. Починка: добавить первой строкой
категория «Продажи». Та же ошибка возникает от Категория с заглавной.
FTS_NATURAL_DECLARATION — опечатка в ключевом слове объявления.
категория «Продажи»
объектт Покупка
сумма является деньгами
FTS_NATURAL_DECLARATION | ожидались объект, структура, морфизм, теорема или утилита
path: строка 3
FTS_NATURAL_INDENT — объявление написано на одном уровне с категорией. Отступ в FTS несёт смысл, как в Python.
категория «Продажи»
объект Покупка
сумма является деньгами
FTS_NATURAL_INDENT | объявление должно иметь отступ относительно категории
path: строка 3
FTS_NATURAL_FIELD — поле без связки является.
категория «Продажи»
объект Покупка
сумма деньги
FTS_NATURAL_FIELD | поле записывается как '«имя» является типом'
path: строка 4
FTS_UNCLOSED_STRING — открытая ёлочка без закрывающей.
FTS_UNCLOSED_STRING | не закрыта кавычка-ёлочка
path: строка 3
FTS_UTILITY_COMPARISON — в условии использован глагол, которого в языке нет. Список допустимых сравнений закрыт и перечислен прямо в сообщении.
правило «Большая покупка»
если сумма превышает 10000
то добавить 10 процентов от поля сумма
FTS_UTILITY_COMPARISON | ожидалось сравнение: равен, не равен, больше, меньше, не больше или не меньше
path: строка 13
FTS_UTILITY_INPUT / FTS_UTILITY_OUTPUT / FTS_UTILITY_INITIAL — у утилиты пропущена одна из трёх обязательных строк. Сообщение называет утилиту и недостающее слово:
FTS_UTILITY_INPUT | утилита «Рассчитать скидку» требует строку 'принимает'
FTS_UTILITY_OUTPUT | утилита «Рассчитать скидку» требует строку 'возвращает'
FTS_UTILITY_INITIAL | утилита «Рассчитать скидку» требует строку 'начинает с'
FTS_NATURAL_UTILITY_INDENT — строки тела утилиты стоят на разных уровнях. Уровень определяется по минимальному отступу в теле, поэтому «лишний отступ» получит та строка, которую вы сдвинули вправо.
FTS_NATURAL_UTILITY_INDENT | строка утилиты имеет лишний отступ
path: строка 8
FTS_UTILITY_EXAMPLE — пример без строки ожидания.
FTS_UTILITY_EXAMPLE | пример «Большая покупка» требует строку 'ожидается'
path: строка 19
Ошибки модели
Стадия validate. Текст разобран, но модель противоречива. Здесь диагностик
может быть сразу несколько, и они складываются в цепочку от одной причины.
FTS_UTILITY_FIELD — правило ссылается на поле, которого нет во входном объекте. Самая частая ошибка после переименования поля.
утилита «Рассчитать скидку»
принимает Покупка
возвращает деньги
начинает с 0
правило «Большая покупка»
если стоимость не меньше 10000
то добавить 10 процентов от поля сумма
FTS_UTILITY_FIELD | unknown utility input field 'стоимость'
path: $.utilities[0].rules[0].when[0].field
FTS_UTILITY_INPUT — утилита принимает несуществующий объект. Показательный
случай каскада: одна опечатка в строке принимает даёт шесть диагностик.
FTS_UTILITY_INPUT | utility 'Рассчитать скидку' references unknown input structure 'Заказ'
path: $.utilities[0].input
FTS_UTILITY_FIELD | unknown utility input field 'сумма'
path: $.utilities[0].rules[0].when[0].field
...
Правило чтения: сначала чините диагностику с самым коротким path. Остальные
почти всегда её следствие.
FTS_UTILITY_INITIAL_TYPE — начальное значение не того типа, что объявленный
результат (начинает с «нет» при возвращает деньги).
FTS_UTILITY_INITIAL_TYPE | utility 'Рассчитать скидку' initial value does not match 'Деньги'
path: $.utilities[0].initial
FTS_UTILITY_COMPARE_TYPE — сравнение порядка применено к нечисловому полю.
FTS_UTILITY_COMPARE_TYPE | field 'постоянный клиент' is not numeric
path: $.utilities[0].rules[0].when[0]
FTS_UTILITY_COMPARE_TYPE | comparison for 'постоянный клиент' uses incompatible values
path: $.utilities[0].rules[0].when[0]
FTS_UTILITY_EXAMPLE_FIELD — пример не задаёт все обязательные поля входа.
Необязательные поля объявляются через иногда является.
FTS_UTILITY_EXAMPLE_FIELD | example 'Большая покупка' misses 'постоянный клиент'
path: $.utilities[0].examples[0].input
FTS_UTILITY_EXAMPLE_TYPE и FTS_UTILITY_EXPECTED_TYPE — значение входа или ожидаемый результат не соответствуют объявленному типу.
FTS_UTILITY_EXAMPLE_TYPE | example field 'сумма' does not match 'Деньги'
FTS_UTILITY_EXPECTED_TYPE | example 'Большая покупка' result does not match 'Деньги'
FTS_DUPLICATE_FIELD — поле объявлено дважды.
FTS_DUPLICATE_FIELD | duplicate field 'Покупка.сумма'
path: $.structures[0].fields[2].name
Рядом живут FTS_DUPLICATE_STRUCTURE и FTS_DUPLICATE_UTILITY с той же
формой сообщения.
Ошибки выполнения утилиты
Стадия executeUtility: модель корректна, проблема во входных данных вызова.
Эти диагностики не имеют ни span, ни path — они описывают вызов.
FTS_UNKNOWN_UTILITY | не найдена утилита «Рассчитать бонус»
FTS_UTILITY_INPUT | во входных данных отсутствует поле «постоянный клиент»
FTS_UTILITY_INPUT_TYPE | поле «сумма» не соответствует типу «Деньги»
FTS_UTILITY_INPUT_FIELD | входная структура «Покупка» не содержит поле «скидка»
FTS_UTILITY_INPUT_TYPE чаще всего означает строку вместо числа: "20000"
вместо 20000 — типовой результат неразобранной формы или JSON из внешнего
сервиса.
FTS_UTILITY_INPUT_FIELD срабатывает на лишнее поле: вход утилиты закрыт.
FTS_UTILITY_PROPERTY — вычисление завершилось, но нарушило постусловие. Свойство не исправляет результат, оно останавливает выполнение.
правило «Большая покупка»
если сумма не меньше 10000
то добавить 30 процентов от поля сумма
свойство «Скидка ограничена»
результат не больше 15000
FTS_UTILITY_PROPERTY | нарушено свойство «Скидка ограничена» утилиты «Рассчитать скидку»
Это единственная диагностика, которая сообщает о содержательной ошибке бизнес- логики, а не о форме. Починка — либо правило, либо граница свойства: решение предметное, компилятор его не примет за вас.
Ошибки примеров
Стадия testUtilities. Две диагностики выбрасываются до запуска:
FTS_NO_UTILITIES | документ не содержит утилит
FTS_NO_UTILITY_EXAMPLES | утилиты не содержат примеров
Несовпадение примера диагностикой не является — это отчёт. Поле valid
становится false, а в results видно ожидание и факт:
{ "utility": "Рассчитать скидку", "example": "Большая покупка",
"passed": false, "expected": 1000, "actual": 2000 }
Если пример упал по ошибке выполнения, вместо actual приходит error:
{ "passed": false, "expected": 6000,
"error": "нарушено свойство «Скидка ограничена» утилиты «Рассчитать скидку»" }
Разница важна: expected/actual означает неверную арифметику ожидания,
error — что до результата дело не дошло.
Ошибки доказательства
Теорема проверяется в два приёма. Типы морфизмов и наличие полей сверяются
ещё при разборе, а сверка с реальными данными — на стадии prove.
FTS_UNKNOWN_FIELD — в строке дано названо поле, которого нет у объекта.
FTS_UNKNOWN_FIELD | не найдено поле «Заказ».«готов к отправке»
FTS_UNKNOWN_FUNCTOR — морфизм с таким именем не объявлен. Во внутренней модели морфизмы всё ещё называются функторами, отсюда слово в коде.
FTS_UNKNOWN_FUNCTOR | не найден морфизм «Заказ можно отгрузить»
FTS_PROOF_TYPE_MISMATCH — домен морфизма не совпал с типом свидетельства.
Здесь теорема опирается на поле номер типа Строка, а морфизм ждёт
состояние.
теорема «Заказ ЗК-7781 можно отгрузить»
дано Заказ имеет номер равное «ЗК-7781»
в данных заказы найти где номер равен «ЗК-7781»
по морфизму «Готовый заказ можно отгрузить»
следовательно «Отгрузить заказ разрешено»
FTS_PROOF_TYPE_MISMATCH | морфизм «Готовый заказ можно отгрузить» ожидает «Готов к отгрузке», получено «Строка»
FTS_THEOREM_CONCLUSION — вывод цепочки не совпал с объявленным.
FTS_THEOREM_CONCLUSION | теорема объявляет «Заказ отгружен», но вывод имеет тип «Отгрузить заказ разрешено»
FTS_MULTIPLE_PROPOSITIONS — в документе две теоремы.
FTS_MULTIPLE_PROPOSITIONS | версия FTS 0.2 допускает одну теорему в документе
FTS_WITNESS_MISMATCH — единственная диагностика, которая появляется только при доказательстве с данными. Без context FTS строит символическую деривацию и молчит; с context он находит объект по пути и сверяет значение:
FTS_WITNESS_MISMATCH | witness does not match context at заказы[номер="ЗК-7781"].готов к отгрузке: expected true, got false
path: $.proposition
Сообщение показывает построенный путь целиком. Если вместо значения стоит
<missing>, ошибка в пути или в селекторе, а не в факте.
Практика в песочнице
Упражнение 1. Откройте модель скидки и в правиле «Большая покупка» замените
сумма на стоимость. Ожидаемый результат — FTS_UTILITY_FIELD с путём
$.utilities[0].rules[0].when[0].field. Верните имя и убедитесь, что
диагностика исчезла.
Упражнение 2. В том же правиле замените 10 процентов на 30 процентов и
запустите примеры. Модель пройдёт проверку, но на примере «Покупка на потолок
скидки» выполнение остановит FTS_UTILITY_PROPERTY: 30 000 против потолка
15 000 — ошибка не в форме, а в противоречии между правилом и свойством.
Упражнение 3. В модели отгрузки поменяйте строку дано на поле номер и
посмотрите на FTS_PROOF_TYPE_MISMATCH, затем верните поле и измените
следовательно — получите FTS_THEOREM_CONCLUSION.
Чек-лист: что проверить, когда модель не компилируется
- Первая значимая строка —
категория «Имя»в нижнем регистре. Английское сообщение вместо русского означает, что документ ушёл в скобочный парсер. - Все объявления имеют отступ относительно категории, а строки внутри одного блока — одинаковый отступ.
- Кавычки-ёлочки закрыты, имя не продолжается лишним текстом.
- У каждой утилиты есть
принимает,возвращаетиначинает с. - Сравнения взяты из закрытого списка:
равен,не равен,больше,меньше,не больше,не меньше. - Имена полей в правилах, свойствах и примерах совпадают с объявленными в объекте — посимвольно.
- При нескольких диагностиках чините ту, у которой самый короткий
path. - Если ошибка появилась только на выполнении, смотрите не на модель, а на входные данные вызова.
Починенная модель из упражнений целиком:
категория «Продажи»
объект Покупка
сумма является деньгами
«постоянный клиент» является признаком
утилита «Рассчитать скидку»
принимает Покупка
возвращает деньги
начинает с 0
правило «Большая покупка»
если сумма не меньше 10000
и сумма не больше 100000
то добавить 10 процентов от поля сумма
правило «Постоянный клиент»
если «постоянный клиент» равен да
и сумма больше 0
и сумма не больше 100000
то добавить 5 процентов от поля сумма
правило «Очень крупная покупка»
если сумма больше 100000
то добавить 15000
свойство «Скидка ограничена»
результат не больше 15000
пример «Большая покупка постоянного клиента»
дано сумма равна 20000
дано «постоянный клиент» равен да
ожидается результат равен 3000