feat(form-compile,form-edit): add type synonym resolution for resilient DSL

Add Resolve-TypeStr/resolve_type_str to form-compile and form-edit (PS+PY)
that silently normalizes Russian type names (Строка→string, Число→decimal,
Булево→boolean, etc.), Number→decimal alias, and Russian reference prefixes
(СправочникСсылка→CatalogRef, etc.). Also accept + as composite type
separator alongside |. This makes form skills more forgiving when the model
uses meta-compile DSL conventions instead of form-specific ones.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-03-05 15:47:15 +03:00
co-authored by Claude Opus 4.6
parent 66eb0b19ec
commit 0a1f8888dc
4 changed files with 167 additions and 5 deletions
@@ -58,6 +58,49 @@ function Emit-MLText {
# --- 5. Type emitter ---
$script:formTypeSynonyms = New-Object System.Collections.Hashtable
$script:formTypeSynonyms["строка"] = "string"
$script:formTypeSynonyms["число"] = "decimal"
$script:formTypeSynonyms["булево"] = "boolean"
$script:formTypeSynonyms["дата"] = "date"
$script:formTypeSynonyms["датавремя"]= "dateTime"
$script:formTypeSynonyms["number"] = "decimal"
$script:formTypeSynonyms["bool"] = "boolean"
$script:formTypeSynonyms["справочникссылка"] = "CatalogRef"
$script:formTypeSynonyms["справочникобъект"] = "CatalogObject"
$script:formTypeSynonyms["документссылка"] = "DocumentRef"
$script:formTypeSynonyms["документобъект"] = "DocumentObject"
$script:formTypeSynonyms["перечислениессылка"] = "EnumRef"
$script:formTypeSynonyms["плансчетовссылка"] = "ChartOfAccountsRef"
$script:formTypeSynonyms["планвидовхарактеристикссылка"] = "ChartOfCharacteristicTypesRef"
$script:formTypeSynonyms["планвидоврасчётассылка"] = "ChartOfCalculationTypesRef"
$script:formTypeSynonyms["планвидоврасчетассылка"] = "ChartOfCalculationTypesRef"
$script:formTypeSynonyms["планобменассылка"] = "ExchangePlanRef"
$script:formTypeSynonyms["бизнеспроцессссылка"] = "BusinessProcessRef"
$script:formTypeSynonyms["задачассылка"] = "TaskRef"
$script:formTypeSynonyms["определяемыйтип"] = "DefinedType"
function Resolve-TypeStr {
param([string]$typeStr)
if (-not $typeStr) { return $typeStr }
if ($typeStr -match '^([^(]+)\((.+)\)$') {
$base = $Matches[1].Trim(); $params = $Matches[2]
$r = $script:formTypeSynonyms[$base.ToLower()]
if ($r) { return "$r($params)" }
return $typeStr
}
if ($typeStr.Contains('.')) {
$i = $typeStr.IndexOf('.')
$prefix = $typeStr.Substring(0, $i); $suffix = $typeStr.Substring($i)
$r = $script:formTypeSynonyms[$prefix.ToLower()]
if ($r) { return "$r$suffix" }
return $typeStr
}
$r = $script:formTypeSynonyms[$typeStr.ToLower()]
if ($r) { return $r }
return $typeStr
}
function Emit-Type {
param($typeStr, [string]$indent)
@@ -68,8 +111,8 @@ function Emit-Type {
$typeString = "$typeStr"
# Composite type: "Type1 | Type2"
$parts = $typeString -split '\s*\|\s*'
# Composite type: "Type1 | Type2" or "Type1 + Type2"
$parts = $typeString -split '\s*[|+]\s*'
X "$indent<Type>"
foreach ($part in $parts) {
@@ -82,6 +125,8 @@ function Emit-Type {
function Emit-SingleType {
param([string]$typeStr, [string]$indent)
$typeStr = Resolve-TypeStr $typeStr
# boolean
if ($typeStr -eq "boolean") {
X "$indent<v8:Type>xs:boolean</v8:Type>"
@@ -208,7 +208,43 @@ CFG_REF_PATTERN = re.compile(
)
_FORM_TYPE_SYNONYMS = {
"строка": "string", "число": "decimal", "булево": "boolean",
"дата": "date", "датавремя": "dateTime",
"number": "decimal", "bool": "boolean",
"справочникссылка": "CatalogRef", "справочникобъект": "CatalogObject",
"документссылка": "DocumentRef", "документобъект": "DocumentObject",
"перечислениессылка": "EnumRef",
"плансчетовссылка": "ChartOfAccountsRef",
"планвидовхарактеристикссылка": "ChartOfCharacteristicTypesRef",
"планвидоврасчётассылка": "ChartOfCalculationTypesRef",
"планвидоврасчетассылка": "ChartOfCalculationTypesRef",
"планобменассылка": "ExchangePlanRef",
"бизнеспроцессссылка": "BusinessProcessRef",
"задачассылка": "TaskRef",
"определяемыйтип": "DefinedType",
}
def resolve_type_str(type_str):
if not type_str:
return type_str
m = re.match(r'^([^(]+)\((.+)\)$', type_str)
if m:
base, params = m.group(1).strip(), m.group(2)
r = _FORM_TYPE_SYNONYMS.get(base.lower())
return f"{r}({params})" if r else type_str
if '.' in type_str:
i = type_str.index('.')
prefix, suffix = type_str[:i], type_str[i:]
r = _FORM_TYPE_SYNONYMS.get(prefix.lower())
return f"{r}{suffix}" if r else type_str
r = _FORM_TYPE_SYNONYMS.get(type_str.lower())
return r if r else type_str
def emit_single_type(lines, type_str, indent):
type_str = resolve_type_str(type_str)
# boolean
if type_str == 'boolean':
lines.append(f'{indent}<v8:Type>xs:boolean</v8:Type>')
@@ -289,7 +325,7 @@ def emit_type(lines, type_str, indent):
return
type_string = str(type_str)
parts = [p.strip() for p in type_string.split('|')]
parts = [p.strip() for p in re.split(r'[|+]', type_string)]
lines.append(f'{indent}<Type>')
for part in parts: