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,223 @@
1
+ #!/usr/bin/env python3
2
+ """Эмиттер C#. Модель — статический класс `Model`; проект — один csproj с
3
+ `Program.cs` (раннер либо харнесс), `Runtime.cs` и `Model.cs`."""
4
+ import json
5
+ import shutil
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ SUPPORTED_PLAN = "law-backend-plan/0.7"
10
+
11
+
12
+ def check_plan(plan: dict) -> None:
13
+ v = plan.get("planVersion")
14
+ if v != SUPPORTED_PLAN:
15
+ raise ValueError(f"версия плана не поддержана эмиттером: {v!r} (поддержана {SUPPORTED_PLAN})")
16
+
17
+
18
+ def jstr(s: str) -> str:
19
+ return json.dumps(s, ensure_ascii=False)
20
+
21
+
22
+ def key_readers(plan: dict) -> dict[str, list[str]]:
23
+ readers: dict[str, set[str]] = {}
24
+ for rule in plan["rules"]:
25
+ for atom in rule.get("atoms") or []:
26
+ readers.setdefault(atom["pred"], set()).add(rule["head"]["pred"])
27
+ for check in rule.get("checks") or []:
28
+ readers.setdefault(check["pred"], set()).add(rule["head"]["pred"])
29
+ return {source: sorted(consumers) for source, consumers in sorted(readers.items())}
30
+
31
+
32
+ def term(t: dict) -> str:
33
+ k = t["k"]
34
+ if k == "var":
35
+ return f'r{t["row"]}[{t["col"]}]'
36
+ if k == "lit":
37
+ if t["t"] == "money":
38
+ return f'RT.money({jstr(t["v"])}, {jstr(t["cur"])})'
39
+ if t["t"] == "dec":
40
+ return f'RT.dec({jstr(t["v"])})'
41
+ if t["t"] == "date":
42
+ return f'RT.date({jstr(t["v"])})'
43
+ if t["t"] == "enum":
44
+ return f'RT.sym({jstr(t["v"])})'
45
+ return jstr(t["v"])
46
+ if k == "bin":
47
+ return f'RT.{t["op"]}({term(t["l"])}, {term(t["r"])})'
48
+ if k == "call":
49
+ args = t["args"]
50
+ if t["fn"] == "round":
51
+ return f'RT.round({term(args[0])}, {term(args[1])}, {jstr(args[2]["v"])})'
52
+ return f'RT.{t["fn"]}(' + ", ".join(term(a) for a in args) + ')'
53
+ raise AssertionError(k)
54
+
55
+
56
+ def emit(plan: dict) -> str:
57
+ check_plan(plan)
58
+ L = ["public static class Model",
59
+ "{",
60
+ " public static readonly Dictionary<string, List<List<object>>> TABLES = new();",
61
+ " // Объявленные типы параметров: раннер сверяет с ними аргументы дела (§189).",
62
+ " public static readonly Dictionary<string, string[]> PARAMS = new();",
63
+ " // Константы §54: тип для проверки §189 и значение для разыменования аргументов дела.",
64
+ " public static readonly Dictionary<string, (string, object)> CONSTS = new();",
65
+ " // Наследование сущностей: подтип подходит параметру базового типа.",
66
+ " public static readonly Dictionary<string, string> BASES = new();",
67
+ " // Ключи отношений §45.1: позиции столбцов каждого ключа.",
68
+ " public static readonly Dictionary<string, int[][]> KEYS = new();",
69
+ " // E-0210/E-0242: читатели конфликтного отношения в плане.",
70
+ " public static readonly Dictionary<string, string[]> KEY_READERS = new();",
71
+ " static readonly HashSet<string> Seen = new();",
72
+ "",
73
+ " static Model()",
74
+ " {"]
75
+ for t in plan["tables"]:
76
+ L.append(f' TABLES[{jstr(t["pred"])}] = new List<List<object>>();')
77
+ L.append(f' PARAMS[{jstr(t["pred"])}] = new string[] {{ ' + ", ".join(jstr(p) for p in t["params"]) + " };")
78
+ for t in plan["tables"]:
79
+ if t["keys"]:
80
+ L.append(f' KEYS[{jstr(t["pred"])}] = new int[][] {{ ' + ", ".join("new int[] { " + ", ".join(str(i) for i in k) + " }" for k in t["keys"]) + " };")
81
+ for source, consumers in key_readers(plan).items():
82
+ L.append(f' KEY_READERS[{jstr(source)}] = new string[] {{ ' + ", ".join(jstr(value) for value in consumers) + " };")
83
+ for cid, c in plan["consts"].items():
84
+ L.append(f' CONSTS[{jstr(cid)}] = ({jstr(c["type"])}, {term(c["term"])});')
85
+ for sub, base in plan["bases"].items():
86
+ L.append(f' BASES[{jstr(sub)}] = {jstr(base)};')
87
+ L.append(" }")
88
+ L.append("")
89
+ L.append(" // KEY_CONFLICT §45.1/§196: группы строк одной таблицы с одинаковой проекцией на ключ; строки уникальны по ключу строки, поэтому группа из двух и более строк — различные accepted-кортежи. Считается после насыщения.")
90
+ L.append(" public static int KeyConflicts()")
91
+ L.append(" {")
92
+ L.append(" int n = 0;")
93
+ L.append(" foreach (var e in KEYS)")
94
+ L.append(" {")
95
+ L.append(" foreach (var key in e.Value)")
96
+ L.append(" {")
97
+ L.append(" var groups = new Dictionary<string, int>();")
98
+ L.append(" foreach (var row in TABLES[e.Key])")
99
+ L.append(" {")
100
+ L.append(" var p = RT.key(key.Select(i => row[i]).ToList());")
101
+ L.append(" groups[p] = groups.TryGetValue(p, out var c) ? c + 1 : 1;")
102
+ L.append(" }")
103
+ L.append(" n += groups.Values.Count(c => c > 1);")
104
+ L.append(" }")
105
+ L.append(" }")
106
+ L.append(" return n;")
107
+ L.append(" }")
108
+ L.append(" public static HashSet<string> KeyConflictPredicates()")
109
+ L.append(" {")
110
+ L.append(" var outp = new HashSet<string>();")
111
+ L.append(" foreach (var e in KEYS) foreach (var key in e.Value)")
112
+ L.append(" { var groups = new Dictionary<string, int>(); foreach (var row in TABLES[e.Key]) { var p = RT.key(key.Select(i => row[i]).ToList()); groups[p] = groups.TryGetValue(p, out var c) ? c + 1 : 1; } if (groups.Values.Any(c => c > 1)) outp.Add(e.Key); }")
113
+ L.append(" return outp;")
114
+ L.append(" }")
115
+ L.append("")
116
+ L.append(" static bool AddRow(string urn, List<object> row)")
117
+ L.append(" {")
118
+ L.append(' if (!Seen.Add(urn + "#" + RT.key(row))) return false;')
119
+ L.append(" TABLES[urn].Add(row);")
120
+ L.append(" return true;")
121
+ L.append(" }")
122
+ L.append("")
123
+ for r in plan["rules"]:
124
+ L.append(f' // {r["id"]}')
125
+ L.append(f' static bool Rule_{r["name"]}()')
126
+ L.append(" {")
127
+ L.append(" bool grew = false;")
128
+ if r["window"]:
129
+ if r["window"].get("start"):
130
+ L.append(f' if (string.CompareOrdinal(RT.legalTime(), {jstr(r["window"]["start"])}) < 0) return false;')
131
+ if r["window"].get("end"):
132
+ L.append(f' if (string.CompareOrdinal(RT.legalTime(), {jstr(r["window"]["end"])}) >= 0) return false;')
133
+ ind = " "
134
+ for i, a in enumerate(r["atoms"]):
135
+ L.append(f'{ind}foreach (var r{i} in TABLES[{jstr(a["pred"])}].ToList())')
136
+ L.append(f'{ind}{{')
137
+ ind += " "
138
+ for (ai, aj, _at), (bi, bj, _bt) in r["joins"]:
139
+ L.append(f'{ind}if (!RT.cmp(r{ai}[{aj}], "eq", r{bi}[{bj}])) continue;')
140
+ for f in r["filters"]:
141
+ L.append(f'{ind}if (!RT.eq(r{f["row"]}[{f["col"]}], {term(f["term"])})) continue;')
142
+ for g in r["guards"]:
143
+ L.append(f'{ind}if (!RT.cmp({term(g["l"])}, "{g["op"]}", {term(g["r"])})) continue;')
144
+ args = ", ".join(term(a) for a in r["head"]["args"])
145
+ L.append(f'{ind}if (AddRow({jstr(r["head"]["pred"])}, RT.row({args}))) grew = true;')
146
+ for _ in r["atoms"]:
147
+ ind = ind[:-4]
148
+ L.append(ind + "}")
149
+ L.append(" return grew;")
150
+ L.append(" }")
151
+ L.append("")
152
+ names = [r["name"] for r in plan["rules"]]
153
+ L.append(" public static void Saturate()")
154
+ L.append(" {")
155
+ L.append(" bool grew = true;")
156
+ L.append(" while (grew)")
157
+ L.append(" {")
158
+ L.append(" grew = false;")
159
+ for n in names:
160
+ L.append(f" if (Rule_{n}()) grew = true;")
161
+ L.append(" }")
162
+ L.append(" }")
163
+ L.append("")
164
+ L.append(" public static void Register(string urn, List<object> row) => AddRow(urn, row);")
165
+ L.append("")
166
+ L.append(" public static void PackageFacts()")
167
+ L.append(" {")
168
+ for f in plan["facts"]:
169
+ L.append(f' AddRow({jstr(f["pred"])}, RT.row(' + ", ".join(term(a) for a in f["args"]) + "));")
170
+ L.append(" }")
171
+ L.append("}")
172
+ return "\n".join(L) + "\n"
173
+
174
+
175
+ # --- дескриптор цели ---------------------------------------------------------
176
+ HERE = Path(__file__).resolve().parent
177
+ EXT = "cs"
178
+ MODEL = "Model.cs"
179
+ MAINS = {"runner": "Runner.cs", "cert": "Cert.cs"}
180
+ CSPROJ = '''<Project Sdk="Microsoft.NET.Sdk">
181
+ <PropertyGroup>
182
+ <OutputType>Exe</OutputType>
183
+ <TargetFramework>net10.0</TargetFramework>
184
+ <Nullable>enable</Nullable>
185
+ <ImplicitUsings>enable</ImplicitUsings>
186
+ <AssemblyName>app</AssemblyName>
187
+ <RootNamespace>lawgen</RootNamespace>
188
+ <InvariantGlobalization>true</InvariantGlobalization>
189
+ <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
190
+ </PropertyGroup>
191
+ </Project>
192
+ '''
193
+
194
+
195
+ def available():
196
+ dotnet = shutil.which("dotnet")
197
+ return (True, dotnet) if dotnet else (False, "нет dotnet в PATH")
198
+
199
+
200
+ def layout(proj, main: str) -> list:
201
+ proj = Path(proj)
202
+ proj.mkdir(parents=True, exist_ok=True)
203
+ out = []
204
+ shutil.copyfile(HERE / "Runtime.cs", proj / "Runtime.cs")
205
+ shutil.copyfile(HERE / MAINS[main], proj / "Program.cs")
206
+ (proj / "app.csproj").write_text(CSPROJ, encoding="utf-8")
207
+ out += [proj / "Runtime.cs", proj / "Program.cs", proj / "app.csproj"]
208
+ return out
209
+
210
+
211
+ def build(proj):
212
+ return ["dotnet", "build", "-nologo", "-v", "q", "-c", "Release"], str(proj)
213
+
214
+
215
+ def run(proj, main: str):
216
+ return ["dotnet", str(Path(proj) / "bin" / "Release" / "net10.0" / "app.dll")], None
217
+
218
+
219
+ if __name__ == "__main__":
220
+ sys.path.insert(0, str(HERE.parents[1]))
221
+ import plan as P
222
+ ir = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
223
+ Path(sys.argv[2]).write_text(emit(P.build_plan(ir)), encoding="utf-8")
@@ -0,0 +1,99 @@
1
+ // Харнесс сертификации рантайма: исполняет runtime-cert.json и печатает,
2
+ // что получилось. Ожидания сверяют ворота, харнесс их не читает.
3
+ import 'dart:io';
4
+
5
+ import 'json.dart' as J;
6
+ import 'runtime.dart';
7
+
8
+ Object lit(Object? x) {
9
+ if (x is String) return x;
10
+ final m = x as Map<String, Object?>;
11
+ if (m.containsKey('dec')) return RT.dec(m['dec'] as String);
12
+ if (m.containsKey('money')) return RT.money(m['money'] as String, m['cur'] as String);
13
+ if (m.containsKey('text')) return m['text'] as String;
14
+ if (m.containsKey('date')) return RT.date(m['date'] as String);
15
+ if (m.containsKey('sym')) return RT.sym(m['sym'] as String);
16
+ if (m.containsKey('row')) return (m['row'] as List).map(lit).toList();
17
+ throw StateError('литерал фикстуры: $x');
18
+ }
19
+
20
+ // Отображение с ЯВНЫМ масштабом: 2.00 и 2 — разные записи одного значения.
21
+ String digits(BigInt u, int s) {
22
+ final body = u.abs().toString().padLeft(s + 1, '0');
23
+ final text = s == 0 ? body : '${body.substring(0, body.length - s)}.${body.substring(body.length - s)}';
24
+ return (u.isNegative ? '-' : '') + text;
25
+ }
26
+
27
+ String render(Object? v) {
28
+ if (v == null) return 'null';
29
+ if (v is bool) return '$v';
30
+ if (v is String) return J.str(v);
31
+ if (v is Money) return J.str('${digits(v.u, v.s)} ${v.cur}');
32
+ if (v is LDate) return J.str(v.v);
33
+ if (v is Sym) return J.str(v.id);
34
+ final d = v as Dec;
35
+ return J.str(digits(d.u, d.s));
36
+ }
37
+
38
+ Object? run(String op, List<Object> a) {
39
+ switch (op) {
40
+ case 'dec':
41
+ return RT.dec(a[0] as String);
42
+ case 'money':
43
+ return RT.money(a[0] as String, a[1] as String);
44
+ case 'date':
45
+ return RT.date(a[0] as String);
46
+ case 'sym':
47
+ return RT.sym(a[0] as String);
48
+ case 'mul':
49
+ return RT.mul(a[0], a[1]);
50
+ case 'add':
51
+ return RT.add(a[0], a[1]);
52
+ case 'sub':
53
+ return RT.sub(a[0], a[1]);
54
+ case 'div':
55
+ return RT.div(a[0], a[1]);
56
+ case 'mod':
57
+ return RT.mod(a[0], a[1]);
58
+ case 'min':
59
+ return RT.min(a[0], a[1]);
60
+ case 'max':
61
+ return RT.max(a[0], a[1]);
62
+ case 'abs':
63
+ return RT.abs(a[0]);
64
+ case 'pow':
65
+ return RT.pow(a[0], a[1]);
66
+ case 'round':
67
+ return RT.round(a[0], a[1], a[2] as String);
68
+ case 'cmp':
69
+ return RT.cmp(a[0], a[1] as String, a[2]);
70
+ case 'eq':
71
+ return RT.eq(a[0], a[1]);
72
+ case 'keyEq':
73
+ return RT.key((a[0] as List).cast<Object>()) == RT.key((a[1] as List).cast<Object>());
74
+ case 'legalTime':
75
+ return RT.legalTime();
76
+ case 'setLegalTime':
77
+ RT.setLegalTime(a[0] as String);
78
+ return null;
79
+ }
80
+ throw StateError('неизвестная операция фикстуры: $op');
81
+ }
82
+
83
+ void main(List<String> argv) {
84
+ final fixture = J.readJson(argv[0]) as Map<String, Object?>;
85
+ final out = <String>[];
86
+ for (final c in (fixture['cases'] as List).cast<Map<String, Object?>>()) {
87
+ final args = (c['args'] as List).map(lit).toList();
88
+ String got;
89
+ try {
90
+ got = render(run(c['op'] as String, args));
91
+ } on Refused catch (e) {
92
+ got = '{"refused":${J.str(e.message)}}';
93
+ }
94
+ out.add('{"id":${J.str(c['id'] as String)},"got":$got}');
95
+ }
96
+ J.emitLine('[${out.join(',')}]');
97
+ exitCode = 0;
98
+ stdout.flush();
99
+ }
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env python3
2
+ """Эмиттер Dart. Тот же план и тот же контракт рантайма. У Dart нет кода
3
+ верхнего уровня, поэтому собственные факты пакета — функция `packageFacts`,
4
+ которую раннер зовёт до фактов дела."""
5
+ import json
6
+ import shutil
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ # Версия плана, которую этот эмиттер умеет печатать. Другая версия — отказ:
11
+ # незнакомое поле плана печаталось бы молча, а это другой ответ, не меньший.
12
+ SUPPORTED_PLAN = "law-backend-plan/0.7"
13
+
14
+
15
+ def check_plan(plan: dict) -> None:
16
+ v = plan.get("planVersion")
17
+ if v != SUPPORTED_PLAN:
18
+ raise ValueError(f"версия плана не поддержана эмиттером: {v!r} (поддержана {SUPPORTED_PLAN})")
19
+
20
+
21
+ def ds(s: str) -> str:
22
+ """Строковый литерал Dart: экраны JSON Dart понимает, а `$` — интерполяция."""
23
+ return json.dumps(s, ensure_ascii=False).replace("$", "\\$")
24
+
25
+
26
+ def term(t: dict) -> str:
27
+ k = t["k"]
28
+ if k == "var":
29
+ return f'r{t["row"]}[{t["col"]}]'
30
+ if k == "lit":
31
+ if t["t"] == "money":
32
+ return f'RT.money({ds(t["v"])}, {ds(t["cur"])})'
33
+ if t["t"] == "dec":
34
+ return f'RT.dec({ds(t["v"])})'
35
+ if t["t"] == "date":
36
+ return f'RT.date({ds(t["v"])})'
37
+ if t["t"] == "enum":
38
+ return f'RT.sym({ds(t["v"])})'
39
+ return ds(t["v"])
40
+ if k == "bin":
41
+ return f'RT.{t["op"]}({term(t["l"])}, {term(t["r"])})'
42
+ if k == "call":
43
+ args = t["args"]
44
+ if t["fn"] == "round":
45
+ return f'RT.round({term(args[0])}, {term(args[1])}, {ds(args[2]["v"])})'
46
+ return f'RT.{t["fn"]}(' + ", ".join(term(a) for a in args) + ')'
47
+ raise AssertionError(k)
48
+
49
+
50
+ def emit(plan: dict) -> str:
51
+ check_plan(plan)
52
+ L = ["// ignore_for_file: non_constant_identifier_names, unused_local_variable, prefer_const_constructors",
53
+ "import 'runtime.dart';", "",
54
+ "final Map<String, List<List<Object>>> TABLES = {"]
55
+ for t in plan["tables"]:
56
+ L.append(f' {ds(t["pred"])}: <List<Object>>[],')
57
+ L.append("};")
58
+ readers: dict[str, set[str]] = {}
59
+ for rule in plan["rules"]:
60
+ for atom in rule.get("atoms") or []:
61
+ readers.setdefault(atom["pred"], set()).add(rule["head"]["pred"])
62
+ for check in rule.get("checks") or []:
63
+ readers.setdefault(check["pred"], set()).add(rule["head"]["pred"])
64
+ L.append("// E-0210/E-0242: forward readers of a keyed relation.")
65
+ L.append("const Map<String, List<String>> KEY_READERS = {")
66
+ for source, consumers in sorted(readers.items()):
67
+ L.append(f' {ds(source)}: <String>[' + ", ".join(ds(value) for value in sorted(consumers)) + "],")
68
+ L.append("};")
69
+ L.append("// Объявленные типы параметров: раннер сверяет с ними аргументы дела (§189).")
70
+ L.append("const Map<String, List<String>> PARAMS = {")
71
+ for t in plan["tables"]:
72
+ L.append(f' {ds(t["pred"])}: <String>[' + ", ".join(ds(p) for p in t["params"]) + "],")
73
+ L.append("};")
74
+ L.append("// Константы §54: тип для проверки §189 и значение для разыменования аргументов дела.")
75
+ L.append("final Map<String, List<Object>> CONSTS = {")
76
+ for cid, c in plan["consts"].items():
77
+ L.append(f' {ds(cid)}: <Object>[{ds(c["type"])}, {term(c["term"])}],')
78
+ L.append("};")
79
+ L.append("// Наследование сущностей: подтип подходит параметру базового типа.")
80
+ L.append("const Map<String, String> BASES = {")
81
+ for sub, base in plan["bases"].items():
82
+ L.append(f" {ds(sub)}: {ds(base)},")
83
+ L.append("};")
84
+ L.append("// Ключи отношений §45.1: позиции столбцов каждого ключа.")
85
+ L.append("const Map<String, List<List<int>>> KEYS = {")
86
+ for t in plan["tables"]:
87
+ if t["keys"]:
88
+ L.append(f' {ds(t["pred"])}: {json.dumps(t["keys"])},')
89
+ L.append("};")
90
+ L.append("final Map<String, Set<String>> _seen = {};")
91
+ L.append("")
92
+ L.append("bool addRow(String urn, List<Object> row) {")
93
+ L.append(" if (!_seen.putIfAbsent(urn, () => <String>{}).add(RT.key(row))) return false;")
94
+ L.append(" TABLES[urn]!.add(row);")
95
+ L.append(" return true;")
96
+ L.append("}")
97
+ L.append("")
98
+ for r in plan["rules"]:
99
+ L.append(f'// {r["id"]}')
100
+ L.append(f'bool rule_{r["name"]}() {{')
101
+ L.append(" var grew = false;")
102
+ if r["window"]:
103
+ if r["window"].get("start"):
104
+ L.append(f' if (RT.legalTime().compareTo({ds(r["window"]["start"])}) < 0) return false;')
105
+ if r["window"].get("end"):
106
+ L.append(f' if (RT.legalTime().compareTo({ds(r["window"]["end"])}) >= 0) return false;')
107
+ ind = " "
108
+ for i, a in enumerate(r["atoms"]):
109
+ L.append(f'{ind}for (final r{i} in List.of(TABLES[{ds(a["pred"])}]!)) {{')
110
+ ind += " "
111
+ for (ai, aj, _at), (bi, bj, _bt) in r["joins"]:
112
+ L.append(f'{ind}if (!RT.cmp(r{ai}[{aj}], "eq", r{bi}[{bj}])) continue;')
113
+ for f in r["filters"]:
114
+ L.append(f'{ind}if (!RT.eq(r{f["row"]}[{f["col"]}], {term(f["term"])})) continue;')
115
+ for g in r["guards"]:
116
+ L.append(f'{ind}if (!RT.cmp({term(g["l"])}, "{g["op"]}", {term(g["r"])})) continue;')
117
+ args = ", ".join(term(a) for a in r["head"]["args"])
118
+ L.append(f'{ind}if (addRow({ds(r["head"]["pred"])}, <Object>[{args}])) grew = true;')
119
+ for _ in r["atoms"]:
120
+ ind = ind[:-2]
121
+ L.append(ind + "}")
122
+ L.append(" return grew;")
123
+ L.append("}")
124
+ L.append("")
125
+ L.append("void saturate() {")
126
+ L.append(" var grew = true;")
127
+ L.append(" while (grew) {")
128
+ L.append(" grew = false;")
129
+ for r in plan["rules"]:
130
+ L.append(f' if (rule_{r["name"]}()) grew = true;')
131
+ L.append(" }")
132
+ L.append("}")
133
+ L.append("")
134
+ L.append("bool register(String urn, List<Object> row) => addRow(urn, row);")
135
+ L.append("")
136
+ L.append("// KEY_CONFLICT §45.1/§196: группы строк одной таблицы с одинаковой проекцией на ключ; строки уникальны по ключу строки, поэтому группа из двух и более строк — различные accepted-кортежи. Считается после насыщения.")
137
+ L.append("int keyConflicts() {")
138
+ L.append(" var n = 0;")
139
+ L.append(" KEYS.forEach((urn, keys) {")
140
+ L.append(" for (final key in keys) {")
141
+ L.append(" final groups = <String, int>{};")
142
+ L.append(" for (final row in TABLES[urn]!) {")
143
+ L.append(" final g = RT.key([for (final i in key) row[i]]);")
144
+ L.append(" groups[g] = (groups[g] ?? 0) + 1;")
145
+ L.append(" }")
146
+ L.append(" n += groups.values.where((c) => c > 1).length;")
147
+ L.append(" }")
148
+ L.append(" });")
149
+ L.append(" return n;")
150
+ L.append("}")
151
+ L.append("")
152
+ L.append("Set<String> keyConflictPredicates() {")
153
+ L.append(" final out = <String>{};")
154
+ L.append(" KEYS.forEach((urn, keys) {")
155
+ L.append(" for (final key in keys) {")
156
+ L.append(" final groups = <String, int>{};")
157
+ L.append(" for (final row in TABLES[urn]!) { final g = RT.key([for (final i in key) row[i]]); groups[g] = (groups[g] ?? 0) + 1; }")
158
+ L.append(" if (groups.values.any((c) => c > 1)) out.add(urn);")
159
+ L.append(" }")
160
+ L.append(" });")
161
+ L.append(" return out;")
162
+ L.append("}")
163
+ L.append("")
164
+ L.append("// Собственные факты пакета: у Dart нет кода верхнего уровня, раннер зовёт это до фактов дела.")
165
+ L.append("void packageFacts() {")
166
+ for f in plan["facts"]:
167
+ L.append(f' addRow({ds(f["pred"])}, <Object>[' + ", ".join(term(a) for a in f["args"]) + "]);")
168
+ L.append("}")
169
+ return "\n".join(L) + "\n"
170
+
171
+
172
+ # --- дескриптор цели ---------------------------------------------------------
173
+ HERE = Path(__file__).resolve().parent
174
+ EXT = "dart"
175
+ MODEL = "model.dart"
176
+ MAINS = {"runner": "runner.dart", "cert": "cert.dart"}
177
+
178
+
179
+ def available():
180
+ dart = shutil.which("dart")
181
+ return (True, dart) if dart else (False, "нет dart в PATH")
182
+
183
+
184
+ def layout(proj, main: str) -> list:
185
+ proj = Path(proj)
186
+ proj.mkdir(parents=True, exist_ok=True)
187
+ out = []
188
+ for name in ("runtime.dart", "json.dart", MAINS[main]):
189
+ shutil.copyfile(HERE / name, proj / name)
190
+ out.append(proj / name)
191
+ return out
192
+
193
+
194
+ def build(proj):
195
+ return None
196
+
197
+
198
+ def run(proj, main: str):
199
+ return ["dart", "run", str(Path(proj) / MAINS[main])], None
200
+
201
+
202
+ if __name__ == "__main__":
203
+ sys.path.insert(0, str(HERE.parents[1]))
204
+ import plan as P
205
+ ir = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
206
+ Path(sys.argv[2]).write_text(emit(P.build_plan(ir)), encoding="utf-8")