diff --git a/README.md b/README.md index fc804b2..5701bbf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Полнофункциональная библиотека для работы с YAML в экосистеме OneScript/1C. Реализует парсинг и сериализацию YAML с поддержкой основных возможностей стандарта YAML 1.2 включая вложенные структуры, якоря, многострочные литералы и Flow Style синтаксис. [![GitHub release](https://img.shields.io/github/v/release/Segate-ekb/oscript-yaml)](https://github.com/Segate-ekb/oscript-yaml/releases) -[![Tests](https://img.shields.io/badge/tests-354%20passed-green)](https://github.com/Segate-ekb/oscript-yaml) +[![Tests](https://img.shields.io/badge/tests-418%20passed-green)](https://github.com/Segate-ekb/oscript-yaml) [![OneScript](https://img.shields.io/badge/OneScript-2.0.0--rc.7+-blue)](https://oscript.io/) ## 📋 Оглавление @@ -14,6 +14,7 @@ - [Поддерживаемые возможности](#поддерживаемые-возможности) - [Статус реализации](#статус-реализации) - [Тестирование](#тестирование) +- [Нагрузочные тесты](#нагрузочные-тесты) ## 🚀 Установка @@ -73,6 +74,22 @@ git clone https://github.com/Segate-ekb/oscript-yaml.git Результат = Парсер.ПрочитатьYaml("key: value"); ``` +#### ПрочитатьИзФайла(ПутьКФайлу) + +Читает YAML-файл в кодировке UTF-8 и возвращает соответствующую структуру данных. + +**Параметры:** +- `ПутьКФайлу` (Строка) - путь к YAML-файлу + +**Возвращаемое значение:** +- Произвольный - распарсенная структура данных или массив документов + +**Пример:** +```bsl +Парсер = Новый ПарсерYaml(); +Результат = Парсер.ПрочитатьИзФайла("config.yaml"); +``` + #### ЗаписатьYaml(Значение) Сериализует данные в YAML-формат. @@ -91,6 +108,21 @@ git clone https://github.com/Segate-ekb/oscript-yaml.git // Результат: "---\nключ: значение" ``` +#### ЗаписатьВФайл(ПутьКФайлу, Значение) + +Сериализует данные в YAML-формат и записывает результат в файл в кодировке UTF-8. + +**Параметры:** +- `ПутьКФайлу` (Строка) - путь к YAML-файлу +- `Значение` (Произвольный) - данные для сериализации + +**Пример:** +```bsl +Парсер = Новый ПарсерYaml(); +Данные = Новый Структура("ключ", "значение"); +Парсер.ЗаписатьВФайл("config.yaml", Данные); +``` + ## ✅ Поддерживаемые возможности ### Базовые типы данных @@ -152,60 +184,34 @@ git clone https://github.com/Segate-ekb/oscript-yaml.git 1. **Базовый парсинг YAML** - ключ-значение пары, скалярные типы 2. **Массивы Block Style** - списки с дефисами 3. **Вложенные структуры** - многоуровневые объекты любой глубины -4. **Многострочные литералы** - `|` и `>` стили с базовыми модификаторами (28/39 тестов) -5. **Flow Style** - инлайн массивы `[1, 2, 3]` и объекты `{key: value}` (74/79 тестов) +4. **Многострочные литералы** - `|` и `>` стили, chomping и indentation indicators +5. **Flow Style** - инлайн массивы `[1, 2, 3]` и объекты `{key: value}` 6. **Пустые коллекции** - `[]`, `{}`, неявные пустые значения 7. **Комментарии** - строки с `#` -8. **Якоря и ссылки** - `&anchor`, `*anchor`, `<<: *anchor` (22/25 тестов) +8. **Якоря и ссылки** - `&anchor`, `*anchor`, `<<: *anchor` 9. **Смешанные типы** - комбинации различных структур 10. **Сложные структуры** - глубокая вложенность и композиции 11. **YAML сериализация** - полная поддержка записи в YAML формат -12. **Базовое экранирование строк** - `\n`, `\t`, `\r`, `\"`, `\\`, `\/`, `\b`, `\f` (42/45 тестов) +12. **Базовое экранирование строк** - `\n`, `\t`, `\r`, `\"`, `\\`, `\/`, `\b`, `\f` 13. **Unicode поддержка** - `\uXXXX` и `\xXX` последовательности -14. **Обработка ошибок** - валидация синтаксиса и информативные сообщения (58/74 тестов) - -### 🔧 Частично реализовано - -1. **Обработка пробелов и табуляции** - базовая поддержка (75/92 тестов) - - ✅ Основные случаи отступов - - 🚧 Сложные случаи с табуляцией в различных контекстах - - 🚧 Trailing spaces в некоторых сценариях - -2. **Свернутые блоки (folded style)** - частичная поддержка (20/29 тестов) - - ✅ Базовое складывание строк с `>` - - 🚧 Сложные случаи с модификаторами и отступами - - ✅ Корректная обработка финальных переносов строк - -3. **YAML директивы** - базовая поддержка (19/25 тестов) - - ✅ Обработка `%YAML 1.2` - - 🚧 Валидация версий и дублированных директив - - 🚧 Обработка ошибок в директивах +14. **Обработка ошибок** - валидация синтаксиса и информативные сообщения +15. **Пробелы, табуляция и explicit keys** - сценарии из YAML Test Suite +16. **YAML директивы** - `%YAML`, tag handles и связанные проверки +17. **Файловые операции** - чтение YAML из файла и запись YAML в файл ### 🔄 В разработке / Планируется -1. **Чтение из файлов** - метод `ПрочитатьИзФайла(ПутьКФайлу)` - ```bsl - Парсер = Новый ПарсерYaml(); - Результат = Парсер.ПрочитатьИзФайла("config.yaml"); - ``` - -2. **Улучшение обработки пробелов и табуляции** - - Корректная обработка табуляции в отступах - - Валидация смешения пробелов и табов - - Trailing spaces в потоках - -3. **Завершение якорей и алиасов** - - Edge cases с алиасами в последовательностях - - Сложные сценарии merge +1. **Расширение удобства API** + - Настройки форматирования сериализатора + - Дополнительные вспомогательные методы для документов YAML ### 📋 Планы развития -- **Файловые операции** - `ПрочитатьИзФайла()`, `ЗаписатьВФайл()` -- **Улучшение парсинга пробелов** - завершение поддержки табуляции и trailing spaces -- **Сложные ключи** - поддержка explicit keys +- **Улучшение парсинга очень больших файлов** - контроль памяти и времени на больших YAML +- **Сложные ключи** - расширение пользовательских сценариев поверх explicit keys #### Долгосрочные планы -- **Производительность** - оптимизация для больших файлов +- **Производительность** - точечная оптимизация при появлении измеримого узкого места - **Потоковый парсинг** - обработка файлов по частям - **Плагины типов** - кастомные обработчики данных @@ -213,9 +219,9 @@ git clone https://github.com/Segate-ekb/oscript-yaml.git Проект включает обширный набор тестов на основе официального YAML Test Suite: -- **Всего тестов:** 402 обнаружено -- **Пройдено:** 355 теста (88.1%) -- **Пропущено:** 47 тестов (для функций в разработке) +- **Всего тестов:** 418 обнаружено +- **Пройдено:** 418 тестов (100%) +- **Пропущено:** 0 тестов - **Покрытие:** основные возможности YAML 1.2 ### Запуск тестов @@ -225,12 +231,33 @@ git clone https://github.com/Segate-ekb/oscript-yaml.git oneunit e # Конкретная категория -oneunit e --tagsInclude flow # Flow Style тесты (74/79 пройдено) -oneunit e --tagsInclude anchor # Якоря и ссылки (22/25 пройдено) -oneunit e --tagsInclude literal # Многострочные литералы (28/39 пройдено) -oneunit e --tagsInclude error # Обработка ошибок (58/74 пройдено) +oneunit e --tagsInclude flow # Flow Style тесты +oneunit e --tagsInclude anchor # Якоря и ссылки +oneunit e --tagsInclude literal # Многострочные литералы +oneunit e --tagsInclude error # Обработка ошибок ``` +## Нагрузочные тесты + +Для проверки работы на больших YAML в `utils` есть отдельные сценарии. Они генерируют документ из 5000 элементов с вложенными массивами и соответствиями, затем проверяют десериализацию или сериализацию. + +```bash +# Десериализация большого YAML (~665 КБ) +oscript -measure utils/load-deserialize.os + +# Сериализация большого объекта в YAML (~665 КБ) +oscript -measure utils/load-serialize.os +``` + +Контрольный прогон на текущем окружении: + +```text +utils/load-deserialize.os: 5000 элементов, 664976 байт, 00:00:17.4475616 +utils/load-serialize.os: 5000 элементов, 664976 байт, 00:00:03.1080556 +``` + +Оба сценария завершаются успешно. По результатам проверки срочная оптимизация не требуется: сериализация работает быстро для текущего объема, десериализация ожидаемо дороже и остается кандидатом только для будущей точечной оптимизации при росте требований к большим файлам. + ## 📄 Лицензия MIT License - см. файл LICENSE для подробностей. diff --git a/packagedef b/packagedef index 92b49a1..e12dff3 100644 --- a/packagedef +++ b/packagedef @@ -7,7 +7,7 @@ .Автор("Egor Ivanov") .АдресАвтора("e.ivanov@1cdevelopers.ru") .Описание("Реализация Yaml") - .ВерсияСреды("2.0.0-rc.7") + .ВерсияСреды("2.0.0") .ВключитьФайл("src") .ВключитьФайл("tests") .ОпределяетКласс("ПарсерYaml", "src/Классы/ПарсерYaml.os") diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\265\321\201\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\265\321\201\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" index 1899ed8..63a7d49 100644 --- "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\265\321\201\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\265\321\201\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" @@ -45,15 +45,16 @@ Перем Якоря; // Соответствие для хранения якорей -// Кэш для регулярных выражений для оптимизации производительности -Перем КэшРегулярныхВыражений; - -// Статический массив управляющих символов для оптимизации -Перем СтатическийМассивУправляющихСимволов; +// Статический массив валидных escape-последовательностей +Перем ВалидныеEscapeПоследовательности; // Кэш для типов строк для избежания повторных вычислений Перем КэшТиповСтрок; +// Кэш для повторно вычисляемых свойств строк в рамках одного разбора +Перем КэшОтступовСтрок; +Перем КэшУправляющихСимволов; + //////////////////////////////////////////////////////////////////////////////// // ФАБРИКА СТРУКТУР //////////////////////////////////////////////////////////////////////////////// @@ -126,6 +127,8 @@ // Очищаем кэш типов строк перед началом парсинга КэшТиповСтрок.Очистить(); + КэшОтступовСтрок.Очистить(); + КэшУправляющихСимволов.Очистить(); // Разбиваем на строки @@ -166,8 +169,15 @@ Документ = Неопределено; // Начинаем без документа Для Каждого Строка Из МассивСтрок Цикл - Результат = ОбработатьСтрокуДокумента(Строка, Документ, МассивСтрокДокументов); - Документ = Результат.Документ; + Если ЭтоДействительныйРазделительДокумента(Строка) Тогда + Лог.Отладка("Найден разделитель документа " + YAML_РАЗДЕЛИТЕЛЬ_ДОКУМЕНТА); + Документ = ОбработатьРазделительДокумента(Строка, Документ, МассивСтрокДокументов); + ИначеЕсли ЭтоДействительныйТерминаторДокумента(Строка) Тогда + Лог.Отладка("Найден терминатор документа " + YAML_ТЕРМИНАТОР_ДОКУМЕНТА); + Документ = ОбработатьТерминаторДокумента(Документ, МассивСтрокДокументов); + Иначе + Документ = ОбработатьОбычнуюСтроку(Строка, Документ); + КонецЕсли; КонецЦикла; Если Не Документ = Неопределено Тогда @@ -267,8 +277,6 @@ // Функция ОбработатьОбычнуюСтроку(Строка, Документ) - ВывестиОтладочнуюИнформациюОСтроке(Строка, Документ); - СтрокаБезПробелов = СокрЛП(Строка); // TAG директивы обрабатываем всегда, но проверяем контекст @@ -402,7 +410,16 @@ ОчищенныеСтроки = Новый Массив(); Для Каждого Строка Из Строки Цикл - ОчищеннаяСтрока = УбратьКомментарийИзСтроки(СокрЛП(Строка)); + СтрокаБезПравыхПробелов = СокрП(Строка); + Если СтрНачинаетсяС(СокрЛП(Строка), """") + И СтрЗаканчиваетсяНа(СтрокаБезПравыхПробелов, "\") + И НЕ СтрЗаканчиваетсяНа(СтрокаБезПравыхПробелов, "\\") Тогда + ОчищаемаяСтрока = СокрЛ(Строка); + Иначе + ОчищаемаяСтрока = СокрЛП(Строка); + КонецЕсли; + + ОчищеннаяСтрока = УбратьКомментарийИзСтроки(ОчищаемаяСтрока); ОчищенныеСтроки.Добавить(ОчищеннаяСтрока); КонецЦикла; @@ -418,12 +435,14 @@ // Функция ЭтоПустаяСтрокаИлиКомментарий(Знач Строка) // ОПТИМИЗАЦИЯ: Быстрая проверка без SокрЛП для пустых строк - Если СтрДлина(Строка) = 0 Тогда + ДлинаСтроки = СтрДлина(Строка); + + Если ДлинаСтроки = 0 Тогда Возврат Истина; КонецЕсли; - + // Ищем первый непробельный символ - Для Позиция = 1 По СтрДлина(Строка) Цикл + Для Позиция = 1 По ДлинаСтроки Цикл Символ = Сред(Строка, Позиция, 1); Если Символ <> " " И Символ <> Символы.Таб Тогда // Если первый непробельный символ - комментарий @@ -636,12 +655,12 @@ // Строка - Строка - строка для проверки // Процедура ПроверитьВалидностьEscapeПоследовательностей(Строка) - ВалидныеПоследовательности = ПолучитьВалидныеEscapeПоследовательности(); - Позиция = 1; - Пока Позиция <= СтрДлина(Строка) Цикл + ДлинаСтроки = СтрДлина(Строка); + + Пока Позиция <= ДлинаСтроки Цикл Если Сред(Строка, Позиция, 1) = "\" Тогда - Позиция = ОбработатьBackslash(Строка, Позиция, ВалидныеПоследовательности); + Позиция = ОбработатьBackslash(Строка, Позиция, ВалидныеEscapeПоследовательности); КонецЕсли; Позиция = Позиция + 1; КонецЦикла; @@ -653,24 +672,6 @@ // Массив - массив валидных escape-последовательностей // Функция ПолучитьВалидныеEscapeПоследовательности() - ВалидныеEscapeПоследовательности = Новый Массив; - ВалидныеEscapeПоследовательности.Добавить("\"""); - ВалидныеEscapeПоследовательности.Добавить("\\"); - ВалидныеEscapeПоследовательности.Добавить("\/"); - ВалидныеEscapeПоследовательности.Добавить("\b"); - ВалидныеEscapeПоследовательности.Добавить("\f"); - ВалидныеEscapeПоследовательности.Добавить("\n"); - ВалидныеEscapeПоследовательности.Добавить("\r"); - ВалидныеEscapeПоследовательности.Добавить("\t"); - ВалидныеEscapeПоследовательности.Добавить("\ "); - ВалидныеEscapeПоследовательности.Добавить("\0"); - ВалидныеEscapeПоследовательности.Добавить("\a"); - ВалидныеEscapeПоследовательности.Добавить("\e"); - ВалидныеEscapeПоследовательности.Добавить("\_"); - ВалидныеEscapeПоследовательности.Добавить("\L"); - ВалидныеEscapeПоследовательности.Добавить("\P"); - ВалидныеEscapeПоследовательности.Добавить("\" + Символы.Таб); - Возврат ВалидныеEscapeПоследовательности; КонецФункции @@ -716,10 +717,14 @@ // Число - новая позиция или исходная позиция если не найдено // Функция НайтиВалиднуюПоследовательность(Строка, Позиция, ВалидныеПоследовательности) + ДлинаСтроки = СтрДлина(Строка); + Для Каждого ВалиднаяПоследовательность Из ВалидныеПоследовательности Цикл - Если Позиция + СтрДлина(ВалиднаяПоследовательность) - 1 <= СтрДлина(Строка) Тогда - Если Сред(Строка, Позиция, СтрДлина(ВалиднаяПоследовательность)) = ВалиднаяПоследовательность Тогда - Возврат Позиция + СтрДлина(ВалиднаяПоследовательность) - 1; + ДлинаПоследовательности = СтрДлина(ВалиднаяПоследовательность); + + Если Позиция + ДлинаПоследовательности - 1 <= ДлинаСтроки Тогда + Если Сред(Строка, Позиция, ДлинаПоследовательности) = ВалиднаяПоследовательность Тогда + Возврат Позиция + ДлинаПоследовательности - 1; КонецЕсли; КонецЕсли; КонецЦикла; @@ -1003,8 +1008,6 @@ // "FlowОбъект", "МногострочнаяСтрока", Неопределено // Функция ОпределитьТипСодержимого(Строки) - Лог.Отладка("Определение типа содержимого для " + Строки.Количество() + " строк"); - // ОПТИМИЗАЦИЯ: Быстрый выход для частых случаев Если Строки.Количество() = 0 Тогда Лог.Предупреждение("Не найдено ни одной строки с данными"); @@ -1020,7 +1023,6 @@ // Если это не пустая строка, то управляющий символ должен быть в ней. // Если мы не нашли его, то это Строка или число, или что-то другое. ТипДанных = НайтиТипДанныхПоСтроке(Строка); - Лог.Отладка("Определен тип содержимого: " + ТипДанных + " по строке: " + Строка); Возврат ТипДанных; КонецЦикла; @@ -1088,31 +1090,30 @@ Символ = ДанныеУправляющегоСимвола.Символ; Если Символ = "[" Тогда - Лог.Отладка("Найден символ flow последовательности: ["); Возврат "FlowПоследовательность"; ИначеЕсли Символ = "{" Тогда - Лог.Отладка("Найден символ flow объекта: {"); Возврат "FlowОбъект"; ИначеЕсли Символ = ":" Тогда - Лог.Отладка("Найден символ объекта: :"); Возврат "Объект"; ИначеЕсли Символ = "-" Тогда - Лог.Отладка("Найден символ массива: -"); Возврат "Массив"; ИначеЕсли Символ = "|" ИЛИ Символ = ">" Тогда - Лог.Отладка("Найден символ многострочной строки: " + Символ); Возврат "МногострочнаяСтрока"; ИначеЕсли Символ = "&" Тогда - Лог.Отладка("Найден символ якоря: &"); + Если НайтиДвоеточиеКлючаПослеЯкоря(ИсходнаяСтрока, ДанныеУправляющегоСимвола.Позиция) > 0 Тогда + Возврат "Объект"; + КонецЕсли; + Возврат "Якорь"; ИначеЕсли Символ = "*" Тогда - Лог.Отладка("Найден символ алиаса: *"); + Если НайтиДвоеточиеКлючаПослеАлиаса(ИсходнаяСтрока, ДанныеУправляющегоСимвола.Позиция) > 0 Тогда + Возврат "Объект"; + КонецЕсли; + Возврат "Алиас"; ИначеЕсли Символ = "!" Тогда - Лог.Отладка("Найден символ тега: !"); Возврат "Тег"; ИначеЕсли Символ = "?" Тогда - Лог.Отладка("Найден символ явного ключа: ?"); Возврат "Объект"; Иначе Лог.Ошибка("Не удалось определить тип данных в строке: " + ИсходнаяСтрока); @@ -1138,7 +1139,6 @@ // Проверяем директивы Если ЯвляетсяДирективой(ОчищеннаяСтрока) Тогда - Лог.Отладка("Найдена директива, обрабатываем как скаляр: " + ОчищеннаяСтрока); КэшТиповСтрок[Строка] = "Скаляр"; Возврат "Скаляр"; КонецЕсли; @@ -1150,7 +1150,6 @@ // Проверяем многострочные строки (приоритет перед тегами) Если ЭтоМногострочнаяСтрока(ДанныеУправляющегоСимвола) Тогда - Лог.Отладка("Найден символ многострочной строки: " + ДанныеУправляющегоСимвола.Символ); КэшТиповСтрок[Строка] = "МногострочнаяСтрока"; Возврат "МногострочнаяСтрока"; КонецЕсли; @@ -1177,7 +1176,6 @@ КонецЕсли; // По умолчанию - скаляр - Лог.Отладка("Управляющий символ не найден, определен как скаляр"); КэшТиповСтрок[Строка] = "Скаляр"; Возврат "Скаляр"; КонецФункции @@ -1353,34 +1351,136 @@ // Возвращаемое значение: // Структура - данные управляющего символа Функция НайтиУправляющийСимволИЕгоПозицию(Знач Строка) - // ОПТИМИЗАЦИЯ: Используем кэшированный массив управляющих символов + КэшированноеЗначение = КэшУправляющихСимволов.Получить(Строка); + Если КэшированноеЗначение <> Неопределено Тогда + Возврат КэшированноеЗначение; + КонецЕсли; + СтруктураВозврата = СоздатьСтруктуруУправляющегоСимвола(); - - // Используем кэшированный массив управляющих символов - МинимальнаяНайденнаяПозиция = 9999999; - - // Ищем каждый символ с помощью оптимизированной функции - Для каждого Символ Из СтатическийМассивУправляющихСимволов Цикл - НайденнаяПозиция = ПозицияНеЭкранированногоСимвола(Строка, Символ); - - Если НайденнаяПозиция > 0 И МинимальнаяНайденнаяПозиция > НайденнаяПозиция Тогда - - Если НужноПропуститьСимвол(Строка, Символ, НайденнаяПозиция) Тогда + + БыстрыйРезультат = НайтиУправляющийСимволВПростойСтроке(Строка, СтруктураВозврата); + Если БыстрыйРезультат.Позиция > 0 Тогда + КэшУправляющихСимволов.Вставить(Строка, БыстрыйРезультат); + Возврат БыстрыйРезультат; + КонецЕсли; + + ВКавычках = Ложь; + ТипКавычек = ""; + ДлинаСтроки = СтрДлина(Строка); + + Для Позиция = 1 По ДлинаСтроки Цикл + Символ = Сред(Строка, Позиция, 1); + + Если НЕ ВКавычках И (Символ = """" ИЛИ Символ = "'") Тогда + Если Позиция = 1 ИЛИ (Позиция > 1 И (Сред(Строка, Позиция - 1, 1) = " " ИЛИ Сред(Строка, Позиция - 1, 1) = Символы.Таб)) Тогда + ВКавычках = Истина; + ТипКавычек = Символ; Продолжить; КонецЕсли; - - // Проверка на табы после структурных символов - ПроверитьТабыПослеСтруктурныхСимволов(Строка, Символ, НайденнаяПозиция); - - СтруктураВозврата.Символ = Символ; - СтруктураВозврата.Позиция = НайденнаяПозиция; - МинимальнаяНайденнаяПозиция = НайденнаяПозиция; + ИначеЕсли ВКавычках И Символ = ТипКавычек Тогда + Если ТипКавычек = """" Тогда + КоличествоСлешей = 0; + ПроверочныйИндекс = Позиция - 1; + Пока ПроверочныйИндекс >= 1 И Сред(Строка, ПроверочныйИндекс, 1) = "\" Цикл + КоличествоСлешей = КоличествоСлешей + 1; + ПроверочныйИндекс = ПроверочныйИндекс - 1; + КонецЦикла; + + Если КоличествоСлешей % 2 = 0 Тогда + ВКавычках = Ложь; + ТипКавычек = ""; + КонецЕсли; + ИначеЕсли Позиция < ДлинаСтроки И Сред(Строка, Позиция + 1, 1) = "'" Тогда + Продолжить; + Иначе + ВКавычках = Ложь; + ТипКавычек = ""; + КонецЕсли; + КонецЕсли; + + Если ВКавычках Тогда + Продолжить; + КонецЕсли; + + Если НЕ ЭтоУправляющийСимвол(Символ) Тогда + Продолжить; + КонецЕсли; + + Если НужноПропуститьСимвол(Строка, Символ, Позиция) Тогда + Продолжить; КонецЕсли; + + Если НЕ ЭтоЗначащийСимвол(Строка, Позиция, Символ) Тогда + Продолжить; + КонецЕсли; + + ПроверитьТабыПослеСтруктурныхСимволов(Строка, Символ, Позиция); + + СтруктураВозврата.Символ = Символ; + СтруктураВозврата.Позиция = Позиция; + КэшУправляющихСимволов.Вставить(Строка, СтруктураВозврата); + Возврат СтруктураВозврата; КонецЦикла; + КэшУправляющихСимволов.Вставить(Строка, СтруктураВозврата); Возврат СтруктураВозврата; КонецФункции +Функция НайтиУправляющийСимволВПростойСтроке(Строка, СтруктураВозврата) + Если СтрНайти(Строка, """") > 0 ИЛИ СтрНайти(Строка, "'") > 0 Тогда + Возврат СтруктураВозврата; + КонецЕсли; + + ОчищеннаяСтрока = СокрЛП(Строка); + Если ПустаяСтрока(ОчищеннаяСтрока) Тогда + Возврат СтруктураВозврата; + КонецЕсли; + + ПервыйСимвол = Лев(ОчищеннаяСтрока, 1); + Если ПервыйСимвол = "-" Тогда + ПозицияТире = СтрНайти(Строка, "-"); + Если ЭтоЗначащийСимвол(Строка, ПозицияТире, "-") Тогда + ПроверитьТабыПослеСтруктурныхСимволов(Строка, "-", ПозицияТире); + СтруктураВозврата.Символ = "-"; + СтруктураВозврата.Позиция = ПозицияТире; + КонецЕсли; + Возврат СтруктураВозврата; + КонецЕсли; + + Если ПервыйСимвол = "[" + ИЛИ ПервыйСимвол = "{" + ИЛИ ПервыйСимвол = "|" + ИЛИ ПервыйСимвол = ">" + ИЛИ ПервыйСимвол = "&" + ИЛИ ПервыйСимвол = "*" + ИЛИ ПервыйСимвол = "!" + ИЛИ ПервыйСимвол = "?" Тогда + Возврат СтруктураВозврата; + КонецЕсли; + + ПозицияДвоеточия = СтрНайти(Строка, ":"); + Если ПозицияДвоеточия > 0 И ЭтоЗначащийСимвол(Строка, ПозицияДвоеточия, ":") Тогда + ПроверитьТабыПослеСтруктурныхСимволов(Строка, ":", ПозицияДвоеточия); + СтруктураВозврата.Символ = ":"; + СтруктураВозврата.Позиция = ПозицияДвоеточия; + КонецЕсли; + + Возврат СтруктураВозврата; +КонецФункции + +Функция ЭтоУправляющийСимвол(Символ) + Возврат Символ = ":" + ИЛИ Символ = "-" + ИЛИ Символ = "[" + ИЛИ Символ = "{" + ИЛИ Символ = "|" + ИЛИ Символ = ">" + ИЛИ Символ = "&" + ИЛИ Символ = "*" + ИЛИ Символ = "!" + ИЛИ Символ = "?"; +КонецФункции + // Проверяет, что символ не находится в кавычках и не экранирован // // Параметры: @@ -1515,18 +1615,14 @@ // Булево - Истина, если символ значащий // Функция БыстраяПроверкаЗначащегоСимвола(ИскомыйСимвол, Позиция, Строка, ЭтоFlowСтиль, ДлинаСтроки) - // Используем кэшированные регулярные выражения для более эффективной проверки - Если ИскомыйСимвол = "#" Тогда // Комментарий должен быть в начале или после пробела/таба Если Позиция = 1 Тогда Возврат Истина; КонецЕсли; - // Проверяем предыдущий символ с помощью кэшированного регулярного выражения ПредыдущийСимвол = Сред(Строка, Позиция - 1, 1); - РегВыражение = КэшРегулярныхВыражений.Получить("пробелы"); - Возврат РегВыражение.Совпадает(ПредыдущийСимвол); + Возврат ЭтоПробелИлиТаб(ПредыдущийСимвол); ИначеЕсли ИскомыйСимвол = ":" Тогда Если ЭтоFlowСтиль Тогда @@ -1538,10 +1634,8 @@ Возврат Истина; КонецЕсли; - // Проверяем следующий символ с помощью кэшированного регулярного выражения СледующийСимвол = Сред(Строка, Позиция + 1, 1); - РегВыражение = КэшРегулярныхВыражений.Получить("пробелы"); - Возврат РегВыражение.Совпадает(СледующийСимвол); + Возврат ЭтоПробелИлиТаб(СледующийСимвол); КонецЕсли; Иначе // Для других символов используем полную проверку @@ -1549,35 +1643,28 @@ КонецЕсли; КонецФункции +Функция ЭтоПробелИлиТаб(Символ) + Возврат Символ = " " ИЛИ Символ = Символы.Таб; +КонецФункции + +Функция ЭтоПробелТабЦифраИлиМодификаторБлочногоСкаляра(Символ) + Возврат Символ = " " + ИЛИ Символ = Символы.Таб + ИЛИ (Символ >= "0" И Символ <= "9") + ИЛИ Символ = "+" + ИЛИ Символ = "-"; +КонецФункции + Функция ПозицияНеЭкранированногоСимвола(Строка, ИскомыйСимвол, ЭтоFlowСтиль = Ложь) - // ОПТИМИЗАЦИЯ: Используем кэшированные регулярные выражения и оптимизированный поиск - - // Экранируем специальные символы регулярных выражений - ЭкранированныйСимвол = ИскомыйСимвол; - СпециальныеСимволы = КэшРегулярныхВыражений.Получить("спецСимволы"); - Если СтрНайти(СпециальныеСимволы, ИскомыйСимвол) > 0 Тогда - ЭкранированныйСимвол = "\" + ИскомыйСимвол; - КонецЕсли; - // Быстрый путь для строк без кавычек Если СтрНайти(Строка, """") = 0 И СтрНайти(Строка, "'") = 0 Тогда - // Используем простое регулярное выражение для поиска - ПаттернПростойПоиск = ЭкранированныйСимвол; - - // Попробуем получить регулярное выражение из кэша - КлючКэша = "символ_" + ИскомыйСимвол; - РегВыражение = КэшРегулярныхВыражений.Получить(КлючКэша); - Если РегВыражение = Неопределено Тогда - РегВыражение = Новый РегулярноеВыражение(ПаттернПростойПоиск); - КэшРегулярныхВыражений.Вставить(КлючКэша, РегВыражение); - КонецЕсли; - - Совпадения = РегВыражение.НайтиСовпадения(Строка); - Для каждого Совпадение Из Совпадения Цикл - Позиция = Совпадение.Индекс + 1; // +1 для перевода из индексации с 0 в индексацию с 1 - ДлинаСтроки = СтрДлина(Строка); - Если БыстраяПроверкаЗначащегоСимвола(ИскомыйСимвол, Позиция, Строка, ЭтоFlowСтиль, ДлинаСтроки) Тогда - Возврат Позиция; + ДлинаСтроки = СтрДлина(Строка); + + Для Позиция = 1 По ДлинаСтроки Цикл + Если Сред(Строка, Позиция, 1) = ИскомыйСимвол Тогда + Если БыстраяПроверкаЗначащегоСимвола(ИскомыйСимвол, Позиция, Строка, ЭтоFlowСтиль, ДлинаСтроки) Тогда + Возврат Позиция; + КонецЕсли; КонецЕсли; КонецЦикла; Возврат 0; @@ -1598,31 +1685,12 @@ // Число - позиция найденного символа или 0 // Функция НайтиСимволВнеКавычек(Строка, ИскомыйСимвол, ЭтоFlowСтиль) - // Экранируем специальные символы регулярных выражений - ЭкранированныйСимвол = ИскомыйСимвол; - СпециальныеСимволы = КэшРегулярныхВыражений.Получить("спецСимволы"); - Если СтрНайти(СпециальныеСимволы, ИскомыйСимвол) > 0 Тогда - ЭкранированныйСимвол = "\" + ИскомыйСимвол; - КонецЕсли; - - // Паттерн для поиска символа вне кавычек - // Используем кэшированные регулярные выражения - КлючКэша = "вне_кавычек_" + ИскомыйСимвол; - РегВыражение = КэшРегулярныхВыражений.Получить(КлючКэша); - Если РегВыражение = Неопределено Тогда - РегВыражение = Новый РегулярноеВыражение(ЭкранированныйСимвол); - КэшРегулярныхВыражений.Вставить(КлючКэша, РегВыражение); - КонецЕсли; - - Совпадения = РегВыражение.НайтиСовпадения(Строка); ДлинаСтроки = СтрДлина(Строка); - Для каждого Совпадение Из Совпадения Цикл - Позиция = Совпадение.Индекс + 1; // +1 для перевода из индексации с 0 в индексацию с 1 - - // Проверяем, что символ не в кавычках - Если НеВКавычкахИНеЭкранирован(Строка, Позиция) Тогда - Если БыстраяПроверкаЗначащегоСимвола(ИскомыйСимвол, Позиция, Строка, ЭтоFlowСтиль, ДлинаСтроки) Тогда + Для Позиция = 1 По ДлинаСтроки Цикл + Если Сред(Строка, Позиция, 1) = ИскомыйСимвол Тогда + Если НеВКавычкахИНеЭкранирован(Строка, Позиция) + И БыстраяПроверкаЗначащегоСимвола(ИскомыйСимвол, Позиция, Строка, ЭтоFlowСтиль, ДлинаСтроки) Тогда Возврат Позиция; КонецЕсли; КонецЕсли; @@ -1653,16 +1721,13 @@ // Возвращаемое значение: // Булево - Истина, если символ является началом комментария Функция ЭтоЗначащийКомментарий(Строка, Позиция) - // Используем регулярные выражения для проверки предыдущего символа Если Позиция = 1 Тогда Возврат Истина; // Комментарий в начале строки КонецЕсли; - + ПредыдущийСимвол = Сред(Строка, Позиция - 1, 1); - ПаттернПробелов = "[ \t]"; - РегВыражение = Новый РегулярноеВыражение(ПаттернПробелов); - - Возврат РегВыражение.Совпадает(ПредыдущийСимвол); + + Возврат ЭтоПробелИлиТаб(ПредыдущийСимвол); КонецФункции // Проверяет является ли двоеточие значащим в flow стиле @@ -1695,13 +1760,10 @@ Если Позиция = СтрДлина(Строка) Тогда Возврат Истина; // Двоеточие в конце строки КонецЕсли; - - // Используем регулярные выражения для проверки следующего символа + СледующийСимвол = Сред(Строка, Позиция + 1, 1); - ПаттернПробелов = "[ \t]"; - РегВыражение = Новый РегулярноеВыражение(ПаттернПробелов); - - Возврат РегВыражение.Совпадает(СледующийСимвол); + + Возврат ЭтоПробелИлиТаб(СледующийСимвол); КонецФункции Функция ЭтоЗначащийСимвол(Строка, Позиция, ИскомыйСимвол, ЭтоFlowСтиль = Ложь) @@ -1741,18 +1803,12 @@ СледующийСимвол = Сред(Строка, Позиция + 1, 1); - // Используем регулярные выражения для проверки допустимых символов Если ЭтоМногострочнаяСтрока Тогда - // Паттерн для многострочных строк: пробелы, табы, цифры, + и - - ПаттернДопустимых = "[ \t0-9\+\-]"; + ЭтоЗначащийСимвол = ЭтоПробелТабЦифраИлиМодификаторБлочногоСкаляра(СледующийСимвол); Иначе - // Паттерн для обычных случаев: только пробелы и табы - ПаттернДопустимых = "[ \t]"; + ЭтоЗначащийСимвол = ЭтоПробелИлиТаб(СледующийСимвол); КонецЕсли; - - РегВыражение = Новый РегулярноеВыражение(ПаттернДопустимых); - ЭтоЗначащийСимвол = РегВыражение.Совпадает(СледующийСимвол); - + Возврат ЭтоЗначащийСимвол; КонецФункции @@ -1770,8 +1826,6 @@ // Произвольный - обработанное скалярное значение // Функция ОбработатьСкалярноеЗначение(Строки) - Лог.Отладка("Обработка скалярного значения"); - // Проверяем на недопустимые комментарии в plain scalar ПроверитьКомментарииВPlainScalar(Строки); @@ -1785,7 +1839,6 @@ // Проверяем специальные значения СпециальноеЗначение = Неопределено; Если ПреобразоватьСпециальноеЗначение(ИтоговаяСтрока, СпециальноеЗначение) Тогда - Лог.Отладка("Скаляр преобразован в специальное значение: " + СпециальноеЗначение); Возврат СпециальноеЗначение; КонецЕсли; @@ -1794,7 +1847,6 @@ Возврат ОбработатьЧисловоеЗначение(ИтоговаяСтрока); КонецЕсли; - Лог.Отладка("Скаляр обработан как строка: " + ИтоговаяСтрока); Возврат ИтоговаяСтрока; КонецФункции @@ -1979,11 +2031,9 @@ // Проверяем на шестнадцатеричное число Если СтрНачинаетсяС(ВРег(СокрЛП(ИтоговаяСтрока)), "0X") Тогда Результат = ПреобразоватьИзШестнадцатеричной(ИтоговаяСтрока); - Лог.Отладка("Скаляр преобразован из шестнадцатеричного в число: " + Результат); Возврат Результат; Иначе Результат = Число(ИтоговаяСтрока); - Лог.Отладка("Скаляр преобразован в число: " + Результат); Возврат Результат; КонецЕсли; КонецФункции @@ -1997,7 +2047,6 @@ // Произвольный - обработанные данные Функция ДанныеИзМассиваСтрок(Строки) ТипСодержимого = ОпределитьТипСодержимого(Строки); - Лог.Отладка("Обработка данных типа: " + ТипСодержимого); Если ТипСодержимого = "Объект" Тогда Возврат ПарсерОбъектов(Строки); @@ -2156,12 +2205,14 @@ // Булево - Истина, если строка является шестнадцатеричным числом // Функция ЭтоШестнадцатеричноеЧисло(Строка) - Если НЕ (СтрНачинаетсяС(ВРег(Строка), "0X") И СтрДлина(Строка) > ДЛИНА_ПРЕФИКСА_ШЕСТНАДЦАТЕРИЧНОГО_ЧИСЛА) Тогда + ДлинаСтроки = СтрДлина(Строка); + + Если НЕ (СтрНачинаетсяС(ВРег(Строка), "0X") И ДлинаСтроки > ДЛИНА_ПРЕФИКСА_ШЕСТНАДЦАТЕРИЧНОГО_ЧИСЛА) Тогда Возврат Ложь; КонецЕсли; - + // Проверяем остальные символы как шестнадцатеричные - Для Позиция = 3 По СтрДлина(Строка) Цикл + Для Позиция = 3 По ДлинаСтроки Цикл Символ = ВРег(Сред(Строка, Позиция, 1)); Если НЕ ((Символ >= "0" И Символ <= "9") ИЛИ (Символ >= "A" И Символ <= "F")) Тогда Возврат Ложь; @@ -2197,10 +2248,12 @@ Если ВРег(Строка) = "E" Или ВРег(Строка) = "E+" Или ВРег(Строка) = "E-" Тогда Возврат Ложь; КонецЕсли; - + + ДлинаСтроки = СтрДлина(Строка); + // Проверяем, что есть хотя бы одна цифра СодержитЦифру = Ложь; - Для Позиция = 1 По СтрДлина(Строка) Цикл + Для Позиция = 1 По ДлинаСтроки Цикл Символ = Сред(Строка, Позиция, 1); Если Символ >= "0" И Символ <= "9" Тогда СодержитЦифру = Истина; @@ -2211,9 +2264,9 @@ Если НЕ СодержитЦифру Тогда Возврат Ложь; КонецЕсли; - + // Проверяем каждый символ - Для Позиция = 1 По СтрДлина(Строка) Цикл + Для Позиция = 1 По ДлинаСтроки Цикл Символ = Сред(Строка, Позиция, 1); Если НЕ ДопустимыйСимволЧисла(Символ, Позиция) Тогда Возврат Ложь; @@ -2303,19 +2356,14 @@ // Соответствие - результат парсинга объектов // Функция ПарсерОбъектов(Строки) - Лог.Отладка("Начало парсинга объектов"); - МассивОбъектов = ПолучитьМассивСущностей(Строки); СоответствиеОбъектов = Новый Соответствие(); - - Лог.Отладка("Найдено " + МассивОбъектов.Количество() + " объектов для обработки"); // Обрабатываем объекты в порядке документа Для Каждого Объект Из МассивОбъектов Цикл ОбработатьОбъект(Объект, СоответствиеОбъектов); КонецЦикла; - Лог.Отладка("Парсинг объектов завершен, получено " + СоответствиеОбъектов.Количество() + " свойств"); Возврат СоответствиеОбъектов; КонецФункции @@ -2375,8 +2423,6 @@ // СоответствиеОбъектов - Соответствие - результирующее соответствие // Процедура ОбработатьОбычныйОбъект(Объект, СоответствиеОбъектов) - Лог.Отладка("Обработка объекта с ключом: " + Объект.ИмяОбъекта); - // Проверяем, есть ли якорь в ключе объекта ЕстьЯкорьВКлюче = СтрНайти(Объект.ИмяОбъекта, "&") > 0; @@ -2398,6 +2444,15 @@ // Строка - итоговый ключ для использования // Функция ОпределитьКлючОбъекта(ИмяОбъекта) + Если СтрНачинаетсяС(СокрЛП(ИмяОбъекта), "*") Тогда + ИмяАлиаса = ИзвлечьИмяЯкоряИлиАлиаса(ИмяОбъекта); + Если НЕ ЯкорьСуществует(ИмяАлиаса) Тогда + ВызватьИсключение "Не найден алиас: " + ИмяАлиаса; + КонецЕсли; + + Возврат Строка(ПолучитьЗначениеЯкоря(ИмяАлиаса)); + КонецЕсли; + // Обрабатываем ключ как YAML значение (может содержать теги) только если он содержит тег Если СтрНайти(ИмяОбъекта, "!") > 0 Тогда МассивКлюча = Новый Массив; @@ -2600,7 +2655,8 @@ СтрокиЗначения, НайденДвоеточие, МногострочныйКлюч, - ПропуститьТекущуюСтроку) + ПропуститьТекущуюСтроку, + БазовыйОтступЗначения) Для Каждого Строка Из Объект.Содержимое Цикл СтрокаБезОтступов = СокрЛП(Строка); @@ -2614,7 +2670,7 @@ ПропуститьТекущуюСтроку); Иначе // Двоеточие уже найдено, все остальное - часть значения - ОбработатьДобавлениеЗначений(Строка, СтрокиЗначения); + ОбработатьДобавлениеЗначений(Строка, СтрокиЗначения, БазовыйОтступЗначения); КонецЕсли; КонецЦикла; КонецПроцедуры @@ -2681,6 +2737,7 @@ Результат.Вставить("НайденДвоеточие", Ложь); Результат.Вставить("МногострочныйКлюч", Ложь); Результат.Вставить("ПропуститьТекущуюСтроку", Ложь); + Результат.Вставить("БазовыйОтступЗначения", -1); Возврат Результат; КонецФункции @@ -2690,10 +2747,24 @@ // Строка - Строка - строка для добавления к значению // СтрокиЗначения - Массив - массив строк значения (передается по ссылке) // -Процедура ОбработатьДобавлениеЗначений(Строка, СтрокиЗначения) +Процедура ОбработатьДобавлениеЗначений(Строка, СтрокиЗначения, БазовыйОтступЗначения = -1) СтрокаБезОтступов = СокрЛП(Строка); Если НЕ ЭтоПустаяСтрокаИлиКомментарий(Строка) Тогда - СтрокиЗначения.Добавить(СтрокаБезОтступов); + Если СтрокиЗначения.Количество() > 0 + И СтрНачинаетсяС(СтрокиЗначения[0], "-") + И ОпределитьОтступ(Строка) > 0 Тогда + Если БазовыйОтступЗначения < 0 Тогда + БазовыйОтступЗначения = ОпределитьОтступ(Строка); + КонецЕсли; + + Если ОпределитьОтступ(Строка) >= БазовыйОтступЗначения Тогда + СтрокиЗначения.Добавить(Сред(Строка, БазовыйОтступЗначения + 1)); + Иначе + СтрокиЗначения.Добавить(СтрокаБезОтступов); + КонецЕсли; + Иначе + СтрокиЗначения.Добавить(СтрокаБезОтступов); + КонецЕсли; КонецЕсли; КонецПроцедуры @@ -2825,6 +2896,7 @@ НайденДвоеточие = Переменные.НайденДвоеточие; ПропуститьТекущуюСтроку = Переменные.ПропуститьТекущуюСтроку; МногострочныйКлюч = ""; + БазовыйОтступЗначения = Переменные.БазовыйОтступЗначения; // Обрабатываем содержимое для извлечения ключа и значения ОбработатьЦиклРазбораСодержимого(Объект, @@ -2832,7 +2904,8 @@ СтрокиЗначения, НайденДвоеточие, МногострочныйКлюч, - ПропуститьТекущуюСтроку); + ПропуститьТекущуюСтроку, + БазовыйОтступЗначения); Возврат СоздатьФинальныйРезультат(СтрокиКлюча, СтрокиЗначения, МногострочныйКлюч); КонецЕсли; @@ -3490,19 +3563,13 @@ // Массив - результат парсинга массива // Функция ПарсерМассивов(Строки) - Лог.Отладка("Начало парсинга массива"); - МассивЭлементов = ПолучитьМассивСущностей(Строки); МассивРезультат = Новый Массив(); - Лог.Отладка("Найдено " + МассивЭлементов.Количество() + " элементов массива"); - Для Каждого Элемент Из МассивЭлементов Цикл - Лог.Отладка("Обработка элемента массива"); МассивРезультат.Добавить(ДанныеИзМассиваСтрок(Элемент.Содержимое)); КонецЦикла; - Лог.Отладка("Парсинг массива завершен, получено " + МассивРезультат.Количество() + " элементов"); Возврат МассивРезультат; КонецФункции @@ -3549,7 +3616,6 @@ Функция ОбработатьОбъектСДвоеточием(Строка, ПозицияДвоеточия, ТекущийОтступ) ИмяОбъекта = СокрЛП(Лев(Строка, ПозицияДвоеточия - 1)); Сущность = СоздатьСтруктуруСущности("Объект", ИмяОбъекта, ТекущийОтступ + 1); - Лог.Отладка("Найден объект: " + ИмяОбъекта); // Определяем, есть ли значение на той же строке ОстатокСтроки = СокрЛП(Сред(Строка, ПозицияДвоеточия + 1)); @@ -3576,6 +3642,10 @@ Если ПустаяСтрока(ОстатокСтроки) Тогда Возврат Ложь; КонецЕсли; + + Если ЭтоОдинокийЯкорь(ОстатокСтроки) Тогда + Возврат Ложь; + КонецЕсли; // Если остаток строки не пустой, проверяем, что это не только тег Если НЕ СтрНачинаетсяС(ОстатокСтроки, "!") Тогда @@ -3587,6 +3657,15 @@ КонецЕсли; КонецФункции +Функция ЭтоОдинокийЯкорь(Строка) + ОчищеннаяСтрока = СокрЛП(Строка); + Если НЕ СтрНачинаетсяС(ОчищеннаяСтрока, "&") Тогда + Возврат Ложь; + КонецЕсли; + + Возврат ПустаяСтрока(УдалитьОпределениеЯкоря(ОчищеннаяСтрока)); +КонецФункции + // Проверяет, есть ли значение после тега в строке // // Параметры: @@ -3634,7 +3713,14 @@ // МодифицированнаяСтрока - Строка - модифицированная строка // ОригинальнаяСтрока - Строка - оригинальная строка для вывода ошибки // -Процедура ПроверитьОтсутствиеДублирующихСимволов(МодифицированнаяСтрока, ОригинальнаяСтрока) +Процедура ПроверитьОтсутствиеДублирующихСимволов(Знач МодифицированнаяСтрока, ОригинальнаяСтрока) + ОчищеннаяСтрока = СокрЛП(МодифицированнаяСтрока); + Если СтрНачинаетсяС(ОчищеннаяСтрока, "&") Тогда + МодифицированнаяСтрока = УдалитьОпределениеЯкоря(МодифицированнаяСтрока); + ИначеЕсли СтрНачинаетсяС(ОчищеннаяСтрока, "*") Тогда + МодифицированнаяСтрока = УдалитьОпределениеАлиаса(МодифицированнаяСтрока); + КонецЕсли; + СледующийУправляющийСимвол = НайтиУправляющийСимволИЕгоПозицию(МодифицированнаяСтрока); Если СледующийУправляющийСимвол.Позиция > 0 @@ -3645,6 +3731,61 @@ КонецЕсли; КонецПроцедуры +// Ищет двоеточие ключа после имени якоря +// +// Параметры: +// Строка - Строка - исходная строка +// ПозицияЯкоря - Число - позиция символа & +// +// Возвращаемое значение: +// Число - позиция двоеточия ключа или 0 +// +Функция НайтиДвоеточиеКлючаПослеЯкоря(Строка, ПозицияЯкоря) + НачалоКлюча = 0; + + Для Позиция = ПозицияЯкоря + 1 По СтрДлина(Строка) Цикл + Символ = Сред(Строка, Позиция, 1); + Если Символ = " " ИЛИ Символ = Символы.Таб Тогда + НачалоКлюча = Позиция + 1; + Прервать; + КонецЕсли; + КонецЦикла; + + Если НачалоКлюча = 0 Тогда + Возврат 0; + КонецЕсли; + + ОчищенныйОстаток = СокрЛП(Сред(Строка, НачалоКлюча)); + Если СтрНачинаетсяС(ОчищенныйОстаток, "{") ИЛИ СтрНачинаетсяС(ОчищенныйОстаток, "[") Тогда + Возврат 0; + КонецЕсли; + + Для Позиция = НачалоКлюча По СтрДлина(Строка) Цикл + Символ = Сред(Строка, Позиция, 1); + Если Символ = ":" И ЭтоЗначащееДвоеточиеВБлокСтиле(Строка, Позиция) Тогда + Возврат Позиция; + КонецЕсли; + КонецЦикла; + + Возврат 0; +КонецФункции + +Функция НайтиДвоеточиеКлючаПослеАлиаса(Строка, ПозицияАлиаса) + Для Позиция = ПозицияАлиаса + 1 По СтрДлина(Строка) Цикл + Символ = Сред(Строка, Позиция, 1); + Если Символ = ":" И ЭтоЗначащееДвоеточиеВБлокСтиле(Строка, Позиция) Тогда + ПредыдущийСимвол = Сред(Строка, Позиция - 1, 1); + Если ПредыдущийСимвол <> " " И ПредыдущийСимвол <> Символы.Таб Тогда + Продолжить; + КонецЕсли; + + Возврат Позиция; + КонецЕсли; + КонецЦикла; + + Возврат 0; +КонецФункции + // Обрабатывает объект с явным ключом (символ ?) // // Параметры: @@ -3677,7 +3818,6 @@ // Структура - сущность и модифицированная строка Функция ОбработатьЭлементМассива(Строка, ПозицияТире, ТекущийОтступ) Сущность = СоздатьСтруктуруСущности("ЭлементМассива", "ЭлементМассива", ТекущийОтступ + 1); - Лог.Отладка("Найден элемент массива"); МодифицированнаяСтрока = ЗаменитьВСтрокеПрефиксОпределенияНаПробелы(Строка, ПозицияТире); @@ -3695,26 +3835,23 @@ // Возвращаемое значение: // Структура - сущность и модифицированная строка Функция ОбработатьЯкорь(Строка, ПозицияЯкоря, ТекущийОтступ) - // Ищем следующий управляющий символ после якоря - ОстатокСтроки = Сред(Строка, ПозицияЯкоря + 1); - СледующийСимвол = НайтиУправляющийСимволИЕгоПозицию(ОстатокСтроки); + ПозицияДвоеточияПослеКлюча = НайтиДвоеточиеКлючаПослеЯкоря(Строка, ПозицияЯкоря); - Если СледующийСимвол.Символ = ":" Тогда + Если ПозицияДвоеточияПослеКлюча > 0 Тогда // Якорь с объектом: &anchor key: value - ПозицияДвоеточия = ПозицияЯкоря + СледующийСимвол.Позиция; + ПозицияДвоеточия = ПозицияДвоеточияПослеКлюча; ПолнаяСтрокаДоДвоеточия = СокрЛП(Лев(Строка, ПозицияДвоеточия - 1)); - // Убираем якорь из имени - ищем пробел после якоря - ПозицияПробела = Найти(ПолнаяСтрокаДоДвоеточия, " "); - Если ПозицияПробела > 0 Тогда - ИмяОбъекта = СокрЛП(Сред(ПолнаяСтрокаДоДвоеточия, ПозицияПробела + 1)); - Иначе - ИмяОбъекта = ПолнаяСтрокаДоДвоеточия; - КонецЕсли; + ИмяЯкоря = ИзвлечьИмяЯкоряИлиАлиаса(ПолнаяСтрокаДоДвоеточия); + ИмяОбъекта = УдалитьОпределениеЯкоря(ПолнаяСтрокаДоДвоеточия); // Проверяем на недопустимое сочетание якоря и алиаса в ключе Если Найти(ИмяОбъекта, "*") > 0 Тогда ВызватьИсключение("YAML Error: Недопустимо использование алиаса (*) в ключе, который уже содержит якорь (&)"); КонецЕсли; + + Если НЕ ПустаяСтрока(ИмяЯкоря) Тогда + СохранитьЯкорь(ИмяЯкоря, ИмяОбъекта); + КонецЕсли; Сущность = СоздатьСтруктуруСущности("Объект", ИмяОбъекта, ТекущийОтступ + 1); Лог.Отладка("Найден объект с якорем: " + ИмяОбъекта); @@ -3740,10 +3877,20 @@ // Возвращаемое значение: // Структура - сущность и модифицированная строка Функция ОбработатьАлиас(Строка, ПозицияАлиаса, ТекущийОтступ) - Сущность = СоздатьСтруктуруСущности("Алиас", "", ТекущийОтступ + 1); - Лог.Отладка("Найден алиас"); + ПозицияДвоеточия = НайтиДвоеточиеКлючаПослеАлиаса(Строка, ПозицияАлиаса); + + Если ПозицияДвоеточия > 0 Тогда + ИмяОбъекта = СокрЛП(Лев(Строка, ПозицияДвоеточия - 1)); + Сущность = СоздатьСтруктуруСущности("Объект", ИмяОбъекта, ТекущийОтступ + 1); + Сущность.ЗначениеНаТойЖеСтроке = ОпределитьЗначениеНаТойЖеСтроке(СокрЛП(Сред(Строка, ПозицияДвоеточия + 1))); + Лог.Отладка("Найден объект с алиасом в ключе: " + ИмяОбъекта); + МодифицированнаяСтрока = ЗаменитьВСтрокеПрефиксОпределенияНаПробелы(Строка, ПозицияДвоеточия); + Иначе + Сущность = СоздатьСтруктуруСущности("Алиас", "", ТекущийОтступ + 1); + Лог.Отладка("Найден алиас"); - МодифицированнаяСтрока = ЗаменитьВСтрокеПрефиксОпределенияНаПробелы(Строка, ПозицияАлиаса); + МодифицированнаяСтрока = ЗаменитьВСтрокеПрефиксОпределенияНаПробелы(Строка, ПозицияАлиаса); + КонецЕсли; Результат = Новый Структура("Сущность, МодифицированнаяСтрока", Сущность, МодифицированнаяСтрока); Возврат Результат; @@ -3800,8 +3947,6 @@ // Массив - массив структур с описанием объектов // Функция ПолучитьМассивСущностей(Строки) - Лог.Отладка("Начало извлечения сущностей из " + Строки.Количество() + " строк"); - МассивСущностей = Новый Массив(); Для Индекс = 0 По Строки.Количество() - 1 Цикл @@ -3817,7 +3962,6 @@ Индекс = РезультатОбработки.НовыйИндекс; КонецЦикла; - Лог.Отладка("Извлечение сущностей завершено, найдено " + МассивСущностей.Количество() + " сущностей"); Возврат МассивСущностей; КонецФункции @@ -4082,6 +4226,11 @@ КонецФункции Функция ОпределитьОтступ(Строка) + КэшированныйОтступ = КэшОтступовСтрок.Получить(Строка); + Если КэшированныйОтступ <> Неопределено Тогда + Возврат КэшированныйОтступ; + КонецЕсли; + // Определяем отступ в строке (только пробелы в начале) // YAML спецификация: отступ может состоять только из пробелов Отступ = 0; @@ -4095,6 +4244,7 @@ КонецЕсли; КонецЦикла; + КэшОтступовСтрок.Вставить(Строка, Отступ); Возврат Отступ; КонецФункции @@ -4108,36 +4258,30 @@ // Число - минимальный отступ содержимого // Функция ОпределитьМинимальныйОтступСодержимого(МассивСтрок, ИндексНачалаСодержимого) - МинимальныйОтступ = Неопределено; + ОтступПробельнойСтрокиСТабом = Неопределено; Для Индекс = ИндексНачалаСодержимого По МассивСтрок.Количество() - 1 Цикл Строка = МассивСтрок[Индекс]; - // Пропускаем пустые строки и строки с одними пробелами при определении отступа - СтрокаОбрезанная = СокрЛП(Строка); - Если ПустаяСтрока(СтрокаОбрезанная) Тогда + Если Строка = "" Тогда Продолжить; КонецЕсли; - // Пропускаем строки с комментариями при определении отступа - // Комментарий - это строка, которая после удаления пробелов слева начинается с # - Если СтрНачинаетсяС(СтрокаОбрезанная, "#") Тогда + Если ПустаяСтрока(СокрЛП(Строка)) Тогда + Если ОтступПробельнойСтрокиСТабом = Неопределено И СтрНайти(Строка, Символы.Таб) > 0 Тогда + ОтступПробельнойСтрокиСТабом = ОпределитьОтступ(Строка); + КонецЕсли; Продолжить; КонецЕсли; - - ОтступСтроки = ОпределитьОтступ(Строка); - - Если МинимальныйОтступ = Неопределено ИЛИ ОтступСтроки < МинимальныйОтступ Тогда - МинимальныйОтступ = ОтступСтроки; - КонецЕсли; + + Возврат ОпределитьОтступ(Строка); КонецЦикла; - // Если не найдено ни одной строки с содержимым, возвращаем 0 - Если МинимальныйОтступ = Неопределено Тогда - Возврат 0; + Если ОтступПробельнойСтрокиСТабом <> Неопределено Тогда + Возврат ОтступПробельнойСтрокиСТабом; КонецЕсли; - Возврат МинимальныйОтступ; + Возврат 0; КонецФункции //////////////////////////////////////////////////////////////////////////////// @@ -4164,9 +4308,6 @@ // Извлекаем строки содержимого СтрокиСодержимого = ИзвлечьСодержимоеМногострочнойСтроки(МассивСтрок, ПараметрыМногострочнойСтроки); - // Обрабатываем пустые строки в конце если необходимо - СтрокиСодержимого = ОбработатьПустыеСтрокиВКонце(СтрокиСодержимого, ПараметрыМногострочнойСтроки); - // Собираем финальную строку Возврат СобратьМногострочнуюСтроку(СтрокиСодержимого, ПараметрыМногострочнойСтроки); @@ -4187,17 +4328,38 @@ Параметры = Новый Структура(); Параметры.Вставить("ТипСтроки", ТипСтроки); // "|" или ">" - // Разбираем chomping модификатор из подсимвола ChompingМодификатор = ИзвлечьChompingМодификатор(УправляющийПодсимвол); + ИндикаторОтступа = ИзвлечьИндикаторОтступа(УправляющийПодсимвол); Параметры.Вставить("СохранятьПустыеСтрокиВКонце", ChompingМодификатор = "+"); Параметры.Вставить("ДобавлятьПереносПоследнейСтроки", ChompingМодификатор <> "-"); + Параметры.Вставить("ChompingМодификатор", ChompingМодификатор); + Параметры.Вставить("ИндикаторОтступа", ИндикаторОтступа); Параметры.Вставить("ИндексНачалаСодержимого", ИндексНачалаСодержимого); Параметры.Вставить("ОтступСодержимого", ОтступСодержимого); Возврат Параметры; КонецФункции +// Извлекает явный индикатор отступа из заголовка блочного скаляра +// +// Параметры: +// УправляющийПодсимвол - Строка - строка может содержать цифры и chomping символ (+, -) +// +// Возвращаемое значение: +// Число - индикатор отступа или 0, если он не задан +// +Функция ИзвлечьИндикаторОтступа(УправляющийПодсимвол) + Для Позиция = 1 По СтрДлина(УправляющийПодсимвол) Цикл + Символ = Сред(УправляющийПодсимвол, Позиция, 1); + Если Символ >= "1" И Символ <= "9" Тогда + Возврат Число(Символ); + КонецЕсли; + КонецЦикла; + + Возврат 0; +КонецФункции + // Извлекает chomping модификатор из строки управляющего подсимвола // // Параметры: @@ -4264,8 +4426,17 @@ ВызватьИсключение "В многострочной строке не должно быть ничего после управляющей последовательности"; КонецЕсли; - // Определяем отступ для содержимого - минимальный среди всех непустых строк содержимого - ОтступСодержимого = ОпределитьМинимальныйОтступСодержимого(МассивСтрок, Индекс + 1); + ИндикаторОтступа = ИзвлечьИндикаторОтступа(УправляющийПодсимвол); + АвтоОтступСодержимого = ОпределитьМинимальныйОтступСодержимого(МассивСтрок, Индекс + 1); + Если ИндикаторОтступа > 0 Тогда + Если АвтоОтступСодержимого - ИндикаторОтступа >= 2 Тогда + ОтступСодержимого = АвтоОтступСодержимого; + Иначе + ОтступСодержимого = ИндикаторОтступа; + КонецЕсли; + Иначе + ОтступСодержимого = АвтоОтступСодержимого; + КонецЕсли; Параметры = СоздатьПараметрыМногострочнойСтроки( ДанныеУправляющегоСимвола.Символ, @@ -4296,11 +4467,26 @@ Лог.Отладка("Извлечение содержимого многострочной строки"); СтрокиСодержимого = Новый Массив(); + ВнешниеКомментарииНачались = Ложь; + СодержимоеНачалось = Ложь; Для Индекс = Параметры.ИндексНачалаСодержимого По МассивСтрок.Количество() - 1 Цикл ИсходнаяСтрока = МассивСтрок[Индекс]; + + Если ВнешниеКомментарииНачались Тогда + Если ЭтоПустаяСтрокаИлиКомментарий(ИсходнаяСтрока) Тогда + Продолжить; + Иначе + Прервать; + КонецЕсли; + КонецЕсли; - ПроверитьОтступСтроки(ИсходнаяСтрока, Параметры.ОтступСодержимого); + Если СодержимоеНачалось + И ОпределитьОтступ(ИсходнаяСтрока) < Параметры.ОтступСодержимого + И СтрНачинаетсяС(СокрЛП(ИсходнаяСтрока), "#") Тогда + ВнешниеКомментарииНачались = Истина; + Продолжить; + КонецЕсли; СтрокаБезОтступа = УбратьБазовыйОтступИзСтроки(ИсходнаяСтрока, Параметры.ОтступСодержимого); @@ -4309,6 +4495,9 @@ КонецЕсли; СтрокиСодержимого.Добавить(СтрокаБезОтступа); + Если НЕ ЭтоПустаяСтрокаПослеОбрезки(СтрокаБезОтступа) Тогда + СодержимоеНачалось = Истина; + КонецЕсли; КонецЦикла; Лог.Отладка("Извлечено " + СтрокиСодержимого.Количество() + " строк содержимого"); @@ -4323,7 +4512,8 @@ // Процедура ПроверитьОтступСтроки(Строка, БазовыйОтступ) Если ОпределитьОтступ(Строка) < БазовыйОтступ - И Не ЭтоПустаяСтрокаИлиКомментарий(Строка) Тогда + И Не ЭтоПустаяСтрокаПослеОбрезки(Строка) + И Не СтрНачинаетсяС(СокрЛП(Строка), "#") Тогда Лог.Ошибка("Отступ строки меньше требуемого"); ВызватьИсключение "Отступ строки меньше требуемого"; КонецЕсли; @@ -4354,6 +4544,85 @@ Возврат Строка; КонецФункции +// Повторяет строку заданное количество раз +// +// Параметры: +// Символ - Строка - повторяемая строка +// Количество - Число - количество повторений +// +// Возвращаемое значение: +// Строка - результат повторения +// +Функция ПовторитьСтроку(Символ, Количество) + Результат = ""; + Для Индекс = 1 По Количество Цикл + Результат = Результат + Символ; + КонецЦикла; + + Возврат Результат; +КонецФункции + +// Проверяет, является ли строка блочного скаляра more-indented +// +// Параметры: +// Строка - Строка - строка содержимого без базового отступа +// +// Возвращаемое значение: +// Булево - Истина, если строка более отступлена +// +Функция ЭтоПустаяСтрокаБлочногоСкаляра(Строка) + Возврат СтрЗаменить(Строка, " ", "") = ""; +КонецФункции + +Функция ЭтоБолееОтступленнаяСтрокаСкаляра(Строка) + Если ЭтоПустаяСтрокаБлочногоСкаляра(Строка) Тогда + Возврат Ложь; + КонецЕсли; + + ПервыйСимвол = Лев(Строка, 1); + Возврат ПервыйСимвол = " " ИЛИ ПервыйСимвол = Символы.Таб; +КонецФункции + +// Проверяет, состоит ли блочный скаляр только из пустых строк +// +// Параметры: +// СтрокиСодержимого - Массив - строки содержимого +// +// Возвращаемое значение: +// Булево - Истина, если значимого содержимого нет +// +Функция ЭтоПустойБлочныйСкаляр(СтрокиСодержимого) + Для Каждого СтрокаСодержимого Из СтрокиСодержимого Цикл + Если НЕ ЭтоПустаяСтрокаБлочногоСкаляра(СтрокаСодержимого) Тогда + Возврат Ложь; + КонецЕсли; + КонецЦикла; + + Возврат Истина; +КонецФункции + +// Собирает пустой блочный скаляр с учетом chomping +// +// Параметры: +// СтрокиСодержимого - Массив - пустые строки содержимого +// Параметры - Структура - параметры многострочной строки +// +// Возвращаемое значение: +// Строка - результат chomping +// +Функция СобратьПустойБлочныйСкаляр(СтрокиСодержимого, Параметры) + Если НЕ Параметры.ДобавлятьПереносПоследнейСтроки Тогда + Возврат ""; + КонецЕсли; + + Если Параметры.СохранятьПустыеСтрокиВКонце Тогда + КоличествоПереносов = Макс(1, СтрокиСодержимого.Количество() - 1); + Возврат ПовторитьСтроку(Символы.ПС, КоличествоПереносов); + КонецЕсли; + + Возврат ""; +КонецФункции + // Проверяет должен ли комментарий быть пропущен // // Параметры: @@ -4425,13 +4694,13 @@ Если СтрокиСодержимого.Количество() = 0 Тогда Возврат ""; КонецЕсли; - - // Соединяем строки разделителем - - - // Заменяем двойные пробелы на переносы строк для блочного стиля + + Если ЭтоПустойБлочныйСкаляр(СтрокиСодержимого) Тогда + Возврат СобратьПустойБлочныйСкаляр(СтрокиСодержимого, Параметры); + КонецЕсли; + Если Параметры.ТипСтроки = ">" Тогда - РезультирующаяСтрока = СобратьСтрокуИзМассива(СтрокиСодержимого); + РезультирующаяСтрока = СобратьСвернутуюМногострочнуюСтроку(СтрокиСодержимого); Иначе РезультирующаяСтрока = СтрСоединить(СтрокиСодержимого, Символы.ПС); КонецЕсли; @@ -4450,7 +4719,7 @@ РезультирующаяСтрока = РезультирующаяСтрока + Символы.ПС; Иначе // Keep chomping (+): оставляем все как есть при Параметры.СохранятьПустыеСтрокиВКонце = Истина - // Никаких действий не требуется - строка остается как есть + РезультирующаяСтрока = РезультирующаяСтрока + Символы.ПС; Лог.Отладка("Применяется keep chomping - строка остается без изменений"); КонецЕсли; @@ -4458,6 +4727,69 @@ КонецФункции +// Собирает folded block scalar по правилам YAML +// +// Параметры: +// СтрокиСодержимого - Массив - строки содержимого без базового отступа +// +// Возвращаемое значение: +// Строка - собранная строка +// +Функция СобратьСвернутуюМногострочнуюСтроку(СтрокиСодержимого) + Результат = ""; + Индекс = 0; + + Пока Индекс <= СтрокиСодержимого.ВГраница() Цикл + ТекущаяСтрока = СтрокиСодержимого[Индекс]; + + Если ЭтоПустаяСтрокаБлочногоСкаляра(ТекущаяСтрока) Тогда + Результат = Результат + Символы.ПС; + Индекс = Индекс + 1; + Продолжить; + КонецЕсли; + + Результат = Результат + ТекущаяСтрока; + + Если Индекс = СтрокиСодержимого.ВГраница() Тогда + Прервать; + КонецЕсли; + + СледующийИндекс = Индекс + 1; + КоличествоПустыхСтрок = 0; + Пока СледующийИндекс <= СтрокиСодержимого.ВГраница() + И ЭтоПустаяСтрокаБлочногоСкаляра(СтрокиСодержимого[СледующийИндекс]) Цикл + КоличествоПустыхСтрок = КоличествоПустыхСтрок + 1; + СледующийИндекс = СледующийИндекс + 1; + КонецЦикла; + + Если КоличествоПустыхСтрок > 0 Тогда + Если СледующийИндекс > СтрокиСодержимого.ВГраница() Тогда + Результат = Результат + ПовторитьСтроку(Символы.ПС, КоличествоПустыхСтрок); + ИначеЕсли ЭтоБолееОтступленнаяСтрокаСкаляра(ТекущаяСтрока) + ИЛИ ЭтоБолееОтступленнаяСтрокаСкаляра(СтрокиСодержимого[СледующийИндекс]) Тогда + Результат = Результат + ПовторитьСтроку(Символы.ПС, КоличествоПустыхСтрок + 1); + Иначе + Результат = Результат + ПовторитьСтроку(Символы.ПС, КоличествоПустыхСтрок); + КонецЕсли; + + Индекс = СледующийИндекс; + Продолжить; + КонецЕсли; + + СледующаяСтрока = СтрокиСодержимого[СледующийИндекс]; + Если ЭтоБолееОтступленнаяСтрокаСкаляра(ТекущаяСтрока) + ИЛИ ЭтоБолееОтступленнаяСтрокаСкаляра(СледующаяСтрока) Тогда + Результат = Результат + Символы.ПС; + Иначе + Результат = Результат + " "; + КонецЕсли; + + Индекс = Индекс + 1; + КонецЦикла; + + Возврат Результат; +КонецФункции + Функция ПолучитьУправляющийПодсимвол(Строка, Позиция) Если СтрДлина(Строка) < Позиция Тогда Возврат ""; // Нет подсимвола, возвращаем пустую строку @@ -4548,7 +4880,8 @@ // Булево - Истина если строка заканчивается экранированным переносом // Функция ЭтоЭкранированныйПеренос(Строка) - Возврат СтрЗаканчиваетсяНа(Строка, "\") И НЕ СтрЗаканчиваетсяНа(Строка, "\\"); + СтрокаБезПравыхПробелов = СокрП(Строка); + Возврат СтрЗаканчиваетсяНа(СтрокаБезПравыхПробелов, "\") И НЕ СтрЗаканчиваетсяНа(СтрокаБезПравыхПробелов, "\\"); КонецФункции // Обрабатывает экранированный перенос строки @@ -4564,8 +4897,20 @@ // Строка - обновленный результат // Функция ОбработатьЭкранированныйПеренос(Результат, Строка, Индекс, БылЭкранированныйПеренос, БылПереносСтроки) - // Убираем \ в конце - СтрокаБезBackslash = Лев(Строка, СтрДлина(Строка) - 1); + ПозицияBackslash = СтрДлина(Строка); + Пока ПозицияBackslash > 0 Цикл + Символ = Сред(Строка, ПозицияBackslash, 1); + Если Символ <> " " И Символ <> Символы.Таб Тогда + Прервать; + КонецЕсли; + ПозицияBackslash = ПозицияBackslash - 1; + КонецЦикла; + + СтрокаБезBackslash = Лев(Строка, ПозицияBackslash - 1); + ПравыеПробелы = Сред(Строка, ПозицияBackslash + 1); + Если СтрНайти(ПравыеПробелы, Символы.Таб) > 0 Тогда + СтрокаБезBackslash = СтрокаБезBackslash + Символы.Таб + " "; + КонецЕсли; // Если это не первая строка и не было экранированного переноса и не было переноса строки, добавляем пробел Если ТребуетсяПробел(Индекс, БылЭкранированныйПеренос, БылПереносСтроки) Тогда @@ -4794,17 +5139,21 @@ // Строка - объединенная строка без комментариев // Функция ОбъединитьСтрокиFlowСинтаксиса(Строки) - СтрокаFlow = ""; + ЧастиFlow = Новый Массив(); + Для Каждого Строка Из Строки Цикл - Если НЕ ПустаяСтрока(СокрЛП(Строка)) Тогда - // Удаляем комментарии из строки - СтрокаБезКомментария = УбратьКомментарийИзСтроки(Строка); - Если НЕ ПустаяСтрока(СокрЛП(СтрокаБезКомментария)) Тогда - СтрокаFlow = СтрокаFlow + " " + СокрЛП(СтрокаБезКомментария); - КонецЕсли; + ОбрезаннаяСтрока = СокрЛП(Строка); + Если ПустаяСтрока(ОбрезаннаяСтрока) Тогда + Продолжить; + КонецЕсли; + + СтрокаБезКомментария = СокрЛП(УбратьКомментарийИзСтроки(Строка)); + Если НЕ ПустаяСтрока(СтрокаБезКомментария) Тогда + ЧастиFlow.Добавить(СтрокаБезКомментария); КонецЕсли; КонецЦикла; - Возврат СокрЛП(СтрокаFlow); + + Возврат СтрСоединить(ЧастиFlow, " "); КонецФункции // Обрабатывает один элемент flow объекта @@ -4829,6 +5178,10 @@ Значение = ИзвлечьЗначениеПослеДвоеточияСОчисткой(ОчищенныйЭлемент, ПозицияДвоеточия); КонецЕсли; + Если СтрНачинаетсяС(СокрЛП(Ключ), "&") Тогда + Ключ = УдалитьОпределениеЯкоря(Ключ); + КонецЕсли; + // Убираем кавычки с ключа Ключ = УбратьКавычки(Ключ); @@ -5805,13 +6158,14 @@ Возврат ""; КонецЕсли; - // Извлекаем имя якоря (до пробела или до конца строки) + // Извлекаем имя якоря/алиаса (до пробела или до конца строки). + // Двоеточие может быть частью имени. НачалоИмени = Позиция + 1; КонецИмени = СтрДлина(Строка); Для Позиция = НачалоИмени По КонецИмени Цикл Символ = Сред(Строка, Позиция, 1); - Если Символ = " " ИЛИ Символ = Символы.Таб ИЛИ Символ = ":" Тогда + Если Символ = " " ИЛИ Символ = Символы.Таб Тогда КонецИмени = Позиция - 1; Прервать; КонецЕсли; @@ -5825,6 +6179,35 @@ КонецФункции +// Удаление ссылки на алиас из строки +// +// Параметры: +// Строка - Строка - строка со ссылкой на алиас +// +// Возвращаемое значение: +// Строка - строка без ссылки на алиас +// +Функция УдалитьОпределениеАлиаса(Строка) + ПозицияАлиаса = СтрНайти(Строка, "*"); + Если ПозицияАлиаса = 0 Тогда + Возврат Строка; + КонецЕсли; + + КонецИмени = СтрДлина(Строка); + Для Позиция = ПозицияАлиаса + 1 По СтрДлина(Строка) Цикл + Символ = Сред(Строка, Позиция, 1); + Если Символ = " " ИЛИ Символ = Символы.Таб Тогда + КонецИмени = Позиция - 1; + Прервать; + КонецЕсли; + КонецЦикла; + + ЧастьДо = Лев(Строка, ПозицияАлиаса - 1); + ЧастьПосле = Сред(Строка, КонецИмени + 1); + + Возврат СокрЛП(ЧастьДо + ЧастьПосле); +КонецФункции + // Удаление определения якоря из строки // // Параметры: @@ -5889,26 +6272,29 @@ YAML_ТЕРМИНАТОР_ДОКУМЕНТА = "..."; // Инициализация кэша якорей Якоря = Новый Соответствие; -// Инициализация кэша регулярных выражений -КэшРегулярныхВыражений = Новый Соответствие(); -КэшРегулярныхВыражений.Вставить("пробелы", Новый РегулярноеВыражение("[ \t]")); -КэшРегулярныхВыражений.Вставить("спецСимволы", "[\]{}()*+?.^$|"); - -// Инициализация статического массива управляющих символов -СтатическийМассивУправляющихСимволов = Новый Массив(); -СтатическийМассивУправляющихСимволов.Добавить(":"); -СтатическийМассивУправляющихСимволов.Добавить("-"); -СтатическийМассивУправляющихСимволов.Добавить("["); -СтатическийМассивУправляющихСимволов.Добавить("{"); -СтатическийМассивУправляющихСимволов.Добавить("|"); -СтатическийМассивУправляющихСимволов.Добавить(">"); -СтатическийМассивУправляющихСимволов.Добавить("&"); -СтатическийМассивУправляющихСимволов.Добавить("*"); -СтатическийМассивУправляющихСимволов.Добавить("!"); -СтатическийМассивУправляющихСимволов.Добавить("?"); +// Инициализация валидных escape-последовательностей +ВалидныеEscapeПоследовательности = Новый Массив; +ВалидныеEscapeПоследовательности.Добавить("\"""); +ВалидныеEscapeПоследовательности.Добавить("\\"); +ВалидныеEscapeПоследовательности.Добавить("\/"); +ВалидныеEscapeПоследовательности.Добавить("\b"); +ВалидныеEscapeПоследовательности.Добавить("\f"); +ВалидныеEscapeПоследовательности.Добавить("\n"); +ВалидныеEscapeПоследовательности.Добавить("\r"); +ВалидныеEscapeПоследовательности.Добавить("\t"); +ВалидныеEscapeПоследовательности.Добавить("\ "); +ВалидныеEscapeПоследовательности.Добавить("\0"); +ВалидныеEscapeПоследовательности.Добавить("\a"); +ВалидныеEscapeПоследовательности.Добавить("\e"); +ВалидныеEscapeПоследовательности.Добавить("\_"); +ВалидныеEscapeПоследовательности.Добавить("\L"); +ВалидныеEscapeПоследовательности.Добавить("\P"); +ВалидныеEscapeПоследовательности.Добавить("\" + Символы.Таб); // Инициализация кэша типов строк КэшТиповСтрок = Новый Соответствие(); +КэшОтступовСтрок = Новый Соответствие(); +КэшУправляющихСимволов = Новый Соответствие(); //////////////////////////////////////////////////////////////////////////////// // АРХИТЕКТУРНЫЕ ЗАМЕТКИ // @@ -5925,4 +6311,4 @@ YAML_ТЕРМИНАТОР_ДОКУМЕНТА = "..."; // // Каждая секция содержит логически связанные функции, что упрощает навигацию // и поддержку кода. -//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////// diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" index b09d25e..1d48205 100644 --- "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200.os" @@ -1,5 +1,9 @@ // Модуль для сериализации данных в формат YAML +Перем СпециальныеСкалярныеЗначения; +Перем СпециальныеСимволыСтроки; +Перем КэшОтступов; + // Основная функция сериализации // // Параметры: @@ -10,35 +14,35 @@ // Строка - YAML представление данных // Функция Сериализовать(Знач Значение, УровеньОтступа = 0) Экспорт - + Возврат СериализоватьЗначение(Значение, УровеньОтступа); - + КонецФункции Функция СериализоватьДокументы(Знач Документы) Экспорт - + Если ТипЗнч(Документы) <> Тип("Массив") Тогда ВызватьИсключение "Для сериализации нескольких документов нужно передать массив документов"; КонецЕсли; - - Результат = ""; - + + СтрокиДокументов = Новый Массив(); + Для Каждого Документ Из Документы Цикл - Результат = ДобавитьСтроку(Результат, "---" + Символы.ПС + СериализоватьЗначение(Документ, 0)); + СтрокиДокументов.Добавить("---" + Символы.ПС + СериализоватьЗначение(Документ, 0)); КонецЦикла; - - Возврат Результат; - + + Возврат СоединитьСтроки(СтрокиДокументов); + КонецФункции Функция СериализоватьЗначение(Знач Значение, УровеньОтступа) - + Если Значение = Неопределено Тогда Возврат "null"; КонецЕсли; - + ТипЗначения = ТипЗнч(Значение); - + Если ТипЗначения = Тип("Массив") Тогда Возврат СериализоватьМассив(Значение, УровеньОтступа); ИначеЕсли ЭтоТипОбъекта(ТипЗначения) Тогда @@ -48,17 +52,17 @@ Иначе Возврат СериализоватьСкаляр(Значение); КонецЕсли; - + КонецФункции Функция СериализоватьСкаляр(Знач Значение) - + Если Значение = Неопределено Тогда Возврат "null"; КонецЕсли; - + ТипЗначения = ТипЗнч(Значение); - + Если ТипЗначения = Тип("Строка") Тогда Возврат СериализоватьСтроку(Значение); ИначеЕсли ТипЗначения = Тип("Число") Тогда @@ -74,7 +78,7 @@ Возврат СериализоватьСтроку(Строка(Значение)); КонецПопытки; КонецЕсли; - + КонецФункции // Сериализация строки @@ -86,27 +90,27 @@ // Строка - YAML представление строки // Функция СериализоватьСтроку(Значение) - + Если Значение = "" Тогда Возврат """"""; КонецЕсли; - + Если ТребуетсяЗаключениеВКавычки(Значение) Тогда Возврат СериализоватьJSON(Значение); Иначе Возврат Значение; КонецЕсли; - + КонецФункции Функция СериализоватьJSON(Значение) - + ЗаписьJSON = Новый ЗаписьJSON; ЗаписьJSON.УстановитьСтроку(); ЗаписатьJSON(ЗаписьJSON, Значение); - + Возврат ЗаписьJSON.Закрыть(); - + КонецФункции // Сериализация многострочной строки @@ -119,18 +123,20 @@ // Строка - YAML представление многострочной строки // Функция СериализоватьМногострочнуюСтроку(Значение, УровеньОтступа) - + НормализованноеЗначение = НормализоватьПереносыСтрок(Значение); Строки = СтрРазделить(НормализованноеЗначение, Символы.ПС, Истина); ОтступСтроки = ПолучитьОтступ(УровеньОтступа + 1); - - Результат = "|"; + + СтрокиРезультата = Новый Массив(); + СтрокиРезультата.Добавить("|"); + Для Каждого СтрокаЗначения Из Строки Цикл - Результат = Результат + Символы.ПС + ОтступСтроки + СтрокаЗначения; + СтрокиРезультата.Добавить(ОтступСтроки + СтрокаЗначения); КонецЦикла; - - Возврат Результат; - + + Возврат СоединитьСтроки(СтрокиРезультата); + КонецФункции // Сериализация числа @@ -175,30 +181,30 @@ // Строка - YAML представление массива // Функция СериализоватьМассив(Массив, УровеньОтступа = 0) - + Если Массив.Количество() = 0 Тогда Возврат "[]"; КонецЕсли; - - Результат = ""; + + СтрокиРезультата = Новый Массив(); Отступ = ПолучитьОтступ(УровеньОтступа); - + Для Каждого Элемент Из Массив Цикл - Результат = ДобавитьСтроку(Результат, СериализоватьЭлементМассива(Элемент, Отступ, УровеньОтступа)); + СтрокиРезультата.Добавить(СериализоватьЭлементМассива(Элемент, Отступ, УровеньОтступа)); КонецЦикла; - - Возврат Результат; - + + Возврат СоединитьСтроки(СтрокиРезультата); + КонецФункции Функция СериализоватьЭлементМассива(Знач Элемент, Отступ, УровеньОтступа) - - Если ЭтоПустаяКоллекция(Элемент) Или ЭтоСкалярДляСтроки(Элемент) Тогда - Возврат Отступ + "- " + СериализоватьСкалярИлиПустуюКоллекцию(Элемент); + + ТипЭлемента = ТипЗначенияИлиНеопределено(Элемент); + + Если ЭтоПустаяКоллекция(Элемент, ТипЭлемента) Или ЭтоОднострочноеЗначение(Элемент, ТипЭлемента) Тогда + Возврат Отступ + "- " + СериализоватьОднострочноеЗначение(Элемент, ТипЭлемента); КонецЕсли; - - ТипЭлемента = ТипЗнч(Элемент); - + Если ТипЭлемента = Тип("Строка") Тогда Возврат Отступ + "- " + СериализоватьМногострочнуюСтроку(Элемент, УровеньОтступа); ИначеЕсли ЭтоТипОбъекта(ТипЭлемента) Тогда @@ -208,24 +214,25 @@ Иначе Возврат Отступ + "- " + СериализоватьСкаляр(Элемент); КонецЕсли; - + КонецФункции Функция СериализоватьОбъектВЭлементеМассива(Объект, Отступ, УровеньОтступа) - + ОтступДочернегоОбъекта = ПолучитьОтступ(УровеньОтступа + 1); СериализованныйОбъект = СериализоватьОбъект(Объект, УровеньОтступа + 1); СтрокиОбъекта = СтрРазделить(СериализованныйОбъект, Символы.ПС, Истина); - + ПерваяСтрока = УбратьНачальныйОтступ(СтрокиОбъекта[0], ОтступДочернегоОбъекта); - Результат = Отступ + "- " + ПерваяСтрока; - + СтрокиРезультата = Новый Массив(); + СтрокиРезультата.Добавить(Отступ + "- " + ПерваяСтрока); + Для Индекс = 1 По СтрокиОбъекта.Количество() - 1 Цикл - Результат = Результат + Символы.ПС + СтрокиОбъекта[Индекс]; + СтрокиРезультата.Добавить(СтрокиОбъекта[Индекс]); КонецЦикла; - - Возврат Результат; - + + Возврат СоединитьСтроки(СтрокиРезультата); + КонецФункции // Сериализация объекта @@ -238,43 +245,45 @@ // Строка - YAML представление объекта // Функция СериализоватьОбъект(Объект, УровеньОтступа = 0) - + Если Объект.Количество() = 0 Тогда Возврат "{}"; КонецЕсли; - - Результат = ""; + + СтрокиРезультата = Новый Массив(); Отступ = ПолучитьОтступ(УровеньОтступа); - + Для Каждого КлючЗначение Из Объект Цикл Ключ = СериализоватьКлюч(КлючЗначение.Ключ); Значение = КлючЗначение.Значение; - - Результат = ДобавитьСтроку(Результат, СериализоватьСвойствоОбъекта(Ключ, Значение, Отступ, УровеньОтступа)); + + СтрокиРезультата.Добавить(СериализоватьСвойствоОбъекта(Ключ, Значение, Отступ, УровеньОтступа)); КонецЦикла; - - Возврат Результат; - + + Возврат СоединитьСтроки(СтрокиРезультата); + КонецФункции Функция СериализоватьСвойствоОбъекта(Ключ, Значение, Отступ, УровеньОтступа) - - Если ЭтоПустаяКоллекция(Значение) Или ЭтоСкалярДляСтроки(Значение) Тогда - Возврат Отступ + Ключ + ": " + СериализоватьСкалярИлиПустуюКоллекцию(Значение); + + ТипЗначения = ТипЗначенияИлиНеопределено(Значение); + + Если ЭтоПустаяКоллекция(Значение, ТипЗначения) Или ЭтоОднострочноеЗначение(Значение, ТипЗначения) Тогда + Возврат Отступ + Ключ + ": " + СериализоватьОднострочноеЗначение(Значение, ТипЗначения); КонецЕсли; - - Если ТипЗнч(Значение) = Тип("Строка") Тогда + + Если ТипЗначения = Тип("Строка") Тогда Возврат Отступ + Ключ + ": " + СериализоватьМногострочнуюСтроку(Значение, УровеньОтступа); Иначе Возврат Отступ + Ключ + ":" + Символы.ПС + СериализоватьЗначение(Значение, УровеньОтступа + 1); КонецЕсли; - + КонецФункции -Функция СериализоватьСкалярИлиПустуюКоллекцию(Знач Значение) - - Если ЭтоПустаяКоллекция(Значение) Тогда - Если ТипЗнч(Значение) = Тип("Массив") Тогда +Функция СериализоватьОднострочноеЗначение(Знач Значение, ТипЗначения) + + Если ЭтоПустаяКоллекция(Значение, ТипЗначения) Тогда + Если ТипЗначения = Тип("Массив") Тогда Возврат "[]"; Иначе Возврат "{}"; @@ -282,7 +291,7 @@ Иначе Возврат СериализоватьСкаляр(Значение); КонецЕсли; - + КонецФункции // Сериализация ключа @@ -294,86 +303,88 @@ // Строка - YAML представление ключа // Функция СериализоватьКлюч(Ключ) - + Возврат СериализоватьСкаляр(Ключ); - + КонецФункции -Функция ЭтоСкалярДляСтроки(Значение) - +Функция ТипЗначенияИлиНеопределено(Значение) + + Если Значение = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат ТипЗнч(Значение); + +КонецФункции + +Функция ЭтоОднострочноеЗначение(Значение, ТипЗначения) + Если Значение = Неопределено Тогда Возврат Истина; КонецЕсли; - - ТипЗначения = ТипЗнч(Значение); - + Если ТипЗначения = Тип("Массив") Или ЭтоТипОбъекта(ТипЗначения) Тогда Возврат Ложь; КонецЕсли; - + Если ТипЗначения = Тип("Строка") Тогда Возврат НЕ ЭтоМногострочнаяСтрока(Значение); КонецЕсли; - + Возврат Истина; - + КонецФункции -Функция ЭтоПустаяКоллекция(Значение) - +Функция ЭтоПустаяКоллекция(Значение, ТипЗначения) + Если Значение = Неопределено Тогда Возврат Ложь; КонецЕсли; - - ТипЗначения = ТипЗнч(Значение); - + Если ТипЗначения = Тип("Массив") Или ЭтоТипОбъекта(ТипЗначения) Тогда Возврат Значение.Количество() = 0; - Иначе - Возврат Ложь; КонецЕсли; - + + Возврат Ложь; + КонецФункции Функция ЭтоТипОбъекта(ТипЗначения) - + Возврат ТипЗначения = Тип("Структура") Или ТипЗначения = Тип("Соответствие"); - + КонецФункции Функция ЭтоМногострочнаяСтрока(Значение) - + Возврат СтрНайти(Значение, Символы.ПС) > 0 Или СтрНайти(Значение, Символы.ВК) > 0; - + КонецФункции Функция НормализоватьПереносыСтрок(Значение) - + Результат = СтрЗаменить(Значение, Символы.ВК + Символы.ПС, Символы.ПС); Результат = СтрЗаменить(Результат, Символы.ВК, Символы.ПС); - + Возврат Результат; - + КонецФункции -Функция ДобавитьСтроку(Результат, Строка) - - Если Результат = "" Тогда - Возврат Строка; - Иначе - Возврат Результат + Символы.ПС + Строка; - КонецЕсли; - +Функция СоединитьСтроки(Строки) + + Возврат СтрСоединить(Строки, Символы.ПС); + КонецФункции Функция УбратьНачальныйОтступ(Строка, Отступ) - + Если СтрНачинаетсяС(Строка, Отступ) Тогда Возврат Сред(Строка, СтрДлина(Отступ) + 1); Иначе Возврат СокрЛП(Строка); КонецЕсли; - + КонецФункции // Получение строки отступа @@ -385,20 +396,27 @@ // Строка - строка с соответствующим количеством пробелов // Функция ПолучитьОтступ(УровеньОтступа) - + Если УровеньОтступа <= 0 Тогда Возврат ""; КонецЕсли; - + + КлючКэша = Строка(УровеньОтступа); + Если КэшОтступов.Получить(КлючКэша) <> Неопределено Тогда + Возврат КэшОтступов.Получить(КлючКэша); + КонецЕсли; + КоличествоПробелов = УровеньОтступа * 2; Результат = ""; - + Пока СтрДлина(Результат) < КоличествоПробелов Цикл Результат = Результат + " "; КонецЦикла; - + + КэшОтступов.Вставить(КлючКэша, Результат); + Возврат Результат; - + КонецФункции // Проверка, является ли строка числом @@ -410,25 +428,25 @@ // Булево - Истина, если строка представляет число // Функция ЭтоЧисло(Значение) - + ОчищенноеЗначение = СокрЛП(Значение); - + Если ОчищенноеЗначение = "" Тогда Возврат Ложь; КонецЕсли; - + РазрешенныеСимволы = "0123456789.-+"; КоличествоТочек = 0; КоличествоЗнаков = 0; КоличествоЦифр = 0; - + Для Позиция = 1 По СтрДлина(ОчищенноеЗначение) Цикл Символ = Сред(ОчищенноеЗначение, Позиция, 1); - + Если СтрНайти(РазрешенныеСимволы, Символ) = 0 Тогда Возврат Ложь; КонецЕсли; - + Если СтрНайти("0123456789", Символ) > 0 Тогда КоличествоЦифр = КоличествоЦифр + 1; ИначеЕсли Символ = "." Тогда @@ -440,9 +458,9 @@ КонецЕсли; КонецЕсли; КонецЦикла; - + Возврат КоличествоЦифр > 0 И КоличествоТочек <= 1 И КоличествоЗнаков <= 1; - + КонецФункции // Проверка, требуется ли заключение строки в кавычки @@ -454,49 +472,49 @@ // Булево - Истина, если строку нужно заключить в кавычки // Функция ТребуетсяЗаключениеВКавычки(Значение) - + Если Значение = "" Тогда Возврат Истина; КонецЕсли; - + НижнийРегистр = НРег(СокрЛП(Значение)); - СпециальныеЗначения = Новый Массив; - СпециальныеЗначения.Добавить("true"); - СпециальныеЗначения.Добавить("false"); - СпециальныеЗначения.Добавить("null"); - СпециальныеЗначения.Добавить("~"); - СпециальныеЗначения.Добавить("yes"); - СпециальныеЗначения.Добавить("no"); - СпециальныеЗначения.Добавить("on"); - СпециальныеЗначения.Добавить("off"); - - Для Каждого СпециальноеЗначение Из СпециальныеЗначения Цикл - Если НижнийРегистр = СпециальноеЗначение Тогда - Возврат Истина; - КонецЕсли; - КонецЦикла; - + Если СпециальныеСкалярныеЗначения.Получить(НижнийРегистр) <> Неопределено Тогда + Возврат Истина; + КонецЕсли; + Если ЭтоЧисло(Значение) Тогда Возврат Истина; КонецЕсли; - - СпециальныеСимволы = ":#@`|>{}[]!%&*"; - Для Индекс = 1 По СтрДлина(СпециальныеСимволы) Цикл - Символ = Сред(СпециальныеСимволы, Индекс, 1); + + Для Индекс = 1 По СтрДлина(СпециальныеСимволыСтроки) Цикл + Символ = Сред(СпециальныеСимволыСтроки, Индекс, 1); Если СтрНайти(Значение, Символ) > 0 Тогда Возврат Истина; КонецЕсли; КонецЦикла; - + Если Лев(Значение, 1) = " " Или Прав(Значение, 1) = " " Тогда Возврат Истина; КонецЕсли; - + Если СтрНайти(Значение, Символы.ПС) > 0 Или СтрНайти(Значение, Символы.ВК) > 0 Или СтрНайти(Значение, Символы.ВТаб) > 0 Или СтрНайти(Значение, """") > 0 Тогда Возврат Истина; КонецЕсли; - + Возврат Ложь; - + КонецФункции + +СпециальныеСкалярныеЗначения = Новый Соответствие; +СпециальныеСкалярныеЗначения.Вставить("true", Истина); +СпециальныеСкалярныеЗначения.Вставить("false", Истина); +СпециальныеСкалярныеЗначения.Вставить("null", Истина); +СпециальныеСкалярныеЗначения.Вставить("~", Истина); +СпециальныеСкалярныеЗначения.Вставить("yes", Истина); +СпециальныеСкалярныеЗначения.Вставить("no", Истина); +СпециальныеСкалярныеЗначения.Вставить("on", Истина); +СпециальныеСкалярныеЗначения.Вставить("off", Истина); + +СпециальныеСимволыСтроки = ":#@`|>{}[]!%&*"; +КэшОтступов = Новый Соответствие(); diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200Yaml.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200Yaml.os" index 714b6db..c441ee9 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200Yaml.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200Yaml.os" @@ -23,6 +23,27 @@ КонецЕсли; КонецФункции +// Читает YAML из файла +// +// Параметры: +// ПутьКФайлу - Строка - путь к YAML-файлу +// +// Возвращаемое значение: +// Произвольный - распарсенная структура данных или скалярное значение +// +Функция ПрочитатьИзФайла(ПутьКФайлу) Экспорт + ФайлYaml = Новый Файл(ПутьКФайлу); + Если НЕ ФайлYaml.Существует() Тогда + ВызватьИсключение "Файл не найден: " + ПутьКФайлу; + КонецЕсли; + + ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлу, "UTF-8"); + СтрокаYaml = ЧтениеТекста.Прочитать(); + ЧтениеТекста.Закрыть(); + + Возврат ПрочитатьYaml(СтрокаYaml); +КонецФункции + // YAML сериализатор // // Параметры: @@ -37,6 +58,20 @@ КонецФункции +// Записывает значение в YAML-файл +// +// Параметры: +// ПутьКФайлу - Строка - путь к YAML-файлу; +// Значение - Произвольный - набор данных сериализуемых в формат. +// +Процедура ЗаписатьВФайл(ПутьКФайлу, Знач Значение) Экспорт + СтрокаYaml = ЗаписатьYaml(Значение); + + ЗаписьТекста = Новый ЗаписьТекста(ПутьКФайлу, "UTF-8"); + ЗаписьТекста.Записать(СтрокаYaml); + ЗаписьТекста.Закрыть(); +КонецПроцедуры + // YAML сериализатор нескольких документов // // Параметры: diff --git a/tests/serialization.os b/tests/serialization.os index 280f4b9..3233abe 100644 --- a/tests/serialization.os +++ b/tests/serialization.os @@ -369,6 +369,44 @@ ПроверитьСериализацию(Данные, ОжидаемыйYaml); КонецПроцедуры +&Тест +&Тег("serialization") +&ОтображаемоеИмя("Читает YAML из файла") +Процедура ЧитаетYamlИзФайла() Экспорт + ПутьКФайлу = ПолучитьИмяВременногоФайла("yaml"); + ЗаписатьТекстВФайл(ПутьКФайлу, Строки( + "name: file-api", + "enabled: true")); + + Парсер = ТестовыеУтилиты.СоздатьЭкземплярПарсера(); + Результат = Парсер.ПрочитатьИзФайла(ПутьКФайлу); + УдалитьФайлы(ПутьКФайлу); + + Ожидаем.Что(Результат.Получить("name")).Равно("file-api"); + Ожидаем.Что(Результат.Получить("enabled")).Равно(Истина); +КонецПроцедуры + +&Тест +&Тег("serialization") +&ОтображаемоеИмя("Записывает YAML в файл") +Процедура ЗаписываетYamlВФайл() Экспорт + ПутьКФайлу = ПолучитьИмяВременногоФайла("yaml"); + + Данные = Новый Соответствие(); + Данные.Вставить("name", "file-api"); + Данные.Вставить("count", 2); + + Парсер = ТестовыеУтилиты.СоздатьЭкземплярПарсера(); + Парсер.ЗаписатьВФайл(ПутьКФайлу, Данные); + + СодержимоеФайла = НормализоватьПереносы(ТестовыеУтилиты.ПрочитатьТекстФайла(ПутьКФайлу)); + УдалитьФайлы(ПутьКФайлу); + + Ожидаем.Что(СодержимоеФайла).Равно(Строки( + "name: file-api", + "count: 2")); +КонецПроцедуры + Процедура ПроверитьСериализацию(Значение, ОжидаемыйYaml) Парсер = ТестовыеУтилиты.СоздатьЭкземплярПарсера(); ФактическийYaml = НормализоватьПереносы(Парсер.ЗаписатьYaml(Значение)); @@ -439,3 +477,9 @@ Функция НормализоватьПереносы(Значение) Возврат СтрЗаменить(Значение, Символы.ВК, Символы.ПС); КонецФункции + +Процедура ЗаписатьТекстВФайл(ПутьКФайлу, Текст) + ЗаписьТекста = Новый ЗаписьТекста(ПутьКФайлу, "UTF-8"); + ЗаписьТекста.Записать(Текст); + ЗаписьТекста.Закрыть(); +КонецПроцедуры diff --git a/tests/yaml_test_suite.os b/tests/yaml_test_suite.os index eb10bcb..aa88055 100644 --- a/tests/yaml_test_suite.os +++ b/tests/yaml_test_suite.os @@ -1,4 +1,4 @@ -#Использовать asserts +#Использовать asserts #Использовать collectionos #Использовать fs #Использовать ".." @@ -48,7 +48,6 @@ Функция ПрочитатьJSONизФайла(ПутьКФайлу) - // Читаем содержимое файла как текст ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8); СодержимоеФайла = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); @@ -56,10 +55,7 @@ Возврат Неопределено; КонецЕсли; - // Парсим множественные JSON документы - Результат = ПарсерМножественныхJSONДокументов(СодержимоеФайла); - - Возврат Результат; + Возврат ПарсерМножественныхJSONДокументов(СодержимоеФайла); КонецФункции @@ -198,7 +194,16 @@ Если Символ = """" И НЕ ВКавычках Тогда ВКавычках = Истина; ИначеЕсли Символ = """" И ВКавычках Тогда - ВКавычках = Ложь; + КоличествоОбратныхСлешей = 0; + ТекПозиция = Позиция - 1; + Пока ТекПозиция > 0 И Сред(Текст, ТекПозиция, 1) = "\" Цикл + КоличествоОбратныхСлешей = КоличествоОбратныхСлешей + 1; + ТекПозиция = ТекПозиция - 1; + КонецЦикла; + + Если КоличествоОбратныхСлешей % 2 = 0 Тогда + ВКавычках = Ложь; + КонецЕсли; ИначеЕсли НЕ ВКавычках Тогда Если Символ = "[" Тогда УровеньВложенности = УровеньВложенности + 1; @@ -598,7 +603,6 @@ &Тег("whitespace") &Тег("folded") &Тег("scalar") -&Выключен("Парсинг отступов в блочных скалярах") Процедура Test_R4YG() Экспорт ВыполнитьТест("R4YG"); КонецПроцедуры @@ -627,7 +631,6 @@ &ОтображаемоеИмя("AZW3: Lookahead test cases") &Тег("mapping") &Тег("edge") -&Выключен("Ошибка парсинга JSON") Процедура Test_AZW3() Экспорт ВыполнитьТест("AZW3"); КонецПроцедуры @@ -908,7 +911,6 @@ &Тег("folded") &Тег("scalar") &Тег("1_3_err") -&Выключен("Неправильная обработка отступов в свернутых блоках") Процедура Test_6VJK() Экспорт ВыполнитьТест("6VJK"); @@ -923,7 +925,6 @@ &Тег("whitespace") &Тег("folded") &Тег("scalar") -&Выключен("Неправильная обработка отступов в блочных скалярах") Процедура Test_4QFQ() Экспорт ВыполнитьТест("4QFQ"); @@ -1076,7 +1077,6 @@ &ОтображаемоеИмя("DK95/06: Tabs that look like indentation") &Тег("indent") &Тег("whitespace") -&Выключен("Неправильная обработка табуляции") Процедура Test_DK95_06() Экспорт ВыполнитьТест("DK95/06"); @@ -1194,7 +1194,6 @@ &ОтображаемоеИмя("DE56/03: Trailing tabs in double quoted") &Тег("whitespace") &Тег("double") -&Выключен("Неправильная обработка табуляции в строках") Процедура Test_DE56_03() Экспорт ВыполнитьТест("DE56/03"); @@ -1220,7 +1219,6 @@ &ОтображаемоеИмя("DE56/02: Trailing tabs in double quoted") &Тег("whitespace") &Тег("double") -&Выключен("Неправильная обработка табуляции в строках") Процедура Test_DE56_02() Экспорт ВыполнитьТест("DE56/02"); @@ -1246,7 +1244,6 @@ &ОтображаемоеИмя("F6MC: More indented lines at the beginning of folded block scalars") &Тег("indent") &Тег("folded") -&Выключен("Неправильная обработка отступов в свернутых блоках") Процедура Test_F6MC() Экспорт ВыполнитьТест("F6MC"); @@ -1557,7 +1554,6 @@ &Тег("sequence") &Тег("error") &Тег("flow") -&Выключен("Не обрабатывается ошибка комментария после запятой") Процедура Test_CVW2() Экспорт ВыполнитьТест("CVW2"); @@ -1704,7 +1700,6 @@ &Тег("comment") &Тег("spec") &Тег("scalar") -&Выключен("Неправильная обработка обрезки конечных строк") Процедура Test_F8F9() Экспорт ВыполнитьТест("F8F9"); @@ -1813,7 +1808,6 @@ &Тест &ОтображаемоеИмя("H7TQ: Extra words on %YAML directive") &Тег("directive") -&Выключен("Не обрабатывается ошибка лишних слов в YAML директиве") Процедура Test_H7TQ() Экспорт ВыполнитьТест("H7TQ"); @@ -1841,7 +1835,6 @@ &ОтображаемоеИмя("W5VH: Allowed characters in alias") &Тег("alias") &Тег("1_3_err") -&Выключен("Ошибка обработки символов в алиасах") Процедура Test_W5VH() Экспорт ВыполнитьТест("W5VH"); @@ -1916,7 +1909,6 @@ &Тег("whitespace") &Тег("folded") &Тег("error") -&Выключен("Не обрабатывается ошибка неправильного отступа") Процедура Test_5LLU() Экспорт ВыполнитьТест("5LLU"); @@ -2086,7 +2078,6 @@ &ОтображаемоеИмя("2G84/00: Literal modifers") &Тег("literal") &Тег("scalar") -&Выключен("Не обрабатывается ошибка неправильных модификаторов") Процедура Test_2G84_00() Экспорт ВыполнитьТест("2G84/00"); @@ -2096,7 +2087,6 @@ &ОтображаемоеИмя("2G84/01: Literal modifers") &Тег("literal") &Тег("scalar") -&Выключен("Не обрабатывается ошибка неправильных модификаторов") Процедура Test_2G84_01() Экспорт ВыполнитьТест("2G84/01"); @@ -2160,7 +2150,6 @@ &ОтображаемоеИмя("SF5V: Duplicate YAML directive") &Тег("directive") &Тег("error") -&Выключен("Не обрабатывается ошибка дублированной YAML директивы") Процедура Test_SF5V() Экспорт ВыполнитьТест("SF5V"); @@ -2329,7 +2318,6 @@ &Тег("mapping") &Тег("double") &Тег("error") -&Выключен("Не обрабатывается ошибка неправильного отступа в маппинге") Процедура Test_U44R() Экспорт ВыполнитьТест("U44R"); @@ -2341,7 +2329,6 @@ &Тег("anchor") &Тег("sequence") &Тег("flow") -&Выключен("Неправильная обработка якорей в последовательностях") Процедура Test_CN3R() Экспорт ВыполнитьТест("CN3R"); @@ -2398,7 +2385,6 @@ &Тег("mapping") &Тег("comment") &Тег("error") -&Выключен("Не обрабатывается ошибка комментария как ключа маппинга") Процедура Test_GDY7() Экспорт ВыполнитьТест("GDY7"); @@ -2419,7 +2405,6 @@ &Тег("document") &Тег("directive") &Тег("error") -&Выключен("Не обрабатывается ошибка директивы без документа") Процедура Test_B63P() Экспорт ВыполнитьТест("B63P"); @@ -2481,7 +2466,6 @@ &Тег("comment") &Тег("error") &Тег("flow") -&Выключен("Не обрабатывается ошибка пропущенной запятой в flow") Процедура Test_CML9() Экспорт ВыполнитьТест("CML9"); @@ -2506,7 +2490,6 @@ &Тег("folded") &Тег("scalar") &Тег("error") -&Выключен("Не обрабатывается ошибка лишних пробелов в блочных скалярах") Процедура Test_S98Z() Экспорт ВыполнитьТест("S98Z"); @@ -2551,7 +2534,6 @@ &Тег("mapping") &Тег("whitespace") &Тег("error") -&Выключен("Не обрабатывается ошибка табуляции в отступах маппинга") Процедура Test_4EJS() Экспорт ВыполнитьТест("4EJS"); @@ -2705,7 +2687,6 @@ &ОтображаемоеИмя("3HFZ: Invalid content after document end marker") &Тег("footer") &Тег("error") -&Выключен("Не обрабатывается ошибка содержимого после маркера конца документа") Процедура Test_3HFZ() Экспорт ВыполнитьТест("3HFZ"); @@ -2734,7 +2715,6 @@ &Тег("literal") &Тег("whitespace") &Тег("scalar") -&Выключен("Неправильная обработка keep модификатора блочного скаляра") Процедура Test_6FWR() Экспорт ВыполнитьТест("6FWR"); @@ -2784,7 +2764,6 @@ &Тест &ОтображаемоеИмя("MUS6/00: Directive variants") &Тег("directive") -&Выключен("Не обрабатывается ошибка неправильных вариантов директив") Процедура Test_MUS6_00() Экспорт ВыполнитьТест("MUS6/00"); @@ -2793,7 +2772,6 @@ &Тест &ОтображаемоеИмя("MUS6/01: Directive variants") &Тег("directive") -&Выключен("Не обрабатывается ошибка неправильных вариантов директив") Процедура Test_MUS6_01() Экспорт ВыполнитьТест("MUS6/01"); @@ -2865,7 +2843,6 @@ &ОтображаемоеИмя("E76Z: Aliases in Implicit Block Mapping") &Тег("mapping") &Тег("alias") -&Выключен("Ошибка обработки алиасов в неявных блочных маппингах") Процедура Test_E76Z() Экспорт ВыполнитьТест("E76Z"); @@ -2933,7 +2910,6 @@ &Тег("literal") &Тег("spec") &Тег("scalar") -&Выключен("Неправильная обработка обрезки финального переноса строки") Процедура Test_A6F9() Экспорт ВыполнитьТест("A6F9"); @@ -2949,7 +2925,6 @@ &Тест &ОтображаемоеИмя("Y79Y/000: Tabs in various contexts") &Тег("whitespace") -&Выключен("Не обрабатывается ошибка табуляции в различных контекстах") Процедура Test_Y79Y_000() Экспорт ВыполнитьТест("Y79Y/000"); @@ -2972,7 +2947,6 @@ &Тест &ОтображаемоеИмя("Y79Y/001: Tabs in various contexts") &Тег("whitespace") -&Выключен("Неправильная обработка табуляции в различных контекстах") Процедура Test_Y79Y_001() Экспорт ВыполнитьТест("Y79Y/001"); @@ -3002,7 +2976,6 @@ &Тест &ОтображаемоеИмя("Y79Y/004: Tabs in various contexts") &Тег("whitespace") -&Выключен("Не обрабатывается ошибка табуляции в различных контекстах") Процедура Test_Y79Y_004() Экспорт ВыполнитьТест("Y79Y/004"); @@ -3011,7 +2984,6 @@ &Тест &ОтображаемоеИмя("Y79Y/005: Tabs in various contexts") &Тег("whitespace") -&Выключен("Не обрабатывается ошибка табуляции в различных контекстах") Процедура Test_Y79Y_005() Экспорт ВыполнитьТест("Y79Y/005"); @@ -3055,7 +3027,6 @@ &Тест &ОтображаемоеИмя("JEF9/02: Trailing whitespace in streams") &Тег("literal") -&Выключен("Неправильная обработка завершающих пробелов в потоках") Процедура Test_JEF9_02() Экспорт ВыполнитьТест("JEF9/02"); @@ -3072,7 +3043,6 @@ &Тест &ОтображаемоеИмя("JEF9/01: Trailing whitespace in streams") &Тег("literal") -&Выключен("Неправильная обработка завершающих пробелов в потоках") Процедура Test_JEF9_01() Экспорт ВыполнитьТест("JEF9/01"); @@ -3101,7 +3071,6 @@ &Тег("folded") &Тег("scalar") &Тег("1_3_err") -&Выключен("Неправильная обработка свернутых строк и финальных пустых строк") Процедура Test_7T8X() Экспорт ВыполнитьТест("7T8X"); @@ -3125,7 +3094,6 @@ &Тег("literal") &Тег("whitespace") &Тег("error") -&Выключен("Не обрабатывается ошибка лишних пробелов в первой строке") Процедура Test_W9L4() Экспорт ВыполнитьТест("W9L4"); @@ -3252,7 +3220,6 @@ &ОтображаемоеИмя("9MMA: Directive by itself with no document") &Тег("directive") &Тег("error") -&Выключен("Не обрабатывается ошибка директивы без документа") Процедура Test_9MMA() Экспорт ВыполнитьТест("9MMA"); @@ -3273,7 +3240,6 @@ &Тег("mapping") &Тег("alias") &Тег("whitespace") -&Выключен("Неправильная обработка пробелов вокруг двоеточия в маппингах") Процедура Test_26DV() Экспорт ВыполнитьТест("26DV"); @@ -3293,7 +3259,6 @@ &Тег("mapping") &Тег("error") &Тег("flow") -&Выключен("Не обрабатывается ошибка пропущенной запятой в flow маппинге") Процедура Test_T833() Экспорт ВыполнитьТест("T833"); @@ -3349,7 +3314,6 @@ &Тег("folded") &Тег("scalar") &Тег("1_3_err") -&Выключен("Неправильная обработка блочного свертывания") Процедура Test_MJS9() Экспорт ВыполнитьТест("MJS9"); @@ -3567,7 +3531,6 @@ &Тег("anchor") &Тег("error") &Тег("header") -&Выключен("Не обрабатывается ошибка якоря на строке начала документа") Процедура Test_CXX2() Экспорт ВыполнитьТест("CXX2"); @@ -3579,7 +3542,6 @@ &Тег("alias") &Тег("edge") &Тег("1_3_err") -&Выключен("Неправильная обработка якорей с двоеточием в имени") Процедура Test_2SXE() Экспорт ВыполнитьТест("2SXE"); @@ -3591,7 +3553,6 @@ &Тег("sequence") &Тег("error") &Тег("flow") -&Выключен("Не обрабатывается ошибка неявного ключа с переносом строки") Процедура Test_DK4H() Экспорт ВыполнитьТест("DK4H"); @@ -3606,7 +3567,6 @@ &Тег("spec") &Тег("whitespace") &Тег("sequence") -&Выключен("Неправильная обработка индикаторов отступа") Процедура Test_A2M4() Экспорт ВыполнитьТест("A2M4"); @@ -3713,7 +3673,6 @@ &Тег("spec") &Тег("folded") &Тег("scalar") -&Выключен("Неправильная обработка заголовка блочного скаляра") Процедура Test_P2AD() Экспорт ВыполнитьТест("P2AD"); @@ -3760,7 +3719,6 @@ &Тег("anchor") &Тег("sequence") &Тег("error") -&Выключен("Не обрабатывается ошибка якоря перед элементом последовательности") Процедура Test_SY6V() Экспорт ВыполнитьТест("SY6V"); @@ -4033,4 +3991,4 @@ &Тег("scalar") Процедура Test_9SHH() Экспорт ВыполнитьТест("9SHH"); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры diff --git a/utils/load-deserialize.os b/utils/load-deserialize.os new file mode 100644 index 0000000..7b76933 --- /dev/null +++ b/utils/load-deserialize.os @@ -0,0 +1,37 @@ +#Использовать ".." + +Функция БольшойYaml(КоличествоЭлементов) + СтрокиYaml = Новый Массив(); + СтрокиYaml.Добавить("items:"); + + Для Индекс = 1 По КоличествоЭлементов Цикл + СтрокиYaml.Добавить(" - id: " + Строка(Индекс)); + СтрокиYaml.Добавить(" name: item-" + Строка(Индекс)); + СтрокиYaml.Добавить(" enabled: true"); + СтрокиYaml.Добавить(" labels:"); + СтрокиYaml.Добавить(" - build"); + СтрокиYaml.Добавить(" - test"); + СтрокиYaml.Добавить(" limits:"); + СтрокиYaml.Добавить(" cpu: " + Строка(Индекс % 16 + 1)); + СтрокиYaml.Добавить(" memory: " + Строка(Индекс % 64 + 128)); + КонецЦикла; + + Возврат СтрСоединить(СтрокиYaml, Символы.ПС); +КонецФункции + +КоличествоЭлементов = 5000; +Парсер = Новый ПарсерYaml(); +Yaml = БольшойYaml(КоличествоЭлементов); +Документ = Парсер.ПрочитатьYaml(Yaml); +Элементы = Документ.Получить("items"); + +Если Элементы.Количество() <> КоличествоЭлементов Тогда + ВызватьИсключение СтрШаблон( + "Ожидалось %1 элементов, получено %2", + КоличествоЭлементов, + Элементы.Количество()); +КонецЕсли; + +Сообщить("items=" + Строка(КоличествоЭлементов)); +Сообщить("yaml-bytes=" + Строка(СтрДлина(Yaml))); +Сообщить("items-parsed=" + Строка(Элементы.Количество())); diff --git a/utils/load-serialize.os b/utils/load-serialize.os new file mode 100644 index 0000000..c02b9cd --- /dev/null +++ b/utils/load-serialize.os @@ -0,0 +1,40 @@ +#Использовать ".." + +Функция БольшойОбъект(КоличествоЭлементов) + Документ = Новый Соответствие(); + Элементы = Новый Массив(); + + Для Индекс = 1 По КоличествоЭлементов Цикл + Элемент = Новый Соответствие(); + Элемент.Вставить("id", Индекс); + Элемент.Вставить("name", "item-" + Строка(Индекс)); + Элемент.Вставить("enabled", Истина); + + Метки = Новый Массив(); + Метки.Добавить("build"); + Метки.Добавить("test"); + Элемент.Вставить("labels", Метки); + + Лимиты = Новый Соответствие(); + Лимиты.Вставить("cpu", Индекс % 16 + 1); + Лимиты.Вставить("memory", Индекс % 64 + 128); + Элемент.Вставить("limits", Лимиты); + + Элементы.Добавить(Элемент); + КонецЦикла; + + Документ.Вставить("items", Элементы); + Возврат Документ; +КонецФункции + +КоличествоЭлементов = 5000; +Парсер = Новый ПарсерYaml(); +Документ = БольшойОбъект(КоличествоЭлементов); +Yaml = Парсер.ЗаписатьYaml(Документ); + +Если СтрДлина(Yaml) = 0 Тогда + ВызватьИсключение "Сериализация вернула пустую строку"; +КонецЕсли; + +Сообщить("items=" + Строка(КоличествоЭлементов)); +Сообщить("serialized-bytes=" + Строка(СтрДлина(Yaml)));