arxo-codegen 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. arxo_codegen/__init__.py +5 -0
  2. arxo_codegen/__main__.py +7 -0
  3. arxo_codegen/active-outcomes.json +6837 -0
  4. arxo_codegen/check.py +158 -0
  5. arxo_codegen/check_active_canaries.py +262 -0
  6. arxo_codegen/check_api.py +76 -0
  7. arxo_codegen/check_conformance.py +414 -0
  8. arxo_codegen/check_matrix.py +131 -0
  9. arxo_codegen/check_packages.py +281 -0
  10. arxo_codegen/check_semantic_probes.py +293 -0
  11. arxo_codegen/check_xlsx.py +320 -0
  12. arxo_codegen/consumer_runner.py +316 -0
  13. arxo_codegen/core_support.py +203 -0
  14. arxo_codegen/coverage.py +341 -0
  15. arxo_codegen/deliver.py +180 -0
  16. arxo_codegen/examples/xlsx/pricing.lawir.json +409 -0
  17. arxo_codegen/examples/xlsx/pricing.xlsx.json +52 -0
  18. arxo_codegen/excel_plan.py +602 -0
  19. arxo_codegen/harness/verify/ci/gates/backends/check_backends.py +1248 -0
  20. arxo_codegen/harness/verify/ci/gates/backends/l1_fixtures.py +463 -0
  21. arxo_codegen/inert-nodes.json +62 -0
  22. arxo_codegen/inert.py +91 -0
  23. arxo_codegen/inputs.py +59 -0
  24. arxo_codegen/package.py +565 -0
  25. arxo_codegen/plan.py +4053 -0
  26. arxo_codegen/recertify_registry.py +100 -0
  27. arxo_codegen/runtime-cert.json +5259 -0
  28. arxo_codegen/runtime_cert.py +233 -0
  29. arxo_codegen/survey.py +272 -0
  30. arxo_codegen/target_runner.py +101 -0
  31. arxo_codegen/targets/bsl/cert.bsl +113 -0
  32. arxo_codegen/targets/bsl/emit.py +356 -0
  33. arxo_codegen/targets/bsl/json.bsl +243 -0
  34. arxo_codegen/targets/bsl/runner.bsl +210 -0
  35. arxo_codegen/targets/bsl/runtime.bsl +858 -0
  36. arxo_codegen/targets/cs/Cert.cs +90 -0
  37. arxo_codegen/targets/cs/Runner.cs +154 -0
  38. arxo_codegen/targets/cs/Runtime.cs +243 -0
  39. arxo_codegen/targets/cs/emit.py +223 -0
  40. arxo_codegen/targets/dart/cert.dart +99 -0
  41. arxo_codegen/targets/dart/emit.py +206 -0
  42. arxo_codegen/targets/dart/json.dart +166 -0
  43. arxo_codegen/targets/dart/runner.dart +154 -0
  44. arxo_codegen/targets/dart/runtime.dart +293 -0
  45. arxo_codegen/targets/erl/cert.erl +64 -0
  46. arxo_codegen/targets/erl/emit.py +216 -0
  47. arxo_codegen/targets/erl/rt.erl +238 -0
  48. arxo_codegen/targets/erl/runner.erl +130 -0
  49. arxo_codegen/targets/ex/cert.exs +62 -0
  50. arxo_codegen/targets/ex/emit.py +204 -0
  51. arxo_codegen/targets/ex/runner.exs +124 -0
  52. arxo_codegen/targets/ex/runtime.ex +227 -0
  53. arxo_codegen/targets/go/apiBase.go +483 -0
  54. arxo_codegen/targets/go/apicheck.go +97 -0
  55. arxo_codegen/targets/go/cert.go +218 -0
  56. arxo_codegen/targets/go/emit.py +1806 -0
  57. arxo_codegen/targets/go/evaluate.go +1826 -0
  58. arxo_codegen/targets/go/runner.go +38 -0
  59. arxo_codegen/targets/go/runtime.go +2612 -0
  60. arxo_codegen/targets/java/Cert.java +91 -0
  61. arxo_codegen/targets/java/Json.java +148 -0
  62. arxo_codegen/targets/java/Runner.java +151 -0
  63. arxo_codegen/targets/java/Runtime.java +243 -0
  64. arxo_codegen/targets/java/emit.py +198 -0
  65. arxo_codegen/targets/kt/Cert.kt +67 -0
  66. arxo_codegen/targets/kt/Json.kt +103 -0
  67. arxo_codegen/targets/kt/Runner.kt +126 -0
  68. arxo_codegen/targets/kt/Runtime.kt +196 -0
  69. arxo_codegen/targets/kt/emit.py +195 -0
  70. arxo_codegen/targets/lua/cert.lua +82 -0
  71. arxo_codegen/targets/lua/emit.py +250 -0
  72. arxo_codegen/targets/lua/json.lua +133 -0
  73. arxo_codegen/targets/lua/runner.lua +167 -0
  74. arxo_codegen/targets/lua/runtime.lua +541 -0
  75. arxo_codegen/targets/php/cert.php +84 -0
  76. arxo_codegen/targets/php/emit.py +229 -0
  77. arxo_codegen/targets/php/json.php +144 -0
  78. arxo_codegen/targets/php/runner.php +189 -0
  79. arxo_codegen/targets/php/runtime.php +660 -0
  80. arxo_codegen/targets/py/api_base.py +334 -0
  81. arxo_codegen/targets/py/apicheck.py +51 -0
  82. arxo_codegen/targets/py/cert.py +140 -0
  83. arxo_codegen/targets/py/emit.py +1140 -0
  84. arxo_codegen/targets/py/evaluate.py +980 -0
  85. arxo_codegen/targets/py/runner.py +38 -0
  86. arxo_codegen/targets/py/runtime.py +1634 -0
  87. arxo_codegen/targets/rb/cert.rb +73 -0
  88. arxo_codegen/targets/rb/emit.py +203 -0
  89. arxo_codegen/targets/rb/runner.rb +130 -0
  90. arxo_codegen/targets/rb/runtime.rb +255 -0
  91. arxo_codegen/targets/rs/Cargo.lock +142 -0
  92. arxo_codegen/targets/rs/Cargo.toml +16 -0
  93. arxo_codegen/targets/rs/cert.rs +106 -0
  94. arxo_codegen/targets/rs/emit.py +233 -0
  95. arxo_codegen/targets/rs/prefetch.rs +1 -0
  96. arxo_codegen/targets/rs/runner.rs +181 -0
  97. arxo_codegen/targets/rs/runtime.rs +448 -0
  98. arxo_codegen/targets/scala/Cert.scala +77 -0
  99. arxo_codegen/targets/scala/Json.scala +140 -0
  100. arxo_codegen/targets/scala/Runner.scala +121 -0
  101. arxo_codegen/targets/scala/Runtime.scala +269 -0
  102. arxo_codegen/targets/scala/emit.py +193 -0
  103. arxo_codegen/targets/swift/cert.swift +105 -0
  104. arxo_codegen/targets/swift/emit.py +231 -0
  105. arxo_codegen/targets/swift/json.swift +208 -0
  106. arxo_codegen/targets/swift/runner.swift +133 -0
  107. arxo_codegen/targets/swift/runtime.swift +528 -0
  108. arxo_codegen/targets/ts/apiBase.ts +250 -0
  109. arxo_codegen/targets/ts/apicheck.ts +37 -0
  110. arxo_codegen/targets/ts/cert.ts +96 -0
  111. arxo_codegen/targets/ts/emit.py +1214 -0
  112. arxo_codegen/targets/ts/evaluate.ts +894 -0
  113. arxo_codegen/targets/ts/package-lock.json +45 -0
  114. arxo_codegen/targets/ts/package.json +9 -0
  115. arxo_codegen/targets/ts/runner.ts +31 -0
  116. arxo_codegen/targets/ts/runtime.ts +1369 -0
  117. arxo_codegen/targets/xlsx/README.ru.md +176 -0
  118. arxo_codegen/targets/xlsx/emit.py +198 -0
  119. arxo_codegen/temporal.py +59 -0
  120. arxo_codegen/test_alias_types.py +171 -0
  121. arxo_codegen/test_api_contract.py +89 -0
  122. arxo_codegen/test_calendar_policy.py +48 -0
  123. arxo_codegen/test_check.py +49 -0
  124. arxo_codegen/test_check_active_canaries.py +96 -0
  125. arxo_codegen/test_check_conformance.py +132 -0
  126. arxo_codegen/test_check_semantic_probes.py +85 -0
  127. arxo_codegen/test_check_xlsx.py +52 -0
  128. arxo_codegen/test_consumer_runner.py +175 -0
  129. arxo_codegen/test_core_support.py +80 -0
  130. arxo_codegen/test_e0212_decision_time.py +63 -0
  131. arxo_codegen/test_head_aggregate.py +71 -0
  132. arxo_codegen/test_inert.py +143 -0
  133. arxo_codegen/test_instant_types.py +113 -0
  134. arxo_codegen/test_key_conflict_scope.py +92 -0
  135. arxo_codegen/test_key_conflict_targets.py +17 -0
  136. arxo_codegen/test_package_layout.py +163 -0
  137. arxo_codegen/test_plan_language_version.py +44 -0
  138. arxo_codegen/test_positions_refusal.py +43 -0
  139. arxo_codegen/test_recertify_registry.py +77 -0
  140. arxo_codegen/test_ruby_target.py +41 -0
  141. arxo_codegen/test_runtime_cert.py +83 -0
  142. arxo_codegen/test_secondary_key_conflict_scope.py +17 -0
  143. arxo_codegen/test_stage_r.py +108 -0
  144. arxo_codegen/test_standalone_inputs.py +71 -0
  145. arxo_codegen/test_support.py +150 -0
  146. arxo_codegen/test_survey_inputs.py +115 -0
  147. arxo_codegen-0.4.0.dist-info/LICENSE +202 -0
  148. arxo_codegen-0.4.0.dist-info/METADATA +1673 -0
  149. arxo_codegen-0.4.0.dist-info/RECORD +152 -0
  150. arxo_codegen-0.4.0.dist-info/WHEEL +5 -0
  151. arxo_codegen-0.4.0.dist-info/entry_points.txt +13 -0
  152. arxo_codegen-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,113 @@
