feat(cfe-patch-method,cfe-diff,cfe-validate): распознавание английских ключевых слов BSL

Платформа принимает встроенный язык в двух написаниях, а навыки читали только
русское: в расширении на английском перехватчики и блоки правок не находились,
и отчёт выглядел чистым. -Check молчал, cfe-diff не показывал вставки,
счётчик контролируемых методов в cfe-validate давал ноль.

Пары ключевых слов сверены по таблицам строк платформы (backbas.dll, bsl.dll),
а не по памяти. Разбор стал двуязычным везде, где навык читает модуль:
аннотации, объявления и Конец*, Знач, директивы контекста, препроцессор в
цепочке обрамления, поиск региона при переиспользовании.

Маркеры правок распознаются по началу строки, а не сравнением целой строки, —
заодно перестал теряться хвостовой комментарий (#Вставка // старая логика).

Эмиссия подчиняется одному правилу: отдаём тем языком, который прочитали.
Генерация берёт язык метода-источника, -Actualize — язык переписываемого
перехватчика, чтобы актуализация не превращала чужие Procedure и #Insert в
русские. Комментарии и вывод в консоль остаются русскими.

Таблица ключевых слов и разбор аннотаций скопированы в три навыка и заведены
семьями в реестре анти-дрейфа: разъехавшаяся пара слов дала бы ровно тот же
тихий ложно-чистый отчёт.

Проверено платформой: расширение с &ChangeAndValidate принимается
(/CheckCanApplyConfigurationExtensions), после дрейфа оригинала отвергается,
после -Actualize принимается снова.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-09-19 17:49:30 +03:00
co-authored by Claude Opus 5
parent e2c7f38518
commit 1b2c9388e8
7 changed files with 630 additions and 200 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cfe-validate v1.16 — Validate 1C configuration extension XML structure (CFE)
# cfe-validate v1.17 — Validate 1C configuration extension XML structure (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates extension Configuration.xml: root, InternalInfo, extension properties, ChildObjects, borrowed objects."""
import sys, os, argparse, re
@@ -27,6 +27,40 @@ def ci_parse_args(parser, argv=None):
return parser.parse_args(argv)
# Built-in language keywords in both spellings. The platform accepts either one in any module
# (pairs taken from the platform string tables), so a module written in English is ordinary
# source, not a broken one: we must read both and emit the spelling we read.
def bsl_keywords():
return {
"ru": {
"Async": "Асинх", "Proc": "Процедура", "EndProc": "КонецПроцедуры",
"Func": "Функция", "EndFunc": "КонецФункции", "Val": "Знач",
"Region": "Область", "EndRegion": "КонецОбласти",
"If": "Если", "Then": "Тогда", "ElsIf": "ИначеЕсли", "Else": "Иначе", "EndIf": "КонецЕсли",
"And": "И", "Not": "НЕ",
"Insert": "Вставка", "EndInsert": "КонецВставки", "Delete": "Удаление", "EndDelete": "КонецУдаления",
"Before": "Перед", "After": "После", "Around": "Вместо", "Control": "ИзменениеИКонтроль",
"Proceed": "ПродолжитьВызов", "Return": "Возврат",
# Not a keyword: name of the local the generated Instead-stub declares. Lives here so
# that the language of emitted text is decided in exactly one place.
"ResultVar": "Результат",
"Directives": ["НаКлиенте", "НаСервере", "НаСервереБезКонтекста", "НаКлиентеНаСервереБезКонтекста", "НаКлиентеНаСервере"],
},
"en": {
"Async": "Async", "Proc": "Procedure", "EndProc": "EndProcedure",
"Func": "Function", "EndFunc": "EndFunction", "Val": "Val",
"Region": "Region", "EndRegion": "EndRegion",
"If": "If", "Then": "Then", "ElsIf": "ElsIf", "Else": "Else", "EndIf": "EndIf",
"And": "And", "Not": "Not",
"Insert": "Insert", "EndInsert": "EndInsert", "Delete": "Delete", "EndDelete": "EndDelete",
"Before": "Before", "After": "After", "Around": "Around", "Control": "ChangeAndValidate",
"Proceed": "ProceedWithCall", "Return": "Return",
"ResultVar": "Result",
"Directives": ["AtClient", "AtServer", "AtServerNoContext", "AtClientAtServerNoContext", "AtClientAtServer"],
},
}
NS = {
'md': 'http://v8.1c.ru/8.3/MDClasses',
'v8': 'http://v8.1c.ru/8.1/data/core',
@@ -1267,6 +1301,8 @@ def main():
r.warn(f'16. {issue}')
# --- Breadcrumb: controlled methods (&ИзменениеИКонтроль) drift is not checked here ---
ctrl_kw = bsl_keywords()
ctrl_re = re.compile(r'^\s*&(?:' + ctrl_kw["ru"]["Control"] + '|' + ctrl_kw["en"]["Control"] + r')\(')
ctrl_count = 0
for dp, _dn, files in os.walk(config_dir):
for fn in files:
@@ -1274,7 +1310,7 @@ def main():
try:
with open(os.path.join(dp, fn), 'r', encoding='utf-8-sig') as f:
for ln in f:
if re.match(r'^\s*&ИзменениеИКонтроль\(', ln):
if ctrl_re.match(ln):
ctrl_count += 1
except OSError:
pass