feat(meta-compile,meta-decompile): объектная форма табличной части (синоним/подсказка/комментарий ТЧ) (v1.23/v0.8)

Раундтрип-находка: синоним ТЧ кастомный/мультиязычный у 84% (корпус 1728 ТЧ: multi 1032, custom-ru 417,
auto лишь 279), ToolTip у 24% (413). Компилятор хардкодил синоним=Split-CamelCase, Comment/ToolTip пусто.

DSL: значение ТЧ — массив колонок (синоним авто) ЛИБО объект {synonym, tooltip, comment, attributes/columns}
(по образцу реквизита: shorthand vs object). synonym/tooltip — ML.

- meta-compile (дуал-порт): нормализация ТЧ → {columns, synonym, tooltip, comment}; Emit-TabularSection
  параметризован (synonym через Emit-MLText, Comment/ToolTip из DSL).
- meta-decompile: ТЧ → объектная форма при кастомном синониме/подсказке/комментарии, иначе массив.
- spec §5.

Валидация: PS==PY; TS-категории ~0 (item 4566→6, ToolTip 2188→44); −10067 (89687→79620); регресс 37/37;
1С-cert зелёный. Остаток TabularSection>Synonym (~50) — станд. реквизит LineNumber ТЧ (отдельная категория).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-03 15:33:18 +03:00
parent 72d6d47454
commit 03de2dc86d
6 changed files with 220 additions and 29 deletions
@@ -1078,7 +1078,7 @@ function Emit-Attribute {
# --- 9. TabularSection emitter ---
function Emit-TabularSection {
param([string]$indent, [string]$tsName, $columns, [string]$objectType, [string]$objectName)
param([string]$indent, [string]$tsName, $columns, [string]$objectType, [string]$objectName, $tsSynonymArg = $null, $tsTooltip = $null, $tsComment = $null)
$uuid = New-Guid-String
X "$indent<TabularSection uuid=`"$uuid`">"
@@ -1097,13 +1097,13 @@ function Emit-TabularSection {
X "$indent`t`t</xr:GeneratedType>"
X "$indent`t</InternalInfo>"
$tsSynonym = Split-CamelCase $tsName
$tsSynonym = if ($null -ne $tsSynonymArg) { $tsSynonymArg } else { Split-CamelCase $tsName }
X "$indent`t<Properties>"
X "$indent`t`t<Name>$(Esc-Xml $tsName)</Name>"
Emit-MLText "$indent`t`t" "Synonym" $tsSynonym
X "$indent`t`t<Comment/>"
X "$indent`t`t<ToolTip/>"
if ($tsComment) { X "$indent`t`t<Comment>$(Esc-XmlText $tsComment)</Comment>" } else { X "$indent`t`t<Comment/>" }
Emit-MLText "$indent`t`t" "ToolTip" $tsTooltip
X "$indent`t`t<FillChecking>DontCheck</FillChecking>"
Emit-TabularStandardAttributes "$indent`t`t"
# Use=ForItem only for Catalog tabular sections (Document does not have Use)
@@ -2880,17 +2880,19 @@ if ($objType -in $typesWithAttrTS) {
}
$tsSections = [ordered]@{}
if ($def.tabularSections) {
# Normalize array format: [{name:"X", attributes:[...]}, ...] → {"X": [...]}
# Значение ТЧ: массив колонок (синоним авто) ЛИБО объект {attributes/columns, synonym, tooltip, comment}.
# Нормализуем в $tsSections[name] = @{ columns; synonym; tooltip; comment }.
function New-TsEntry { param($val)
if ($val -is [array] -or $val.GetType().Name -eq 'Object[]') {
return @{ columns = @($val); synonym = $null; tooltip = $null; comment = $null }
}
$cols = if ($val.attributes) { @($val.attributes) } elseif ($val.columns) { @($val.columns) } else { @() }
return @{ columns = $cols; synonym = $val.synonym; tooltip = $val.tooltip; comment = if ($val.comment) { "$($val.comment)" } else { $null } }
}
if ($def.tabularSections -is [array] -or $def.tabularSections.GetType().Name -eq "Object[]") {
foreach ($ts in $def.tabularSections) {
$tsName = $ts.name
$tsCols = if ($ts.attributes) { @($ts.attributes) } else { @() }
$tsSections[$tsName] = $tsCols
}
foreach ($ts in $def.tabularSections) { $tsSections[$ts.name] = New-TsEntry $ts }
} else {
$def.tabularSections.PSObject.Properties | ForEach-Object {
$tsSections[$_.Name] = @($_.Value)
}
$def.tabularSections.PSObject.Properties | ForEach-Object { $tsSections[$_.Name] = New-TsEntry $_.Value }
}
}
@@ -2923,8 +2925,8 @@ if ($objType -in $typesWithAttrTS) {
Emit-Attribute "`t`t`t" $a $context
}
foreach ($tsName in $tsSections.Keys) {
$columns = $tsSections[$tsName]
Emit-TabularSection "`t`t`t" $tsName $columns $objType $objName
$tsE = $tsSections[$tsName]
Emit-TabularSection "`t`t`t" $tsName $tsE.columns $objType $objName $tsE.synonym $tsE.tooltip $tsE.comment
}
foreach ($af in $acctFlags) {
$afName = if ($af.name) { $af.name } else { "$af" }
@@ -1066,7 +1066,7 @@ def emit_attribute(indent, parsed, context):
# 9. TabularSection emitter
# ---------------------------------------------------------------------------
def emit_tabular_section(indent, ts_name, columns, object_type, object_name):
def emit_tabular_section(indent, ts_name, columns, object_type, object_name, ts_synonym_arg=None, ts_tooltip=None, ts_comment=None):
uid = new_uuid()
X(f'{indent}<TabularSection uuid="{uid}">')
type_prefix = f'{object_type}TabularSection'
@@ -1081,12 +1081,15 @@ def emit_tabular_section(indent, ts_name, columns, object_type, object_name):
X(f'{indent}\t\t\t<xr:ValueId>{new_uuid()}</xr:ValueId>')
X(f'{indent}\t\t</xr:GeneratedType>')
X(f'{indent}\t</InternalInfo>')
ts_synonym = split_camel_case(ts_name)
ts_synonym = ts_synonym_arg if ts_synonym_arg is not None else split_camel_case(ts_name)
X(f'{indent}\t<Properties>')
X(f'{indent}\t\t<Name>{esc_xml(ts_name)}</Name>')
emit_mltext(f'{indent}\t\t', 'Synonym', ts_synonym)
X(f'{indent}\t\t<Comment/>')
X(f'{indent}\t\t<ToolTip/>')
if ts_comment:
X(f'{indent}\t\t<Comment>{esc_xml_text(ts_comment)}</Comment>')
else:
X(f'{indent}\t\t<Comment/>')
emit_mltext(f'{indent}\t\t', 'ToolTip', ts_tooltip)
X(f'{indent}\t\t<FillChecking>DontCheck</FillChecking>')
emit_tabular_standard_attributes(f'{indent}\t\t')
if object_type == 'Catalog':
@@ -2598,15 +2601,20 @@ if obj_type in types_with_attr_ts:
ts_order = []
if defn.get('tabularSections'):
ts_data = defn['tabularSections']
# Значение ТЧ: массив колонок (синоним авто) ЛИБО объект {attributes/columns, synonym, tooltip, comment}.
def new_ts_entry(val):
if isinstance(val, list):
return {'columns': val, 'synonym': None, 'tooltip': None, 'comment': None}
cols = _as_list(val.get('attributes') or val.get('columns') or [])
return {'columns': cols, 'synonym': val.get('synonym'), 'tooltip': val.get('tooltip'),
'comment': str(val['comment']) if val.get('comment') else None}
if isinstance(ts_data, list):
for ts in ts_data:
ts_name = ts['name']
ts_cols = _as_list(ts.get('attributes', []))
ts_sections[ts_name] = ts_cols
ts_order.append(ts_name)
ts_sections[ts['name']] = new_ts_entry(ts)
ts_order.append(ts['name'])
else:
for k, v in ts_data.items():
ts_sections[k] = _as_list(v)
ts_sections[k] = new_ts_entry(v)
ts_order.append(k)
# ChartOfAccounts: AccountingFlags + ExtDimensionAccountingFlags
acct_flags = []
@@ -2637,8 +2645,8 @@ if obj_type in types_with_attr_ts:
for a in attrs:
emit_attribute('\t\t\t', a, context)
for ts_name in ts_order:
columns = ts_sections[ts_name]
emit_tabular_section('\t\t\t', ts_name, columns, obj_type, obj_name)
e = ts_sections[ts_name]
emit_tabular_section('\t\t\t', ts_name, e['columns'], obj_type, obj_name, e['synonym'], e['tooltip'], e['comment'])
for af in acct_flags:
af_name = af['name'] if isinstance(af, dict) else str(af)
emit_accounting_flag('\t\t\t', af_name)
@@ -1,4 +1,4 @@
# meta-decompile v0.7 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# meta-decompile v0.8 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
#
# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только
@@ -325,7 +325,23 @@ if ($childObjs) {
$tco = $ts.SelectSingleNode('md:ChildObjects', $nsm)
$cols = [System.Collections.ArrayList]@()
if ($tco) { foreach ($ca in @($tco.SelectNodes('md:Attribute', $nsm))) { [void]$cols.Add((Attr-ToDsl $ca)) } }
$tsMap[$tsName] = $cols
# Синоним/подсказка/комментарий ТЧ. Кастом → объектная форма {synonym?, tooltip?, comment?, attributes}.
$tsSyn = Get-MLValue ($tsp.SelectSingleNode('md:Synonym', $nsm))
$tsSynCustom = $false
if ($tsSyn -is [string]) { if ($tsSyn -ne (Split-CamelWords $tsName)) { $tsSynCustom = $true } }
elseif ($null -ne $tsSyn) { $tsSynCustom = $true }
$tsTt = Get-MLValue ($tsp.SelectSingleNode('md:ToolTip', $nsm))
$tsCmtN = $tsp.SelectSingleNode('md:Comment', $nsm); $tsCmt = if ($tsCmtN) { $tsCmtN.InnerText } else { '' }
if ($tsSynCustom -or ($null -ne $tsTt) -or $tsCmt) {
$to = [ordered]@{}
if ($tsSynCustom) { $to['synonym'] = $tsSyn }
if ($null -ne $tsTt) { $to['tooltip'] = $tsTt }
if ($tsCmt) { $to['comment'] = $tsCmt }
$to['attributes'] = $cols
$tsMap[$tsName] = $to
} else {
$tsMap[$tsName] = $cols
}
}
$dsl['tabularSections'] = $tsMap
}
+13 -1
View File
@@ -213,7 +213,19 @@ JSON DSL для описания объектов метаданных конф
}
```
Ключ — имя табличной части, значение — массив реквизитов (в строковой или объектной форме).
Ключ — имя табличной части, значение — **массив реквизитов** (в строковой или объектной форме; синоним ТЧ авто из имени) ЛИБО **объект** с собственными свойствами ТЧ:
```json
"tabularSections": {
"Представления": {
"synonym": { "ru": "Представления", "en": "Presentations" },
"tooltip": "Локализованные представления",
"attributes": [ "КодЯзыка: String(10) | index", "Наименование: String(150)" ]
}
}
```
Свойства объектной формы ТЧ: `synonym` (ML; нет ключа → авто из имени), `tooltip` (ML), `comment` (строка), `attributes` (колонки; синоним `columns`).
Для Catalog добавляется `<Use>ForItem</Use>` в Properties табличной части. Для Document Use не применяется.
@@ -12,6 +12,13 @@
"attributes": [
{ "name": "Штрихкод", "type": "String", "length": 128 }
]
},
{
"name": "Характеристики",
"synonym": { "ru": "Характеристики", "en": "Features" },
"tooltip": "Дополнительные характеристики товара",
"comment": "ТЧ характеристик",
"attributes": [ "Свойство: String(100)", "Значение: String(200)" ]
}
]
},
@@ -227,6 +227,152 @@
</Attribute>
</ChildObjects>
</TabularSection>
<TabularSection uuid="UUID-019">
<InternalInfo>
<xr:GeneratedType name="CatalogTabularSection.Товары.Характеристики" category="TabularSection">
<xr:TypeId>UUID-020</xr:TypeId>
<xr:ValueId>UUID-021</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="CatalogTabularSectionRow.Товары.Характеристики" category="TabularSectionRow">
<xr:TypeId>UUID-022</xr:TypeId>
<xr:ValueId>UUID-023</xr:ValueId>
</xr:GeneratedType>
</InternalInfo>
<Properties>
<Name>Характеристики</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Характеристики</v8:content>
</v8:item>
<v8:item>
<v8:lang>en</v8:lang>
<v8:content>Features</v8:content>
</v8:item>
</Synonym>
<Comment>ТЧ характеристик</Comment>
<ToolTip>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Дополнительные характеристики товара</v8:content>
</v8:item>
</ToolTip>
<FillChecking>DontCheck</FillChecking>
<StandardAttributes>
<xr:StandardAttribute name="LineNumber">
<xr:LinkByType/>
<xr:FillChecking>DontCheck</xr:FillChecking>
<xr:MultiLine>false</xr:MultiLine>
<xr:FillFromFillingValue>false</xr:FillFromFillingValue>
<xr:CreateOnInput>Auto</xr:CreateOnInput>
<xr:MaxValue xsi:nil="true"/>
<xr:ToolTip/>
<xr:ExtendedEdit>false</xr:ExtendedEdit>
<xr:Format/>
<xr:ChoiceForm/>
<xr:QuickChoice>Auto</xr:QuickChoice>
<xr:ChoiceHistoryOnInput>Auto</xr:ChoiceHistoryOnInput>
<xr:EditFormat/>
<xr:PasswordMode>false</xr:PasswordMode>
<xr:DataHistory>Use</xr:DataHistory>
<xr:MarkNegatives>false</xr:MarkNegatives>
<xr:MinValue xsi:nil="true"/>
<xr:Synonym/>
<xr:Comment/>
<xr:FullTextSearch>Use</xr:FullTextSearch>
<xr:ChoiceParameterLinks/>
<xr:FillValue xsi:nil="true"/>
<xr:Mask/>
<xr:ChoiceParameters/>
</xr:StandardAttribute>
</StandardAttributes>
<Use>ForItem</Use>
</Properties>
<ChildObjects>
<Attribute uuid="UUID-024">
<Properties>
<Name>Свойство</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Свойство</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>100</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<PasswordMode>false</PasswordMode>
<Format/>
<EditFormat/>
<ToolTip/>
<MarkNegatives>false</MarkNegatives>
<Mask/>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<MinValue xsi:nil="true"/>
<MaxValue xsi:nil="true"/>
<FillChecking>DontCheck</FillChecking>
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
<ChoiceParameterLinks/>
<ChoiceParameters/>
<QuickChoice>Auto</QuickChoice>
<CreateOnInput>Auto</CreateOnInput>
<ChoiceForm/>
<LinkByType/>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<Indexing>DontIndex</Indexing>
<FullTextSearch>Use</FullTextSearch>
<DataHistory>Use</DataHistory>
</Properties>
</Attribute>
<Attribute uuid="UUID-025">
<Properties>
<Name>Значение</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Значение</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>200</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<PasswordMode>false</PasswordMode>
<Format/>
<EditFormat/>
<ToolTip/>
<MarkNegatives>false</MarkNegatives>
<Mask/>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<MinValue xsi:nil="true"/>
<MaxValue xsi:nil="true"/>
<FillChecking>DontCheck</FillChecking>
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
<ChoiceParameterLinks/>
<ChoiceParameters/>
<QuickChoice>Auto</QuickChoice>
<CreateOnInput>Auto</CreateOnInput>
<ChoiceForm/>
<LinkByType/>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<Indexing>DontIndex</Indexing>
<FullTextSearch>Use</FullTextSearch>
<DataHistory>Use</DataHistory>
</Properties>
</Attribute>
</ChildObjects>
</TabularSection>
</ChildObjects>
</Catalog>
</MetaDataObject>