1
+ // Харнесс сертификации рантайма: исполняет runtime-cert.json и печатает, что
2
+ // получилось. Ожидания сверяют ворота, харнесс их не читает.
3
+ Перем РТ;
4
+ Перем J;
5
+
6
+ Функция Лит(Х)
7
+ Если ТипЗнч(Х) = Тип("Строка") Тогда
8
+ Возврат Х;
9
+ ИначеЕсли Х.Получить("dec") <> Неопределено Тогда
10
+ Возврат РТ.op_dec(Х.Получить("dec"));
11
+ ИначеЕсли Х.Получить("money") <> Неопределено Тогда
12
+ Возврат РТ.op_money(Х.Получить("money"), Х.Получить("cur"));
13
+ ИначеЕсли Х.Получить("text") <> Неопределено Тогда
14
+ Возврат Х.Получить("text");
15
+ ИначеЕсли Х.Получить("date") <> Неопределено Тогда
16
+ Возврат РТ.op_date(Х.Получить("date"));
17
+ ИначеЕсли Х.Получить("sym") <> Неопределено Тогда
18
+ Возврат РТ.op_sym(Х.Получить("sym"));
19
+ ИначеЕсли Х.Получить("row") <> Неопределено Тогда
20
+ Ряд = Новый Массив;
21
+ Для Каждого Эл Из Х.Получить("row") Цикл
22
+ Ряд.Добавить(Лит(Эл));
23
+ КонецЦикла;
24
+ Возврат Ряд;
25
+ КонецЕсли;
26
+ ВызватьИсключение "литерал фикстуры";
27
+ КонецФункции
28
+
29
+ // Отображение с ЯВНЫМ масштабом: 2.00 и 2 — разные записи одного значения.
30
+ Функция Цифры(Ц, Масштаб)
31
+ Тело = РТ.ЗаписьЦелого(Ц);
32
+ Отр = Лев(Тело, 1) = "-";
33
+ Если Отр Тогда
34
+ Тело = Сред(Тело, 2);
35
+ КонецЕсли;
36
+ Пока СтрДлина(Тело) < Масштаб + 1 Цикл
37
+ Тело = "0" + Тело;
38
+ КонецЦикла;
39
+ Если Масштаб > 0 Тогда
40
+ Тело = Лев(Тело, СтрДлина(Тело) - Масштаб) + "." + Прав(Тело, Масштаб);
41
+ КонецЕсли;
42
+ Возврат ?(Отр, "-", "") + Тело;
43
+ КонецФункции
44
+
45
+ Функция Отобразить(З)
46
+ В = РТ.ВидЗнач(З);
47
+ Если З = Неопределено Тогда
48
+ Возврат "null";
49
+ ИначеЕсли ТипЗнч(З) = Тип("Булево") Тогда
50
+ Возврат ?(З, "true", "false");
51
+ ИначеЕсли В = "text" Тогда
52
+ Возврат J.ТекстJSON(З);
53
+ ИначеЕсли В = "money" Тогда
54
+ Возврат J.ТекстJSON(Цифры(З.Ц, З.Масштаб) + " " + З.Вал);
55
+ ИначеЕсли В = "date" Тогда
56
+ Возврат J.ТекстJSON(З.Зн);
57
+ ИначеЕсли В = "sym" Тогда
58
+ Возврат J.ТекстJSON(З.Ид);
59
+ КонецЕсли;
60
+ Возврат J.ТекстJSON(Цифры(З.Ц, З.Масштаб));
61
+ КонецФункции
62
+
63
+ Функция Исполнить(Случай)
64
+ А = Новый Массив;
65
+ Для Каждого Х Из Случай.Получить("args") Цикл
66
+ А.Добавить(Лит(Х));
67
+ КонецЦикла;
68
+ Оп = Случай.Получить("op");
69
+ Если Оп = "dec" Тогда Возврат РТ.op_dec(А[0]);
70
+ ИначеЕсли Оп = "money" Тогда Возврат РТ.op_money(А[0], А[1]);
71
+ ИначеЕсли Оп = "date" Тогда Возврат РТ.op_date(А[0]);
72
+ ИначеЕсли Оп = "sym" Тогда Возврат РТ.op_sym(А[0]);
73
+ ИначеЕсли Оп = "mul" Тогда Возврат РТ.op_mul(А[0], А[1]);
74
+ ИначеЕсли Оп = "add" Тогда Возврат РТ.op_add(А[0], А[1]);
75
+ ИначеЕсли Оп = "sub" Тогда Возврат РТ.op_sub(А[0], А[1]);
76
+ ИначеЕсли Оп = "div" Тогда Возврат РТ.op_div(А[0], А[1]);
77
+ ИначеЕсли Оп = "mod" Тогда Возврат РТ.op_mod(А[0], А[1]);
78
+ ИначеЕсли Оп = "min" Тогда Возврат РТ.op_min(А[0], А[1]);
79
+ ИначеЕсли Оп = "max" Тогда Возврат РТ.op_max(А[0], А[1]);
80
+ ИначеЕсли Оп = "abs" Тогда Возврат РТ.op_abs(А[0]);
81
+ ИначеЕсли Оп = "pow" Тогда Возврат РТ.op_pow(А[0], А[1]);
82
+ ИначеЕсли Оп = "round" Тогда Возврат РТ.op_round(А[0], А[1], А[2]);
83
+ ИначеЕсли Оп = "cmp" Тогда Возврат РТ.op_cmp(А[0], А[1], А[2]);
84
+ ИначеЕсли Оп = "eq" Тогда Возврат РТ.op_eq(А[0], А[1]);
85
+ ИначеЕсли Оп = "keyEq" Тогда Возврат РТ.op_key(А[0]) = РТ.op_key(А[1]);
86
+ ИначеЕсли Оп = "legalTime" Тогда Возврат РТ.op_legalTime();
87
+ ИначеЕсли Оп = "setLegalTime" Тогда
88
+ РТ.op_setLegalTime(А[0]);
89
+ Возврат Неопределено;
90
+ КонецЕсли;
91
+ ВызватьИсключение "неизвестная операция фикстуры: " + Оп;
92
+ КонецФункции
93
+
94
+ Каталог = ТекущийСценарий().Каталог;
95
+ ПодключитьСценарий(ОбъединитьПути(Каталог, "json.bsl"), "JsonL0");
96
+ ПодключитьСценарий(ОбъединитьПути(Каталог, "runtime.bsl"), "РантаймL0");
97
+ J = Новый JsonL0();
98
+ РТ = Новый РантаймL0();
99
+ Выход = "";
100
+ Для Каждого Случай Из J.Прочитать(АргументыКоманднойСтроки[0]).Получить("cases") Цикл
101
+ Попытка
102
+ Получено = Отобразить(Исполнить(Случай));
103
+ Исключение
104
+ ТекстОтказа = РТ.ТекстОтказа(ИнформацияОбОшибке().Описание);
105
+ Если ТекстОтказа = Неопределено Тогда
106
+ ВызватьИсключение;
107
+ КонецЕсли;
108
+ Получено = "{""refused"":" + J.ТекстJSON(ТекстОтказа) + "}";
109
+ КонецПопытки;
110
+ Запись = "{""id"":" + J.ТекстJSON(Случай.Получить("id")) + ",""got"":" + Получено + "}";
111
+ Выход = ?(Выход = "", Запись, Выход + "," + Запись);
112
+ КонецЦикла;
113
+ Сообщить("[" + Выход + "]");
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env python3
2
+ """Эмиттер встроенного языка 1С (BSL). Тот же план, другой синтаксис и тот же
3
+ контракт рантайма; исполнитель в воротах — OneScript (`oscript`), без
4
+ лицензии платформы.
5
+
6
+ Модель — класс `МодельL0`, подключаемый раннером через `ПодключитьСценарий`:
7
+ строки таблиц лежат в `Массив` в порядке вставки (`Соответствие` служит только
8
+ поиском по ключу), операции рантайма зовутся с префиксом `op_`. BSL не
9
+ различает регистр и имеет ключевые слова в двух алфавитах, поэтому имена из
10
+ плана манглируются префиксом до латинских букв, цифр и `_` и сверяются на
11
+ уникальность без учёта регистра.
12
+ """
13
+ import json
14
+ import re
15
+ import shutil
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ SUPPORTED_PLAN = "law-backend-plan/0.7"
20
+ # Исходники BSL принято хранить с BOM; на печать раннера он не попадает (проба
21
+ # OneScript 2.2.0: stdout — UTF-8 без BOM).
22
+ BOM = "\ufeff"
23
+
24
+
25
+ def check_plan(plan: dict) -> None:
26
+ v = plan.get("planVersion")
27
+ if v != SUPPORTED_PLAN:
28
+ raise ValueError(f"версия плана не поддержана эмиттером: {v!r} (поддержана {SUPPORTED_PLAN})")
29
+
30
+
31
+ def s(text: str) -> str:
32
+ """Строковый литерал BSL: кавычка удваивается, экранов `\\` в языке нет,
33
+ поэтому управляющие символы идут вызовом `Символ(код)`."""
34
+ parts, buf = [], []
35
+ for ch in text:
36
+ if ord(ch) < 32 or ord(ch) == 127 or ch in "\u2028\u2029":
37
+ if buf:
38
+ parts.append('"' + "".join(buf) + '"')
39
+ buf = []
40
+ parts.append(f"Символ({ord(ch)})")
41
+ else:
42
+ buf.append('""' if ch == '"' else ch)
43
+ if buf or not parts:
44
+ parts.append('"' + "".join(buf) + '"')
45
+ return " + ".join(parts)
46
+
47
+
48
+ def ident(prefix: str, name: str, used: dict) -> str:
49
+ """Имя в порождённом коде: префикс плюс имя плана до [A-Za-z0-9_].
50
+ Совпадение без учёта регистра — внутренняя ошибка эмиттера, не отказ: имя
51
+ плана несёт порядковый номер правила и уникально."""
52
+ out = prefix + re.sub(r"[^A-Za-z0-9_]", "_", name)
53
+ fold = out.casefold()
54
+ if fold in used:
55
+ raise AssertionError(f"эмиттер bsl: имена {used[fold]!r} и {name!r} совпали без учёта регистра")
56
+ used[fold] = name
57
+ return out
58
+
59
+
60
+ def literal(t: dict) -> str:
61
+ if t["t"] == "money":
62
+ return f'РТ.op_money({s(t["v"])}, {s(t["cur"])})'
63
+ if t["t"] == "dec":
64
+ return f'РТ.op_dec({s(t["v"])})'
65
+ if t["t"] == "date":
66
+ return f'РТ.op_date({s(t["v"])})'
67
+ if t["t"] == "enum":
68
+ return f'РТ.op_sym({s(t["v"])})'
69
+ return s(t["v"])
70
+
71
+
72
+ def term(t: dict, lits: dict | None = None) -> str:
73
+ """Выражение терма. С `lits` литерал выносится в локальную переменную
74
+ правила: разбор длинного целого в интерпретаторе дорог, а во внутреннем
75
+ цикле соединения он повторялся на каждой строке. Литерал отказа не даёт и
76
+ после построения не изменяется, поэтому вынос ответа не меняет."""
77
+ k = t["k"]
78
+ if k == "var":
79
+ return f'Стр{t["row"]}[{t["col"]}]'
80
+ if k == "lit":
81
+ code = literal(t)
82
+ if lits is None or t["t"] == "text":
83
+ return code
84
+ return lits.setdefault(code, f"Лит{len(lits)}")
85
+ if k == "bin":
86
+ return f'РТ.op_{t["op"]}({term(t["l"], lits)}, {term(t["r"], lits)})'
87
+ if k == "call":
88
+ return f'РТ.op_{t["fn"]}(' + ", ".join(term(a, lits) for a in t["args"]) + ')'
89
+ raise AssertionError(k)
90
+
91
+
92
+ def row(L: list, ind: str, var: str, args: list, lits: dict | None = None) -> None:
93
+ """Массив значений: литералов массива в BSL нет, аргументы вычисляются по
94
+ порядку, как у остальных целей."""
95
+ L.append(f"{ind}{var} = Новый Массив;")
96
+ for a in args:
97
+ L.append(f"{ind}{var}.Добавить({term(a, lits)});")
98
+
99
+
100
+ def emit(plan: dict) -> str:
101
+ check_plan(plan)
102
+ L = [f"// Порождено targets/bsl/emit.py из плана {SUPPORTED_PLAN}; пакет {plan['package'].get('name')}.",
103
+ "Перем РТ;", "Перем Таблицы;", "Перем Виденные;", "Перем ТаблПараметры;", "Перем ТаблКонстанты;",
104
+ "Перем ТаблБазы;", "Перем Ключи;", "Перем ЧитателиКлючей;", ""]
105
+ L.append("Процедура Начать(Рантайм) Экспорт")
106
+ L.append("\tРТ = Рантайм;")
107
+ L.append("\tТаблицы = Новый Соответствие;")
108
+ L.append("\tВиденные = Новый Соответствие;")
109
+ for t in plan["tables"]:
110
+ L.append(f'\tТаблицы.Вставить({s(t["pred"])}, Новый Массив);')
111
+ L.append("\t// Объявленные типы параметров: раннер сверяет с ними аргументы дела (§189).")
112
+ L.append("\tТаблПараметры = Новый Соответствие;")
113
+ for t in plan["tables"]:
114
+ L.append("\tПар = Новый Массив;")
115
+ for p in t["params"]:
116
+ L.append(f"\tПар.Добавить({s(p)});")
117
+ L.append(f'\tТаблПараметры.Вставить({s(t["pred"])}, Пар);')
118
+ L.append("\t// Константы §54: тип для проверки §189 и значение для разыменования аргументов дела.")
119
+ L.append("\tТаблКонстанты = Новый Соответствие;")
120
+ for cid, c in plan["consts"].items():
121
+ L.append(f'\tТаблКонстанты.Вставить({s(cid)}, Новый Структура("Тип, Значение", {s(c["type"])}, {term(c["term"])}));')
122
+ L.append("\t// Наследование сущностей: подтип подходит параметру базового типа.")
123
+ L.append("\tТаблБазы = Новый Соответствие;")
124
+ for sub, base in plan["bases"].items():
125
+ L.append(f"\tТаблБазы.Вставить({s(sub)}, {s(base)});")
126
+ L.append("\t// Ключи отношений §45.1: позиции столбцов каждого ключа, в порядке плана.")
127
+ L.append("\tКлючи = Новый Массив;")
128
+ for t in plan["tables"]:
129
+ for key in t["keys"]:
130
+ L.append("\tПоз = Новый Массив;")
131
+ for i in key:
132
+ L.append(f"\tПоз.Добавить({int(i)});")
133
+ L.append(f'\tКлючи.Добавить(Новый Структура("Пред, Позиции", {s(t["pred"])}, Поз));')
134
+ readers: dict[str, set[str]] = {}
135
+ for rule in plan["rules"]:
136
+ for atom in rule.get("atoms") or []:
137
+ readers.setdefault(atom["pred"], set()).add(rule["head"]["pred"])
138
+ for check in rule.get("checks") or []:
139
+ readers.setdefault(check["pred"], set()).add(rule["head"]["pred"])
140
+ L.append("\t// E-0210/E-0242: прямые читатели отношения с ключом.")
141
+ L.append("\tЧитателиКлючей = Новый Соответствие;")
142
+ for source, consumers in sorted(readers.items()):
143
+ L.append("\tЧитатели = Новый Массив;")
144
+ for consumer in sorted(consumers):
145
+ L.append(f"\tЧитатели.Добавить({s(consumer)});")
146
+ L.append(f"\tЧитателиКлючей.Вставить({s(source)}, Читатели);")
147
+ L.append("\t// Собственные факты пакета кладутся до фактов дела.")
148
+ for f in plan["facts"]:
149
+ row(L, "\t", "Ряд", f["args"])
150
+ L.append(f'\tДобавитьСтроку({s(f["pred"])}, Ряд);')
151
+ L.append("КонецПроцедуры")
152
+ L.append("")
153
+ L.append("Функция Строки(Пред) Экспорт")
154
+ L.append("\tТ = Таблицы.Получить(Пред);")
155
+ L.append("\tВозврат ?(Т = Неопределено, Новый Массив, Т);")
156
+ L.append("КонецФункции")
157
+ L.append("")
158
+ L.append("Функция Параметры(Пред) Экспорт")
159
+ L.append("\tВозврат ТаблПараметры.Получить(Пред);")
160
+ L.append("КонецФункции")
161
+ L.append("")
162
+ L.append("Функция Константа(Ид) Экспорт")
163
+ L.append("\tВозврат ТаблКонстанты.Получить(Ид);")
164
+ L.append("КонецФункции")
165
+ L.append("")
166
+ L.append("Функция База(ИмяТипа) Экспорт")
167
+ L.append("\tВозврат ТаблБазы.Получить(ИмяТипа);")
168
+ L.append("КонецФункции")
169
+ L.append("")
170
+ L.append("Функция ЧитателиКлюча(Пред) Экспорт")
171
+ L.append("\tВозврат ЧитателиКлючей.Получить(Пред);")
172
+ L.append("КонецФункции")
173
+ L.append("")
174
+ L.append("// Строка уникальна по ключу строки в своей таблице; порядок таблицы — порядок вставки.")
175
+ L.append("Функция ДобавитьСтроку(Пред, Ряд)")
176
+ L.append("\tМн = Виденные.Получить(Пред);")
177
+ L.append("\tЕсли Мн = Неопределено Тогда")
178
+ L.append("\t\tМн = Новый Соответствие;")
179
+ L.append("\t\tВиденные.Вставить(Пред, Мн);")
180
+ L.append("\tКонецЕсли;")
181
+ L.append("\tКл = РТ.op_key(Ряд);")
182
+ L.append("\tЕсли Мн.Получить(Кл) <> Неопределено Тогда")
183
+ L.append("\t\tВозврат Ложь;")
184
+ L.append("\tКонецЕсли;")
185
+ L.append("\tМн.Вставить(Кл, Истина);")
186
+ L.append("\tТаблицы.Получить(Пред).Добавить(Ряд);")
187
+ L.append("\tВозврат Истина;")
188
+ L.append("КонецФункции")
189
+ L.append("")
190
+ L.append("Процедура Зарегистрировать(Пред, Ряд) Экспорт")
191
+ L.append("\tДобавитьСтроку(Пред, Ряд);")
192
+ L.append("КонецПроцедуры")
193
+ L.append("")
194
+ used: dict = {}
195
+ names = []
196
+ for r in plan["rules"]:
197
+ fn = ident("Правило_", r["name"], used)
198
+ names.append(fn)
199
+ L.append(f'// {r["id"]}')
200
+ L.append(f"Функция {fn}()")
201
+ L.append("\tРост = Ложь;")
202
+ if r["window"]:
203
+ if r["window"].get("start"):
204
+ L.append(f'\tЕсли РТ.ПорядокТекста(РТ.op_legalTime(), {s(r["window"]["start"])}) < 0 Тогда')
205
+ L.append("\t\tВозврат Ложь;")
206
+ L.append("\tКонецЕсли;")
207
+ if r["window"].get("end"):
208
+ L.append(f'\tЕсли РТ.ПорядокТекста(РТ.op_legalTime(), {s(r["window"]["end"])}) >= 0 Тогда')
209
+ L.append("\t\tВозврат Ложь;")
210
+ L.append("\tКонецЕсли;")
211
+ lit_at = len(L)
212
+ lits: dict = {}
213
+ # Проверки в порядке плана: соединения, фильтры, гарды. Интерпретатор
214
+ # перебирает декартово произведение атомов на порядки медленнее Python
215
+ # (kz.corpus.bin: пять атомов, 43 с на дело), поэтому ПРЕФИКС проверок,
216
+ # которые отказать не могут, стоит в цикле, где связаны их строки:
217
+ # фильтр — `eq` рантайма, соединение — `eq` не над Money (у денег
218
+ # отказ по валютам). Первая проверка, способная отказать, и всё после
219
+ # неё остаются во внутреннем цикле в прежнем порядке. Отсеянное префиксом
220
+ # сочетание во внутреннем цикле тоже отсеялось бы до первой отказывающей
221
+ # проверки; строки головы добавляются в том же порядке — ответ и отказы
222
+ # те же.
223
+ inner = len(r["atoms"]) - 1
224
+ checks, prefix = [], True
225
+ for (ai, aj, at), (bi, bj, bt) in r["joins"]:
226
+ prefix = prefix and at != "money" and bt != "money"
227
+ checks.append((max(ai, bi) if prefix else inner, f'РТ.op_cmp(Стр{ai}[{aj}], "eq", Стр{bi}[{bj}])'))
228
+ for f in r["filters"]:
229
+ checks.append((f["row"] if prefix else inner, f'РТ.op_eq(Стр{f["row"]}[{f["col"]}], {term(f["term"], lits)})'))
230
+ for g in r["guards"]:
231
+ checks.append((inner, f'РТ.op_cmp({term(g["l"], lits)}, {s(g["op"])}, {term(g["r"], lits)})'))
232
+ ind = "\t"
233
+ for i, a in enumerate(r["atoms"]):
234
+ # Число строк снимается на входе в цикл: добавленные внутри правила строки
235
+ # идут в конец массива и в этот проход не попадают (как `.dup` у Ruby).
236
+ L.append(f'{ind}Т{i} = Таблицы.Получить({s(a["pred"])});')
237
+ L.append(f"{ind}Кол{i} = Т{i}.Количество();")
238
+ L.append(f"{ind}Для Инд{i} = 0 По Кол{i} - 1 Цикл")
239
+ ind += "\t"
240
+ L.append(f"{ind}Стр{i} = Т{i}[Инд{i}];")
241
+ for level, c in checks:
242
+ if level == i:
243
+ L.append(f"{ind}Если Не {c} Тогда")
244
+ L.append(f"{ind}\tПродолжить;")
245
+ L.append(f"{ind}КонецЕсли;")
246
+ row(L, ind, "Гол", r["head"]["args"], lits)
247
+ L.append(f'{ind}Если ДобавитьСтроку({s(r["head"]["pred"])}, Гол) Тогда')
248
+ L.append(f"{ind}\tРост = Истина;")
249
+ L.append(f"{ind}КонецЕсли;")
250
+ for _ in r["atoms"]:
251
+ ind = ind[:-1]
252
+ L.append(f"{ind}КонецЦикла;")
253
+ L[lit_at:lit_at] = [f"\t{var} = {code};" for code, var in lits.items()]
254
+ L.append("\tВозврат Рост;")
255
+ L.append("КонецФункции")
256
+ L.append("")
257
+ L.append("Процедура Насытить() Экспорт")
258
+ L.append("\tРост = Истина;")
259
+ L.append("\tПока Рост Цикл")
260
+ L.append("\t\tРост = Ложь;")
261
+ for fn in names:
262
+ L.append(f"\t\tЕсли {fn}() Тогда")
263
+ L.append("\t\t\tРост = Истина;")
264
+ L.append("\t\tКонецЕсли;")
265
+ L.append("\tКонецЦикла;")
266
+ L.append("КонецПроцедуры")
267
+ L.append("")
268
+ L.append("// KEY_CONFLICT §45.1/§196: группы строк одной таблицы с одинаковой проекцией на ключ; строки уникальны по ключу строки, поэтому группа из двух и более строк — различные accepted-кортежи. Считается после насыщения.")
269
+ L.append("Функция КонфликтыКлючей() Экспорт")
270
+ L.append("\tН = 0;")
271
+ L.append("\tДля Каждого Ключ Из Ключи Цикл")
272
+ L.append("\t\tГруппы = Новый Соответствие;")
273
+ L.append("\t\tДля Каждого Р Из Таблицы.Получить(Ключ.Пред) Цикл")
274
+ L.append("\t\t\tПроекция = Новый Массив;")
275
+ L.append("\t\t\tДля Каждого Поз Из Ключ.Позиции Цикл")
276
+ L.append("\t\t\t\tПроекция.Добавить(Р[Поз]);")
277
+ L.append("\t\t\tКонецЦикла;")
278
+ L.append("\t\t\tКл = РТ.op_key(Проекция);")
279
+ L.append("\t\t\tСчёт = Группы.Получить(Кл);")
280
+ L.append("\t\t\tГруппы.Вставить(Кл, ?(Счёт = Неопределено, 0, Счёт) + 1);")
281
+ L.append("\t\tКонецЦикла;")
282
+ L.append("\t\tДля Каждого Гр Из Группы Цикл")
283
+ L.append("\t\t\tЕсли Гр.Значение > 1 Тогда")
284
+ L.append("\t\t\t\tН = Н + 1;")
285
+ L.append("\t\t\tКонецЕсли;")
286
+ L.append("\t\tКонецЦикла;")
287
+ L.append("\tКонецЦикла;")
288
+ L.append("\tВозврат Н;")
289
+ L.append("КонецФункции")
290
+ L.append("")
291
+ L.append("Функция КонфликтныеПредикатыКлючей() Экспорт")
292
+ L.append("\tОтвет = Новый Массив;")
293
+ L.append("\tВидено = Новый Соответствие;")
294
+ L.append("\tДля Каждого Ключ Из Ключи Цикл")
295
+ L.append("\t\tГруппы = Новый Соответствие;")
296
+ L.append("\t\tДля Каждого Р Из Таблицы.Получить(Ключ.Пред) Цикл")
297
+ L.append("\t\t\tПроекция = Новый Массив;")
298
+ L.append("\t\t\tДля Каждого Поз Из Ключ.Позиции Цикл")
299
+ L.append("\t\t\t\tПроекция.Добавить(Р[Поз]);")
300
+ L.append("\t\t\tКонецЦикла;")
301
+ L.append("\t\t\tКл = РТ.op_key(Проекция);")
302
+ L.append("\t\t\tСчёт = Группы.Получить(Кл);")
303
+ L.append("\t\t\tГруппы.Вставить(Кл, ?(Счёт = Неопределено, 0, Счёт) + 1);")
304
+ L.append("\t\tКонецЦикла;")
305
+ L.append("\t\tЕсть = Ложь;")
306
+ L.append("\t\tДля Каждого Гр Из Группы Цикл")
307
+ L.append("\t\t\tЕсли Гр.Значение > 1 Тогда")
308
+ L.append("\t\t\t\tЕсть = Истина;")
309
+ L.append("\t\t\t\tПрервать;")
310
+ L.append("\t\t\tКонецЕсли;")
311
+ L.append("\t\tКонецЦикла;")
312
+ L.append("\t\tЕсли Есть И Видено.Получить(Ключ.Пред) = Неопределено Тогда")
313
+ L.append("\t\t\tВидено.Вставить(Ключ.Пред, Истина);")
314
+ L.append("\t\t\tОтвет.Добавить(Ключ.Пред);")
315
+ L.append("\t\tКонецЕсли;")
316
+ L.append("\tКонецЦикла;")
317
+ L.append("\tВозврат Ответ;")
318
+ L.append("КонецФункции")
319
+ return BOM + "\n".join(L) + "\n"
320
+
321
+
322
+ # --- дескриптор цели ---------------------------------------------------------
323
+ HERE = Path(__file__).resolve().parent
324
+ EXT = "bsl"
325
+ MODEL = "model.bsl"
326
+ MAINS = {"runner": "runner.bsl", "cert": "cert.bsl"}
327
+
328
+
329
+ def available():
330
+ os_ = shutil.which("oscript")
331
+ return (True, os_) if os_ else (False, "нет oscript в PATH")
332
+
333
+
334
+ def layout(proj, main: str) -> list:
335
+ proj = Path(proj)
336
+ proj.mkdir(parents=True, exist_ok=True)
337
+ out = []
338
+ for name in ("json.bsl", "runtime.bsl", MAINS[main]):
339
+ shutil.copyfile(HERE / name, proj / name)
340
+ out.append(proj / name)
341
+ return out
342
+
343
+
344
+ def build(proj):
345
+ return None
346
+
347
+
348
+ def run(proj, main: str):
349
+ return ["oscript", str(Path(proj) / MAINS[main])], None
350
+
351
+
352
+ if __name__ == "__main__":
353
+ sys.path.insert(0, str(HERE.parents[1]))
354
+ import plan as P
355
+ ir = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
356
+ Path(sys.argv[2]).write_text(emit(P.build_plan(ir)), encoding="utf-8")