Перейти к содержанию

Потоковая проверка JSON

liza-json check — первый прикладной шаг Text & Compiler Foundations. Валидатор написан на Liza; он не строит дерево и не сохраняет текст токенов. Новые операции runtime и изменения модели владения не требуются.

python3 -m liza build examples/apps/liza_json.liza -o build/liza-json
./build/liza-json check config.json

На Windows используйте build/liza-json.exe. Успех печатает OK. format пока не поддерживается.

Профиль входа

Основа — RFC 8259. Корнем может быть любое JSON-значение. Повторяющиеся ключи разрешены. Пробелами считаются только SP, TAB, CR и LF. Комментарии, завершающие запятые, NaN и Infinity отклоняются. BOM в начале документа отклоняется.

Весь вход должен быть корректным UTF-8. Escape-последовательности проверяются, включая пары UTF-16 суррогатов. Одиночный суррогат отклоняется: это намеренно более строгий профиль Unicode scalar values, чем ABNF RFC, допускающая непарные суррогаты. Не-символы Unicode не запрещены; \u0000 допустим.

Числа проверяются лексически, без преобразования в f64 или целое число. Длина числа, строки или файла не ограничена размером чанка. Общий счётчик входа ограничен u64; превышение возвращает SizeOverflow. Глубина контейнеров ограничена 64: 65-я открывающая скобка даёт DepthLimit. Это ограничение ресурса, а не утверждение о синтаксической неверности JSON.

Библиотека

Операция Результат
json.create() Независимое значение JsonState
json.feed(state, data:View<u8>) Новое состояние, без удержания view
json.status(state) Option<JsonError>: обнаруженная ошибка или None
json.finish(state) Result<u64,JsonError>: число байтов либо ошибка

None не означает завершённый документ. finish проверяет EOF как снимок: после незавершённого снимка можно продолжить feed. Обнаруженная при feed ошибка сохраняется неизменно; дальнейшие данные игнорируются. Поля состояния закрыты. Состояние можно копировать; оно не зависит от времени жизни чанка.

JsonError содержит kind и offset. Смещения — абсолютные, байтовые, с нуля. Ошибка синтаксиса указывает на отвергнутый байт; незавершённый документ — на конец входа. Ошибка UTF-8 указывает на начало некорректной последовательности. Непарный суррогат указывает на обратную косую черту первого \u. Некорректная hex-цифра указывает на саму цифру.

Проверка UTF-8 выполняется перед синтаксическим переходом для каждого байта. Первое обнаруженное нарушение сохраняется независимо от разбиения входа. На EOF незавершённый UTF-8 имеет приоритет перед незавершённым JSON.

Стоимость и CLI

Стек содержит 64 однобайтовых состояния. Остальные поля — скалярные состояния лексического и UTF-8 автоматов. Память валидатора не растёт вместе с входом; CLI читает file.read_chunk по 2048 байтов. Нет heap-аллокаций валидатора, накопления токенов или декодирования строк. Передача состояния по значению может копировать фиксированный стек; zero-copy исполнения не обещается.

Код выхода Значение
0 Документ принят
1 Синтаксис, UTF-8 или Unicode escape отвергнут
2 Неверная командная строка
3 Ошибка открытия, чтения, закрытия или вывода
4 Лимит глубины или размера

Файл закрывается на всех нормальных путях после открытия, в том числе при ранней ошибке JSON. Ошибка закрытия имеет приоритет над результатом проверки. Диагностика имеет вид InvalidNumber at byte 12 и выводится в stderr.

Проверка и границы

Нативные тесты O0/O2 подают данные во время исполнения, проверяют разбиения внутри UTF-8, escapes, суррогатных пар и чисел, EOF, границы глубины и длинные токены. Генерируемые документы дополнительно сопоставляются с Python JSON. Это тестирование, не новый эксперимент Verified и не доказательство всех входов.

Текущий API — валидатор синтаксиса, не tokenizer с выдачей событий, DOM, форматтер или динамический String. Строковые значения не декодируются наружу. Будущему tokenizer потребуется различать исходный диапазон и декодированный текст, особенно для escapes и строк, пересекающих чанки.