From ed2339a4bc07541be41301af96b0c847da110ac0 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 13 Jun 2026 11:39:30 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-compile):=20=D0=B7=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20v8:Type=20=C2=AB=D0=9D=D0=B5=D0=BE?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=BE=C2=BB=20?= =?UTF-8?q?=E2=80=94=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20xmlns=20=D0=BD=D0=B0=20=D1=82=D0=B5=D0=B3=D0=B5=20(?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=20+=20=D0=BF=D0=B0=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=B5=D1=82=D1=80=20=D0=B4=D0=B8=D0=BD-=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Значение типа v8:Type (на практике всегда :Undefined — тип «Неопределено» из namespace http://v8.1c.ru/8.2/data/types, префикс авто d6p1/d8p1/dN…) эмитилось без объявления namespace → битый QName; а в параметре дин-списка компилятор вообще ронял v8:Type → xs:string. Корпус 8.3.24: 11 тегов (6 фильтра + 5 параметра), значение всегда prefix:Undefined, ns всегда data/types. Топ ROOT-пробел нового baseline (Attribute>value 48 LOST + 44 ADDED). Фикс: хелпер Get-ValueTypeNsAttr / _value_type_ns_attr (объявляет xmlns: для не-стандартного префикса при valueType v8:Type) в обе ветки Emit-FilterItem (скаляр + массив op `in`) + новая ветка v8:Type в Emit-DLValue / emit_dl_value. Выборка 7 форм (Взаимодействия acc/erp, ЖурналОпераций×3, ДокументЭДОБЗК, ЧекиККМ): match 0→6, TOTAL→0. Зеркало py байт-в-байт, регресс 43/43 (ps1+py). Раундтрип восстанавливает точные исходные байты платформы (её собственный формат — cert не нужен). Spec обновлён (раздел filter). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../form-compile/scripts/form-compile.ps1 | 24 ++++++++++++++++--- .../form-compile/scripts/form-compile.py | 22 ++++++++++++++--- docs/form-dsl-spec.md | 1 + 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 57b6f6e7..8fca3352 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -1,4 +1,4 @@ -# form-compile v1.147 — Compile 1C managed form from JSON or object metadata +# form-compile v1.148 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -1675,6 +1675,21 @@ function Parse-FilterShorthand { return $result } +# Значение типа v8:Type (напр. тип «Неопределено» = :Undefined) ссылается на тип +# платформы из namespace http://v8.1c.ru/8.2/data/types — платформа объявляет его ЛОКАЛЬНО +# на теге значения (префикс авто-назначаемый: d6p1/d8p1/dN…). Без объявления QName битый. +# Возвращает строку-атрибут ' xmlns:="…"' (перед xsi:type), либо "". +function Get-ValueTypeNsAttr { + param([string]$valueType, [string]$value) + if ($valueType -eq 'v8:Type' -and "$value" -match '^([A-Za-z]\w*):') { + $pref = $Matches[1] + if ($pref -notin @('xs','cfg','v8','v8ui','ent','dcscor','dcsset','dcssch')) { + return " xmlns:$pref=`"http://v8.1c.ru/8.2/data/types`"" + } + } + return "" +} + function Emit-FilterItem { param($item, [string]$indent) if ($item.group) { @@ -1731,7 +1746,8 @@ function Emit-FilterItem { else { $vt = 'xs:string' } } $vStr = if ($v -is [bool]) { "$v".ToLower() } else { Esc-Xml "$v" } - X "$indent`t$vStr" + $nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$v" + X "$indent`t$vStr" } } } elseif ($null -ne $item.value -and ( @@ -1775,7 +1791,8 @@ function Emit-FilterItem { else { $vt = "xs:string" } } $vStr = if ($item.value -is [bool]) { "$($item.value)".ToLower() } else { Esc-Xml "$($item.value)" } - X "$indent`t$vStr" + $nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$($item.value)" + X "$indent`t$vStr" } if ($item.presentation) { Emit-USPresentation -val $item.presentation -tag "dcsset:presentation" -indent "$indent`t" } if ($item.viewMode) { X "$indent`t$(Esc-Xml "$($item.viewMode)")" } @@ -5164,6 +5181,7 @@ function Emit-DLValue { $valStr = if ($val -is [bool]) { if ($val) { 'true' } else { 'false' } } else { "$val" } if ($type -match '^(date|dateTime|time)') { X "$indent$(Esc-Xml $valStr)" } elseif ($type -eq "boolean") { X "$indent$(Esc-Xml $valStr)" } + elseif ($type -eq 'v8:Type') { $nsAttr = Get-ValueTypeNsAttr -valueType 'v8:Type' -value $valStr; X "$indent$(Esc-Xml $valStr)" } elseif ($type -match '^decimal') { X "$indent$(Esc-Xml $valStr)" } elseif ($type -match '^string') { X "$indent$(Esc-Xml $valStr)" } elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent$(Esc-Xml $valStr)" } diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 5f438f71..a38d0927 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-compile v1.147 — Compile 1C managed form from JSON or object metadata +# form-compile v1.148 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -1425,6 +1425,17 @@ def _value_type_for(v, explicit=None): return 'xs:string' +# Значение типа v8:Type (напр. тип «Неопределено» = :Undefined) ссылается на тип +# платформы из namespace http://v8.1c.ru/8.2/data/types — платформа объявляет его ЛОКАЛЬНО +# на теге значения (префикс авто: d6p1/d8p1/dN…). Без объявления QName битый. +def _value_type_ns_attr(value_type, value): + if value_type == 'v8:Type': + m = re.match(r'^([A-Za-z]\w*):', str(value)) + if m and m.group(1) not in ('xs', 'cfg', 'v8', 'v8ui', 'ent', 'dcscor', 'dcsset', 'dcssch'): + return f' xmlns:{m.group(1)}="http://v8.1c.ru/8.2/data/types"' + return '' + + def emit_filter_item(lines, item, indent): if item.get('group'): g = str(item['group']) @@ -1480,7 +1491,8 @@ def emit_filter_item(lines, item, indent): for v in val: vt = _value_type_for(v, item.get('valueType')) v_str = str(v).lower() if isinstance(v, bool) else esc_xml(str(v)) - lines.append(f'{indent}\t{v_str}') + ns_attr = _value_type_ns_attr(vt, v) + lines.append(f'{indent}\t{v_str}') elif val is not None and ( re.search(r'Standard(Beginning|End)Date$', str(item.get('valueType') or '')) or (not item.get('valueType') and isinstance(val, str) and re.match(r'^\d{4}-\d{2}-\d{2}T', val))): @@ -1507,7 +1519,8 @@ def emit_filter_item(lines, item, indent): elif val is not None: vt = _value_type_for(val, item.get('valueType')) v_str = str(val).lower() if isinstance(val, bool) else esc_xml(str(val)) - lines.append(f'{indent}\t{v_str}') + ns_attr = _value_type_ns_attr(vt, val) + lines.append(f'{indent}\t{v_str}') if item.get('presentation'): emit_us_presentation(lines, f'{indent}\t', 'dcsset:presentation', item['presentation']) if item.get('viewMode'): @@ -4893,6 +4906,9 @@ def emit_dl_value(lines, type_str, val, indent, value_list_allowed=False): lines.append(f'{indent}{esc_xml(val_str)}') elif t == 'boolean': lines.append(f'{indent}{esc_xml(val_str)}') + elif t == 'v8:Type': + ns_attr = _value_type_ns_attr('v8:Type', val_str) + lines.append(f'{indent}{esc_xml(val_str)}') elif re.match(r'^decimal', t): lines.append(f'{indent}{esc_xml(val_str)}') elif re.match(r'^string', t): diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index dd916096..004cbae4 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -1064,6 +1064,7 @@ Shorthand: `"Имя [Заголовок]: тип = Выражение #noField # - **объект** `{ variant, date? }` + `valueType` — полная форма. - **escape** для плоской `xs:dateTime`: явный `valueType: "xs:dateTime"`. Эмитится структурно (``+``). Декомпилятор: Custom+date → голая дата; именованный → строка+valueType. + - **Тип-значение `valueType: "v8:Type"`** (раундтрип; сравнение поля с *типом*, на практике — «Неопределено»): `value` несёт QName типа из namespace платформы (`:Undefined`, префикс авто). Компилятор объявляет `xmlns:="http://v8.1c.ru/8.2/data/types"` локально на теге значения (иначе QName битый). Применимо к `` фильтра (скаляр и массив, op `in`) и к `` параметра дин-списка (`type: "v8:Type"`). - **conditionalAppearance** — объект `{ selection?, filter?, appearance?, presentation?, viewMode?, userSettingID?, use? }`. `appearance` — словарь «параметр: значение» платформы (`ЦветТекста`, `ЦветФона`, `Шрифт` и т.п.). - Значение текстовых параметров (`Текст`/`Заголовок`/`Формат`) ведётся **по форме значения**: голая строка → плоский `xs:string` (нелокализованный литерал; `""` → самозакрывающийся тег); объект `{ru,en}` → локализуемый `LocalStringType`; объект `{field:"путь"}` → ссылка на поле компоновки (`dcscor:Field`). (В отличие от `title`/`tooltip`, где голая строка = `LocalStringType` — здесь это намеренное scoped-различие: платформа хранит обе формы, и их надо различать.)