Java-парсер синтакс-помощника (.hbk) платформы 1С:Предприятие 8.
Извлекает из файлов справки полную модель: типы (включая коллекции и
формы с их параметрами), методы, свойства, события, конструкторы,
перечисления, глобальный контекст и языковые конструкции встроенного
языка (литералы, операторы, директивы компиляции, аннотации, инструкции
препроцессора) — с метаданными (версии появления и депрекации, описания,
примеры, ссылки «См. также», значения по умолчанию для параметров,
рекомендации по замене устаревших элементов).
Парсятся оба парных HBK платформы:
shcntx_*.hbk— типы, методы, свойства, события, перечисления, глобальный контекст;shlang_*.hbk— раздел «Встроенный язык»: примитивы, литералы (Истина/Ложь), операторы и управляющие конструкции (Если,Для,Пока,Попытка,Новый,?,[...],И/ИЛИ/НЕ), объявления (Процедура,Функция,Перем), директивы компиляции (&НаКлиенте,&НаСервере, …), аннотации (&Перед,&После, …), инструкции препроцессора (#Если,#Область, …).
Предназначен для использования в инструментах статического анализа
кода 1С — в первую очередь как источник платформенных типов для
bsl-language-server.
- Распаковка
.hbk— самостоятельно вытаскивает FileStorage из контейнера (внутри это два вложенных ZIP) в память, без записи десятков тысяч HTML-файлов на диск. Полный парсинг русского синтакс-помощника современной платформы занимает порядка секунды. - Полная модель элементов
(
api/):ContextType— платформенный тип со свойствами / методами / событиями / конструкторами, параметрами формы и описанием из СП;ContextCollection extends ContextType— коллекции (Массив,Соответствие,Структура,ТаблицаЗначений,ЭлементыФормы, …): типы элементов + поддержка обходаДля каждогои индексатора[...]с их описаниями (блок «Элементы коллекции:» страницы типа);ContextFormParameter— параметры формы (ContextType.formParameters(), непустой уФормаКлиентскогоПриложения, расширений формы для справочника / документа / отчёта / динамического списка, системных форм сохранения и загрузки настроек): ключи структуры, которая передаётся вОткрытьФорму(…, ПараметрыФормы)и читается внутри формы черезЭтаФорма.Параметры. У ключевых параметров (участвуют в ключе уникальности окна) взведёнisKey();ContextEnum/ContextEnumValue— системные перечисления и их значения; у enum-«библиотек» (БиблиотекаКартинок,ЦветаСтиля, …) заполненvalueType()— общий тип всех значений набора;ContextMethod(в т.ч. флагisAsync()для…Асинх/…Async),ContextProperty(сaccessMode()и per-propertycollectionElementTypes()),ContextEvent,ContextConstructor;ContextMethodSignature(с поддержкой нескольких вариантов синтаксиса) иContextSignatureParameter(isRequired(),defaultValue(),isVariadic()для форм<Знач1>,...,<ЗначN>);PlatformGlobalContext— глобальный контекст (top-level методы, свойства, события приложения / обычного приложения / сеанса / внешнего соединения);ContextLanguageKeyword+LanguageKeywordCategory(LITERAL,STATEMENT,OPERATOR,DECLARATION,PRAGMA,ANNOTATION,PREPROCESSOR_INSTRUCTION) +LanguageKeywordSnippet(двуязычный шаблон автодополнения с плейсхолдерами<?>);KnownStandardAttributes— стандартные реквизиты MD-объектов (Ссылка,ПометкаУдаления,Проведен, …) по типу-владельцу: их состав знает только платформа, в СП и mdclasses его нет.
- Примитивные типы —
Строка,Число,Дата,Булево,Тип,Null,Неопределено— приходят какContextKind.PRIMITIVE_TYPEсо своими описаниями из синтакс-помощника.Произвольный(псевдо-маркер «любой тип») у платформы отдельной страницы не имеет и публикуется как синтетический примитив с тем жеkind. - Метаданные:
sinceVersion,deprecatedSinceVersion,recommendedReplacements,description,notes(«Примечание:» / «Замечание:»),availabilities,examples(«Пример:»),seeAlso(«См. также:» — имена квалифицируются владельцем:Владелец.Член). Снимаются не только с member-страниц, но и с главных страниц типов, коллекций и перечислений — то есть уМассивестьsinceVersion() == "8.0", доступность по клиентам и пример кода, а уГруппировкаКолонок— описание, доступность и «См. также:». Специфичные для элемента:returnValueDescriptionиisAsyncметода,syntaxText(сырая строкаСинтаксис:),defaultValue/isVariadicпараметра,accessModeсвойства,isKeyпараметра формы. - Блок кода и футер страницы. Пример кода в HBK — таблица с фоном
#f7f7f7; распознаётся по ней, а не по чаптеру «Пример:», потому что на части страниц слово «Пример:» вписано прямо в текст описания через<BR>, а таблица лежит внутри незакрытого<p>(ВнешнийОтчет.ОбработкаПроверкиЗаполнения) — иначе код целиком утекал бы в описание. Внизу каждой из 25503 страниц стоит<HR>и ссылка «Методическая информация» на сайт 1С: разбор обрывается на этой линии, иначе футер приклеивается к последней открытой секции — например к синтаксису конструктора (Новый ГрафическаяСхема). - Generic-типы. Типы вида
СправочникСсылка.<Имя справочника>и свойства видаСправочникиМенеджер :: <Имя справочника>— плейсхолдеры, конкретизация которых приходит из конфигурации и парсится отдельным проектомMDClasses. Все такие элементы помечены флагомisGeneric()через эвристику вContextNames. - Имя контекста — с заголовка страницы (
V8SH_pagetitle), а не из оглавления HBK: в оглавлении узел назван относительно родителя («Поле ввода» → «Расширение»), что вне дерева бессмысленно и вдобавок неуникально. На странице стоит полное имя — «Расширение поля ввода системного перечисления». В 8.3.27 так уточняются 209 типов из 2420, и число неуникальных имён падает с 27 до 10. Исключение — страница, на которую ведёт несколько узлов оглавления: так платформа оформляет переименования (PlannerCommandSource.html— это и «ИсточникКомандПланировщика», устаревший с 8.3.23, и «ИсточникКомандПоляПланировщика», причём наборы значений у них разные). Для таких узлов имя берётся из оглавления — только оно их и различает. - Омонимы. Оставшиеся совпадения имён — от самой платформы: например,
«Расширение элементов управления, расположенных в форме» существует
отдельно для обычных (8.0) и управляемых (8.2) форм, причём и ru-, и
en-имена у них одинаковые. Оба контекста есть в модели;
getContextByNameвернёт какой-то один, аgetContextsByName— все, различить их можно поsinceVersion(),availabilities()или составу членов. - Двуязычие (ru + en). Имена самих сущностей приходят сразу с
обоими языками. Имена вариантов сигнатур и параметров, а также
все тексты (описания, примеры, «Замечание:», «См. также:») в одной
HBK живут только на одном языке — для них есть
BilingualMerger, который парсит обе версии (shcntx_ru.hbk+shcntx_root.hbk), сопоставляет контексты по пути страницы внутри HBK (пути ru и en совпадают файл-в-файл, а имена расходятся: на ru-странице в скобках может стоять устаревший английский вариант), подтягивает en-алиасы в ru-провайдер, а en-тексты кладёт рядом какEnAttachments— их отдаётPlatformContextProvider.getEnAttachments(x)(сам объект модели остаётся ru). Ключ карты — сам объект (IdentityHashMap), так что спрашивать можно про контекст, метод, свойство, событие, конструктор, значение перечисления, параметр формы, вариант сигнатуры и параметр. Языковые конструкции изshlang_ru.hbkподмешивают en-алиасы из парногоshlang_root.hbk(имена body-keyword'ов вродеТогда/Then,КонецЕсли/EndIfсматчиваются по позиции тегов на синхронных страницах ru/en), а двуязычные сниппеты автодополнения тащатся прямо из парного.st-файла. - Автодетект установленной платформы —
PlatformFinderна Windows / Linux / macOS, аналог OneScript-библиотекиv8find. Можно запросить самую свежую версию (findLatest()) или конкретную (findVersion("8.3.27.1786")).
Релизы публикуются в Maven Central:
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.1c-syntax:bsl-context:<version>")
}SNAPSHOT'ы (сборки с master) — в snapshot-репозитории Central:
repositories {
maven(url = "https://central.sonatype.com/repository/maven-snapshots/")
}import com.github._1c_syntax.bsl.context.PlatformContextGrabber;
import com.github._1c_syntax.bsl.context.api.ContextProvider;
import com.github._1c_syntax.bsl.context.api.ContextType;
// Автодетект самой свежей установленной платформы.
var grabber = PlatformContextGrabber.autoDetect(null);
grabber.parse();
ContextProvider ctx = grabber.getProvider();
// Резолв по имени (ru или en, регистронезависимый).
var array = ctx.getContextByName("Массив"); // или "Array"
// Перебор типов.
ctx.getContexts().stream()
.filter(c -> c instanceof ContextType)
.map(c -> (ContextType) c)
.forEach(type -> {
System.out.println(type.name() + (type.isGeneric() ? " [generic]" : ""));
type.methods().forEach(m -> {
var since = m.sinceVersion();
var dep = m.deprecatedSinceVersion();
System.out.println(" " + m.name()
+ (since.isEmpty() ? "" : " since=" + since)
+ (dep.isEmpty() ? "" : " DEPRECATED since=" + dep
+ " → " + String.join(", ", m.recommendedReplacements())));
});
});
// Глобальный контекст.
ctx.getGlobalContext().methods().forEach(m -> System.out.println(m.name()));// 1. Автодетект — берёт самую свежую установку, найденную PlatformFinder.
PlatformContextGrabber.autoDetect(workDir);
// 2. По каталогу bin платформы.
PlatformContextGrabber.fromPlatformBin(platformBin, workDir);
// 3. По явному пути к .hbk.
PlatformContextGrabber.fromHbk(hbkFile, workDir);workDir может быть null (и у каждого метода есть перегрузка без него)
— тогда будет использован временный каталог.
parse() сам собирает всё, что лежит рядом с указанным shcntx_ru.hbk:
парный shcntx_root.hbk (двуязычие — en-алиасы сигнатур/параметров и
en-тексты) и shlang_ru.hbk + shlang_root.hbk (примитивы и языковые
конструкции). Отдельно дёргать мердж нужно только если en-файл лежит
не рядом:
grabber.parseBilingual(enHbkFile);shcntx_*.hbk shlang_*.hbk
├─ FileStorage (ZIP) ─► in-memory Map └─ FileStorage (плоский,
└─ PackBlock (ZIP) ─► TableOfContent ru+en) ─► ShlangParser
│ │
▼ │
HbkTreeParser ◄──── extra:Context┘
│ для каждой страницы
▼
HtmlParser ── через PageSource
│ извлекает структурные секции
▼
PlatformContext* объекты (rawTypes = строки)
│
▼
PlatformContextProvider
│ resolve: имена → ссылки на Context
▼ (в т.ч. в shlang-примитивы)
ContextProvider (готов к использованию)
Ключевые компоненты:
| Класс | Роль |
|---|---|
PlatformContextGrabber |
Точка входа: fromHbk / fromPlatformBin / autoDetect / parseBilingual. |
PlatformFinder |
Поиск установок платформы 1С на машине (v8find-аналог). |
HbkContainerExtractor |
Разбирает внешний .hbk-контейнер на FileStorage + PackBlock. |
HbkTreeParser |
Обходит дерево HBK и для каждой страницы строит PlatformContext*-объект через HtmlParser. |
HtmlParser |
Извлекает структурные секции HTML-страницы в *Description-DTO. |
ShlangParser |
Парсит раздел «Встроенный язык» из shlang_*.hbk: примитивные типы и языковые конструкции (литералы, операторы, директивы, аннотации, инструкции препроцессора). Сниппеты автодополнения и en-алиасы вытаскивает из парного shlang_root.hbk. |
PageSource |
Абстракция «открыть страницу по пути». Реализации: InMemory (production) и FileSystem (тесты на распакованных фикстурах). |
PlatformContextProvider |
Хранит готовые контексты, резолвит строковые ссылки в объекты Context и отдаёт en-тексты через getEnAttachments. |
BilingualMerger |
Подтягивает en-алиасы и en-тексты (EnAttachments) из en-провайдера в ru-провайдер. |
ContextNames |
Утилиты по именам: isGeneric, typeParameters, familyCore, placeholders. |
KnownStandardAttributes |
Справочник стандартных реквизитов MD-объектов (в HBK их состава нет). |
API-интерфейсы в
api/ не
зависят от реализаций и не тащат сторонних библиотек — потребитель
пишет адаптер к своей модели прямо через них.
Узкое место в наивной реализации — запись десятков тысяч HTML-файлов
на диск (особенно на NTFS). bsl-context обходит её через
PageSource.InMemory: FileStorage читается в Map<String, byte[]>
и парсится прямо из памяти, без затрагивания файловой системы.
Полный парсинг shcntx_ru.hbk современной платформы занимает порядка
1–2 секунд на ноутбуке среднего класса. После завершения парсинга
in-memory карта страниц очищается, чтобы освободить heap.
Требуется Java 21+ (включена через Gradle toolchain).
./gradlew build # сборка + тесты
./gradlew publishToMavenLocal # положить артефакт в ~/.m2В обычный прогон не включён — требует установленной 1С. Запускать вручную, выставив env-флаг:
BSL_CONTEXT_REAL_HBK=true ./gradlew test --tests "*Smoke*"Тесты автоматически находят свежую установку через PlatformFinder
и парсят пары shcntx_ru.hbk + shcntx_root.hbk (типы) и
shlang_ru.hbk + shlang_root.hbk (примитивы и языковые конструкции).
Проверяются: наличие ключевых типов, срабатывание generic-эвристики,
корректность двуязычного мерджа, коллекции с их элементами, valueType
enum-«библиотек», параметры формы, разрешение типов параметров методов
в shlang-примитивы по ссылочной идентичности.
ContextProvider
├─ getContexts(): List<Context> // типы, коллекции, перечисления, keyword'ы
├─ getContextByName(name): Optional<Context> // ru или en, case-insensitive; при омонимах — любой из них
├─ getContextsByName(name): List<Context> // все омонимы (см. ниже)
└─ getGlobalContext(): PlatformGlobalContext // top-level
PlatformContextProvider (реализация)
└─ getEnAttachments(x): EnAttachments // en-тексты для любого элемента модели
└─ description, returnValueDescription, notes, examples, seeAlso,
forEachDescription, indexAccessDescription
Context
├─ name(): ContextName(ru, en)
├─ kind(): ContextKind { PRIMITIVE_TYPE, TYPE, COLLECTION, ENUM, GLOBAL_CONTEXT, LANGUAGE_KEYWORD }
├─ isGeneric(): boolean
├─ typeParameters(): List<String> // «СправочникСсылка.<Имя справочника>» → [Имя справочника]
├─ familyCore(): String // → «СправочникСсылка»
│ // «страничные» метаданные главной страницы контекста:
├─ description(), notes(): String // «Описание:», «Примечание:»/«Замечание:»
├─ availabilities(): List<Availability> // «Доступность:»
├─ sinceVersion(), deprecatedSinceVersion(): String
├─ examples(): List<String> // «Пример:»
├─ seeAlso(): List<String> // «См. также:» → «Владелец.Член»
└─ recommendedReplacements(): List<String>
ContextType extends Context
├─ methods(): List<ContextMethod>
├─ properties(): List<ContextProperty>
├─ events(): List<ContextEvent>
├─ constructors(): List<ContextConstructor>
└─ formParameters(): List<ContextFormParameter> // непусто только у типов-форм
ContextCollection extends ContextType
├─ collectionElementTypes(): List<Context>
├─ supportsForEach(): boolean, forEachDescription(): String
└─ supportsIndexAccess(): boolean, indexAccessDescription(): String
ContextFormParameter
├─ name(): ContextName
├─ types(): List<Context>
├─ isKey(): boolean // «Использование: Ключевой»
├─ description(), sinceVersion(), deprecatedSinceVersion(): String
└─ seeAlso(), recommendedReplacements(): List<String>
ContextLanguageKeyword extends Context
├─ category(): LanguageKeywordCategory
│ { LITERAL, STATEMENT, OPERATOR, DECLARATION,
│ PRAGMA, ANNOTATION, PREPROCESSOR_INSTRUCTION }
├─ description(): String
└─ snippet(): LanguageKeywordSnippet(ru, en) // шаблон с плейсхолдерами <?>
ContextMethod
├─ name(): ContextName
├─ description(), notes(), returnValueDescription(): String
├─ examples(), seeAlso(), recommendedReplacements(): List<String>
├─ availabilities(): List<Availability>
├─ signatures(): List<ContextMethodSignature>
├─ hasReturnValue(): boolean, returnValues(): List<Context>
├─ sinceVersion(), deprecatedSinceVersion(): String
├─ isAsync(): boolean // …Асинх / …Async (await-методы 8.3.18+)
└─ isGeneric(): boolean
ContextMethodSignature
├─ name(): ContextName // имя варианта
├─ parameters(): List<ContextSignatureParameter>
├─ description(): String
└─ syntaxText(): String // сырая строка «Получить(<Индекс>)»
ContextSignatureParameter
├─ name(): ContextName
├─ isRequired(): boolean
├─ isVariadic(): boolean // <Знач1>,...,<ЗначN> → имя-база «Знач»
├─ types(): List<Context>
├─ description(): String
└─ defaultValue(): String
ContextProperty
├─ name(): ContextName
├─ accessMode(): AccessMode { READ, READ_WRITE }
├─ types(): List<Context>
├─ collectionElementTypes(): List<Context> // «Элементами коллекции являются объекты типа …»
├─ description(), notes(), sinceVersion(), deprecatedSinceVersion(): String
├─ examples(), seeAlso(), recommendedReplacements(): List<String>
├─ availabilities(): List<Availability>
└─ isGeneric(): boolean
ContextEvent
├─ name(): ContextName
├─ signatures(): List<ContextMethodSignature>
├─ description(), notes(), sinceVersion(), deprecatedSinceVersion(): String
├─ availabilities(): List<Availability>
├─ examples(), seeAlso(): List<String>
└─ recommendedReplacements(): List<String>
ContextConstructor
├─ name(): ContextName // «По количеству элементов» и т.п.
├─ parameters(): List<ContextSignatureParameter>
├─ description(), syntaxText(): String
├─ sinceVersion(), deprecatedSinceVersion(): String
├─ examples(), seeAlso(): List<String>
└─ recommendedReplacements(): List<String>
ContextEnum extends Context // страничные метаданные — из Context
├─ values(): List<ContextEnumValue>
└─ valueType(): Optional<ContextName> // у enum-«библиотек»: БиблиотекаКартинок → Картинка
ContextEnumValue
├─ name(): ContextName
├─ description(), sinceVersion(), deprecatedSinceVersion(): String
└─ recommendedReplacements(): List<String>
PlatformGlobalContext extends Context
├─ methods(): List<ContextMethod>, properties(): List<ContextProperty>
├─ applicationEvents(), ordinaryApplicationEvents(),
│ sessionModuleEvents(), externalConnectionModuleEvents(): List<ContextEvent>
└─ sinceVersion(), deprecatedSinceVersion(): String
Availability { THIN_CLIENT, WEB_CLIENT, MOBILE_CLIENT, SERVER, THICK_CLIENT,
EXTERNAL_CONNECTION, MOBILE_APPLICATION_CLIENT,
MOBILE_APPLICATION_SERVER, MOBILE_STANDALONE_SERVER }
KnownStandardAttributes
└─ forOwner("ОбъектМетаданных: Документ"): List<ContextName> // стандартные реквизиты
ContextNames
├─ isGeneric(name), familyCore(name), typeParameters(name)
└─ placeholders(raw): List<Placeholder> // позиции <…> в имени
LGPL-3.0-or-later.
Содержимое .hbk-файлов платформы 1С — собственность фирмы «1С» и
в репозиторий не включено. Тесты используют обезличенные
HTML-фикстуры, повторяющие разметку синтакс-помощника.