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.
- arxo_codegen/__init__.py +5 -0
- arxo_codegen/__main__.py +7 -0
- arxo_codegen/active-outcomes.json +6837 -0
- arxo_codegen/check.py +158 -0
- arxo_codegen/check_active_canaries.py +262 -0
- arxo_codegen/check_api.py +76 -0
- arxo_codegen/check_conformance.py +414 -0
- arxo_codegen/check_matrix.py +131 -0
- arxo_codegen/check_packages.py +281 -0
- arxo_codegen/check_semantic_probes.py +293 -0
- arxo_codegen/check_xlsx.py +320 -0
- arxo_codegen/consumer_runner.py +316 -0
- arxo_codegen/core_support.py +203 -0
- arxo_codegen/coverage.py +341 -0
- arxo_codegen/deliver.py +180 -0
- arxo_codegen/examples/xlsx/pricing.lawir.json +409 -0
- arxo_codegen/examples/xlsx/pricing.xlsx.json +52 -0
- arxo_codegen/excel_plan.py +602 -0
- arxo_codegen/harness/verify/ci/gates/backends/check_backends.py +1248 -0
- arxo_codegen/harness/verify/ci/gates/backends/l1_fixtures.py +463 -0
- arxo_codegen/inert-nodes.json +62 -0
- arxo_codegen/inert.py +91 -0
- arxo_codegen/inputs.py +59 -0
- arxo_codegen/package.py +565 -0
- arxo_codegen/plan.py +4053 -0
- arxo_codegen/recertify_registry.py +100 -0
- arxo_codegen/runtime-cert.json +5259 -0
- arxo_codegen/runtime_cert.py +233 -0
- arxo_codegen/survey.py +272 -0
- arxo_codegen/target_runner.py +101 -0
- arxo_codegen/targets/bsl/cert.bsl +113 -0
- arxo_codegen/targets/bsl/emit.py +356 -0
- arxo_codegen/targets/bsl/json.bsl +243 -0
- arxo_codegen/targets/bsl/runner.bsl +210 -0
- arxo_codegen/targets/bsl/runtime.bsl +858 -0
- arxo_codegen/targets/cs/Cert.cs +90 -0
- arxo_codegen/targets/cs/Runner.cs +154 -0
- arxo_codegen/targets/cs/Runtime.cs +243 -0
- arxo_codegen/targets/cs/emit.py +223 -0
- arxo_codegen/targets/dart/cert.dart +99 -0
- arxo_codegen/targets/dart/emit.py +206 -0
- arxo_codegen/targets/dart/json.dart +166 -0
- arxo_codegen/targets/dart/runner.dart +154 -0
- arxo_codegen/targets/dart/runtime.dart +293 -0
- arxo_codegen/targets/erl/cert.erl +64 -0
- arxo_codegen/targets/erl/emit.py +216 -0
- arxo_codegen/targets/erl/rt.erl +238 -0
- arxo_codegen/targets/erl/runner.erl +130 -0
- arxo_codegen/targets/ex/cert.exs +62 -0
- arxo_codegen/targets/ex/emit.py +204 -0
- arxo_codegen/targets/ex/runner.exs +124 -0
- arxo_codegen/targets/ex/runtime.ex +227 -0
- arxo_codegen/targets/go/apiBase.go +483 -0
- arxo_codegen/targets/go/apicheck.go +97 -0
- arxo_codegen/targets/go/cert.go +218 -0
- arxo_codegen/targets/go/emit.py +1806 -0
- arxo_codegen/targets/go/evaluate.go +1826 -0
- arxo_codegen/targets/go/runner.go +38 -0
- arxo_codegen/targets/go/runtime.go +2612 -0
- arxo_codegen/targets/java/Cert.java +91 -0
- arxo_codegen/targets/java/Json.java +148 -0
- arxo_codegen/targets/java/Runner.java +151 -0
- arxo_codegen/targets/java/Runtime.java +243 -0
- arxo_codegen/targets/java/emit.py +198 -0
- arxo_codegen/targets/kt/Cert.kt +67 -0
- arxo_codegen/targets/kt/Json.kt +103 -0
- arxo_codegen/targets/kt/Runner.kt +126 -0
- arxo_codegen/targets/kt/Runtime.kt +196 -0
- arxo_codegen/targets/kt/emit.py +195 -0
- arxo_codegen/targets/lua/cert.lua +82 -0
- arxo_codegen/targets/lua/emit.py +250 -0
- arxo_codegen/targets/lua/json.lua +133 -0
- arxo_codegen/targets/lua/runner.lua +167 -0
- arxo_codegen/targets/lua/runtime.lua +541 -0
- arxo_codegen/targets/php/cert.php +84 -0
- arxo_codegen/targets/php/emit.py +229 -0
- arxo_codegen/targets/php/json.php +144 -0
- arxo_codegen/targets/php/runner.php +189 -0
- arxo_codegen/targets/php/runtime.php +660 -0
- arxo_codegen/targets/py/api_base.py +334 -0
- arxo_codegen/targets/py/apicheck.py +51 -0
- arxo_codegen/targets/py/cert.py +140 -0
- arxo_codegen/targets/py/emit.py +1140 -0
- arxo_codegen/targets/py/evaluate.py +980 -0
- arxo_codegen/targets/py/runner.py +38 -0
- arxo_codegen/targets/py/runtime.py +1634 -0
- arxo_codegen/targets/rb/cert.rb +73 -0
- arxo_codegen/targets/rb/emit.py +203 -0
- arxo_codegen/targets/rb/runner.rb +130 -0
- arxo_codegen/targets/rb/runtime.rb +255 -0
- arxo_codegen/targets/rs/Cargo.lock +142 -0
- arxo_codegen/targets/rs/Cargo.toml +16 -0
- arxo_codegen/targets/rs/cert.rs +106 -0
- arxo_codegen/targets/rs/emit.py +233 -0
- arxo_codegen/targets/rs/prefetch.rs +1 -0
- arxo_codegen/targets/rs/runner.rs +181 -0
- arxo_codegen/targets/rs/runtime.rs +448 -0
- arxo_codegen/targets/scala/Cert.scala +77 -0
- arxo_codegen/targets/scala/Json.scala +140 -0
- arxo_codegen/targets/scala/Runner.scala +121 -0
- arxo_codegen/targets/scala/Runtime.scala +269 -0
- arxo_codegen/targets/scala/emit.py +193 -0
- arxo_codegen/targets/swift/cert.swift +105 -0
- arxo_codegen/targets/swift/emit.py +231 -0
- arxo_codegen/targets/swift/json.swift +208 -0
- arxo_codegen/targets/swift/runner.swift +133 -0
- arxo_codegen/targets/swift/runtime.swift +528 -0
- arxo_codegen/targets/ts/apiBase.ts +250 -0
- arxo_codegen/targets/ts/apicheck.ts +37 -0
- arxo_codegen/targets/ts/cert.ts +96 -0
- arxo_codegen/targets/ts/emit.py +1214 -0
- arxo_codegen/targets/ts/evaluate.ts +894 -0
- arxo_codegen/targets/ts/package-lock.json +45 -0
- arxo_codegen/targets/ts/package.json +9 -0
- arxo_codegen/targets/ts/runner.ts +31 -0
- arxo_codegen/targets/ts/runtime.ts +1369 -0
- arxo_codegen/targets/xlsx/README.ru.md +176 -0
- arxo_codegen/targets/xlsx/emit.py +198 -0
- arxo_codegen/temporal.py +59 -0
- arxo_codegen/test_alias_types.py +171 -0
- arxo_codegen/test_api_contract.py +89 -0
- arxo_codegen/test_calendar_policy.py +48 -0
- arxo_codegen/test_check.py +49 -0
- arxo_codegen/test_check_active_canaries.py +96 -0
- arxo_codegen/test_check_conformance.py +132 -0
- arxo_codegen/test_check_semantic_probes.py +85 -0
- arxo_codegen/test_check_xlsx.py +52 -0
- arxo_codegen/test_consumer_runner.py +175 -0
- arxo_codegen/test_core_support.py +80 -0
- arxo_codegen/test_e0212_decision_time.py +63 -0
- arxo_codegen/test_head_aggregate.py +71 -0
- arxo_codegen/test_inert.py +143 -0
- arxo_codegen/test_instant_types.py +113 -0
- arxo_codegen/test_key_conflict_scope.py +92 -0
- arxo_codegen/test_key_conflict_targets.py +17 -0
- arxo_codegen/test_package_layout.py +163 -0
- arxo_codegen/test_plan_language_version.py +44 -0
- arxo_codegen/test_positions_refusal.py +43 -0
- arxo_codegen/test_recertify_registry.py +77 -0
- arxo_codegen/test_ruby_target.py +41 -0
- arxo_codegen/test_runtime_cert.py +83 -0
- arxo_codegen/test_secondary_key_conflict_scope.py +17 -0
- arxo_codegen/test_stage_r.py +108 -0
- arxo_codegen/test_standalone_inputs.py +71 -0
- arxo_codegen/test_support.py +150 -0
- arxo_codegen/test_survey_inputs.py +115 -0
- arxo_codegen-0.4.0.dist-info/LICENSE +202 -0
- arxo_codegen-0.4.0.dist-info/METADATA +1673 -0
- arxo_codegen-0.4.0.dist-info/RECORD +152 -0
- arxo_codegen-0.4.0.dist-info/WHEEL +5 -0
- arxo_codegen-0.4.0.dist-info/entry_points.txt +13 -0
- arxo_codegen-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Харнесс сертификации рантайма: исполняет runtime-cert.json и печатает,
|
|
2
|
+
// что получилось. Ожидания сверяют ворота, харнесс их не читает.
|
|
3
|
+
import java.math.BigInteger;
|
|
4
|
+
import java.nio.charset.StandardCharsets;
|
|
5
|
+
import java.nio.file.Files;
|
|
6
|
+
import java.nio.file.Path;
|
|
7
|
+
import java.util.ArrayList;
|
|
8
|
+
import java.util.LinkedHashMap;
|
|
9
|
+
import java.util.List;
|
|
10
|
+
import java.util.Map;
|
|
11
|
+
|
|
12
|
+
public final class Cert {
|
|
13
|
+
static Object lit(Object x) {
|
|
14
|
+
if (x instanceof String) return x;
|
|
15
|
+
Map<String, Object> m = Json.obj(x);
|
|
16
|
+
if (m.containsKey("dec")) return Runtime.dec(Json.str(m.get("dec")));
|
|
17
|
+
if (m.containsKey("money")) return Runtime.money(Json.str(m.get("money")), Json.str(m.get("cur")));
|
|
18
|
+
if (m.containsKey("text")) return Json.str(m.get("text"));
|
|
19
|
+
if (m.containsKey("date")) return Runtime.date(Json.str(m.get("date")));
|
|
20
|
+
if (m.containsKey("sym")) return Runtime.sym(Json.str(m.get("sym")));
|
|
21
|
+
if (m.containsKey("row")) { List<Object> r = new ArrayList<>(); for (Object v : Json.arr(m.get("row"))) r.add(lit(v)); return r; }
|
|
22
|
+
throw new IllegalArgumentException("литерал фикстуры: " + x);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Отображение с ЯВНЫМ масштабом: 2.00 и 2 — разные записи одного значения.
|
|
26
|
+
static String digits(BigInteger u, int s) {
|
|
27
|
+
boolean neg = u.signum() < 0;
|
|
28
|
+
StringBuilder body = new StringBuilder(u.abs().toString());
|
|
29
|
+
while (body.length() < s + 1) body.insert(0, '0');
|
|
30
|
+
String text = s == 0 ? body.toString() : body.substring(0, body.length() - s) + "." + body.substring(body.length() - s);
|
|
31
|
+
return (neg ? "-" : "") + text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static Object render(Object v) {
|
|
35
|
+
if (v == null || v instanceof Boolean || v instanceof String) return v;
|
|
36
|
+
if (v instanceof Runtime.Money) { Runtime.Money m = (Runtime.Money) v; return digits(m.u, m.s) + " " + m.cur; }
|
|
37
|
+
if (v instanceof Runtime.Date) return ((Runtime.Date) v).v;
|
|
38
|
+
if (v instanceof Runtime.Sym) return ((Runtime.Sym) v).id;
|
|
39
|
+
Runtime.Dec d = (Runtime.Dec) v;
|
|
40
|
+
return digits(d.u, d.s);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@SuppressWarnings("unchecked")
|
|
44
|
+
static Object run(Map<String, Object> c) {
|
|
45
|
+
List<Object> a = new ArrayList<>();
|
|
46
|
+
for (Object x : Json.arr(c.get("args"))) a.add(lit(x));
|
|
47
|
+
switch (Json.str(c.get("op"))) {
|
|
48
|
+
case "dec": return Runtime.dec((String) a.get(0));
|
|
49
|
+
case "money": return Runtime.money((String) a.get(0), (String) a.get(1));
|
|
50
|
+
case "date": return Runtime.date((String) a.get(0));
|
|
51
|
+
case "sym": return Runtime.sym((String) a.get(0));
|
|
52
|
+
case "add": return Runtime.add(a.get(0), a.get(1));
|
|
53
|
+
case "sub": return Runtime.sub(a.get(0), a.get(1));
|
|
54
|
+
case "mul": return Runtime.mul(a.get(0), a.get(1));
|
|
55
|
+
case "div": return Runtime.div(a.get(0), a.get(1));
|
|
56
|
+
case "mod": return Runtime.mod(a.get(0), a.get(1));
|
|
57
|
+
case "min": return Runtime.min(a.get(0), a.get(1));
|
|
58
|
+
case "max": return Runtime.max(a.get(0), a.get(1));
|
|
59
|
+
case "abs": return Runtime.abs(a.get(0));
|
|
60
|
+
case "pow": return Runtime.pow(a.get(0), a.get(1));
|
|
61
|
+
case "round": return Runtime.round(a.get(0), a.get(1), (String) a.get(2));
|
|
62
|
+
case "cmp": return Runtime.cmp(a.get(0), (String) a.get(1), a.get(2));
|
|
63
|
+
case "eq": return Runtime.eq(a.get(0), a.get(1));
|
|
64
|
+
case "keyEq": return Runtime.key((List<Object>) a.get(0)).equals(Runtime.key((List<Object>) a.get(1)));
|
|
65
|
+
case "legalTime": return Runtime.legalTime();
|
|
66
|
+
case "setLegalTime": Runtime.setLegalTime((String) a.get(0)); return null;
|
|
67
|
+
default: throw new IllegalArgumentException("неизвестная операция фикстуры: " + c.get("op"));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public static void main(String[] argv) throws Exception {
|
|
72
|
+
Map<String, Object> fixture = Json.obj(Json.parse(Files.readString(Path.of(argv[0]), StandardCharsets.UTF_8)));
|
|
73
|
+
List<Object> out = new ArrayList<>();
|
|
74
|
+
for (Object c : Json.arr(fixture.get("cases"))) {
|
|
75
|
+
Map<String, Object> cm = Json.obj(c);
|
|
76
|
+
Object got;
|
|
77
|
+
try {
|
|
78
|
+
got = render(run(cm));
|
|
79
|
+
} catch (Runtime.Refused e) {
|
|
80
|
+
Map<String, Object> r = new LinkedHashMap<>();
|
|
81
|
+
r.put("refused", e.getMessage());
|
|
82
|
+
got = r;
|
|
83
|
+
}
|
|
84
|
+
Map<String, Object> row = new LinkedHashMap<>();
|
|
85
|
+
row.put("id", cm.get("id"));
|
|
86
|
+
row.put("got", got);
|
|
87
|
+
out.add(row);
|
|
88
|
+
}
|
|
89
|
+
System.out.println(Json.write(out));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Минимальный JSON для раннера и харнесса: в стандартной библиотеке Java его
|
|
2
|
+
// нет. Числа сохраняются ТЕКСТОМ (класс Num) — значение Integer дела не
|
|
3
|
+
// проходит через double. Объекты — LinkedHashMap в порядке документа.
|
|
4
|
+
import java.util.ArrayList;
|
|
5
|
+
import java.util.LinkedHashMap;
|
|
6
|
+
import java.util.List;
|
|
7
|
+
import java.util.Map;
|
|
8
|
+
|
|
9
|
+
public final class Json {
|
|
10
|
+
|
|
11
|
+
public static final class Num {
|
|
12
|
+
public final String text;
|
|
13
|
+
public Num(String t) { text = t; }
|
|
14
|
+
@Override public String toString() { return text; }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private final String s;
|
|
18
|
+
private int i = 0;
|
|
19
|
+
private Json(String s) { this.s = s; }
|
|
20
|
+
|
|
21
|
+
public static Object parse(String text) {
|
|
22
|
+
Json p = new Json(text);
|
|
23
|
+
Object v = p.value();
|
|
24
|
+
p.ws();
|
|
25
|
+
if (p.i != p.s.length()) throw new IllegalArgumentException("JSON: хвост после значения");
|
|
26
|
+
return v;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private void ws() { while (i < s.length() && " \t\r\n".indexOf(s.charAt(i)) >= 0) i++; }
|
|
30
|
+
|
|
31
|
+
private Object value() {
|
|
32
|
+
ws();
|
|
33
|
+
char c = s.charAt(i);
|
|
34
|
+
if (c == '{') return object();
|
|
35
|
+
if (c == '[') return array();
|
|
36
|
+
if (c == '"') return string();
|
|
37
|
+
if (s.startsWith("true", i)) { i += 4; return Boolean.TRUE; }
|
|
38
|
+
if (s.startsWith("false", i)) { i += 5; return Boolean.FALSE; }
|
|
39
|
+
if (s.startsWith("null", i)) { i += 4; return null; }
|
|
40
|
+
int start = i;
|
|
41
|
+
while (i < s.length() && "+-0123456789.eE".indexOf(s.charAt(i)) >= 0) i++;
|
|
42
|
+
if (start == i) throw new IllegalArgumentException("JSON: неожиданный символ " + c);
|
|
43
|
+
return new Num(s.substring(start, i));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private Map<String, Object> object() {
|
|
47
|
+
Map<String, Object> m = new LinkedHashMap<>();
|
|
48
|
+
i++;
|
|
49
|
+
ws();
|
|
50
|
+
if (s.charAt(i) == '}') { i++; return m; }
|
|
51
|
+
while (true) {
|
|
52
|
+
ws();
|
|
53
|
+
String k = string();
|
|
54
|
+
ws();
|
|
55
|
+
if (s.charAt(i) != ':') throw new IllegalArgumentException("JSON: ожидалось ':'");
|
|
56
|
+
i++;
|
|
57
|
+
m.put(k, value());
|
|
58
|
+
ws();
|
|
59
|
+
char c = s.charAt(i++);
|
|
60
|
+
if (c == '}') return m;
|
|
61
|
+
if (c != ',') throw new IllegalArgumentException("JSON: ожидалась ','");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private List<Object> array() {
|
|
66
|
+
List<Object> a = new ArrayList<>();
|
|
67
|
+
i++;
|
|
68
|
+
ws();
|
|
69
|
+
if (s.charAt(i) == ']') { i++; return a; }
|
|
70
|
+
while (true) {
|
|
71
|
+
a.add(value());
|
|
72
|
+
ws();
|
|
73
|
+
char c = s.charAt(i++);
|
|
74
|
+
if (c == ']') return a;
|
|
75
|
+
if (c != ',') throw new IllegalArgumentException("JSON: ожидалась ','");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private String string() {
|
|
80
|
+
if (s.charAt(i) != '"') throw new IllegalArgumentException("JSON: ожидалась строка");
|
|
81
|
+
i++;
|
|
82
|
+
StringBuilder b = new StringBuilder();
|
|
83
|
+
while (true) {
|
|
84
|
+
char c = s.charAt(i++);
|
|
85
|
+
if (c == '"') return b.toString();
|
|
86
|
+
if (c != '\\') { b.append(c); continue; }
|
|
87
|
+
char e = s.charAt(i++);
|
|
88
|
+
switch (e) {
|
|
89
|
+
case '"': b.append('"'); break;
|
|
90
|
+
case '\\': b.append('\\'); break;
|
|
91
|
+
case '/': b.append('/'); break;
|
|
92
|
+
case 'b': b.append('\b'); break;
|
|
93
|
+
case 'f': b.append('\f'); break;
|
|
94
|
+
case 'n': b.append('\n'); break;
|
|
95
|
+
case 'r': b.append('\r'); break;
|
|
96
|
+
case 't': b.append('\t'); break;
|
|
97
|
+
case 'u': b.append((char) Integer.parseInt(s.substring(i, i + 4), 16)); i += 4; break;
|
|
98
|
+
default: throw new IllegalArgumentException("JSON: экран \\" + e);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// --- запись ---------------------------------------------------------------
|
|
104
|
+
public static String quote(String t) {
|
|
105
|
+
StringBuilder b = new StringBuilder("\"");
|
|
106
|
+
for (char c : t.toCharArray()) {
|
|
107
|
+
switch (c) {
|
|
108
|
+
case '"': b.append("\\\""); break;
|
|
109
|
+
case '\\': b.append("\\\\"); break;
|
|
110
|
+
case '\n': b.append("\\n"); break;
|
|
111
|
+
case '\r': b.append("\\r"); break;
|
|
112
|
+
case '\t': b.append("\\t"); break;
|
|
113
|
+
default:
|
|
114
|
+
if (c < 0x20) b.append(String.format("\\u%04x", (int) c)); else b.append(c);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return b.append('"').toString();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@SuppressWarnings("unchecked")
|
|
121
|
+
public static String write(Object v) {
|
|
122
|
+
if (v == null) return "null";
|
|
123
|
+
if (v instanceof Boolean || v instanceof Num) return v.toString();
|
|
124
|
+
if (v instanceof String) return quote((String) v);
|
|
125
|
+
if (v instanceof List) {
|
|
126
|
+
StringBuilder b = new StringBuilder("[");
|
|
127
|
+
boolean first = true;
|
|
128
|
+
for (Object x : (List<Object>) v) { if (!first) b.append(','); first = false; b.append(write(x)); }
|
|
129
|
+
return b.append(']').toString();
|
|
130
|
+
}
|
|
131
|
+
if (v instanceof Map) {
|
|
132
|
+
StringBuilder b = new StringBuilder("{");
|
|
133
|
+
boolean first = true;
|
|
134
|
+
for (Map.Entry<String, Object> e : ((Map<String, Object>) v).entrySet()) {
|
|
135
|
+
if (!first) b.append(','); first = false;
|
|
136
|
+
b.append(quote(e.getKey())).append(':').append(write(e.getValue()));
|
|
137
|
+
}
|
|
138
|
+
return b.append('}').toString();
|
|
139
|
+
}
|
|
140
|
+
throw new IllegalArgumentException("JSON: вид " + v.getClass());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@SuppressWarnings("unchecked")
|
|
144
|
+
public static Map<String, Object> obj(Object v) { return (Map<String, Object>) v; }
|
|
145
|
+
@SuppressWarnings("unchecked")
|
|
146
|
+
public static List<Object> arr(Object v) { return v == null ? new ArrayList<>() : (List<Object>) v; }
|
|
147
|
+
public static String str(Object v) { return v == null ? "" : v.toString(); }
|
|
148
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Раннер Java-бэкенда: тот же вход и тот же ответ, что у runner.py.
|
|
2
|
+
//
|
|
3
|
+
// Протокол выхода общий для всех целей: ответ — JSON и код 0; названный отказ
|
|
4
|
+
// среза — JSON {"refused": текст} и код 3; любой другой код — крах.
|
|
5
|
+
import java.nio.charset.StandardCharsets;
|
|
6
|
+
import java.nio.file.Files;
|
|
7
|
+
import java.nio.file.Path;
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.HashSet;
|
|
10
|
+
import java.util.LinkedHashMap;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import java.util.Map;
|
|
13
|
+
import java.util.Set;
|
|
14
|
+
|
|
15
|
+
public final class Runner {
|
|
16
|
+
static final List<Object> ISSUES = new ArrayList<>();
|
|
17
|
+
|
|
18
|
+
static Object arg(Map<String, Object> a) {
|
|
19
|
+
String kind = Json.str(a.get("kind"));
|
|
20
|
+
if (kind.equals("entity_ref")) return Json.str(a.get("id"));
|
|
21
|
+
if (kind.equals("const_ref")) {
|
|
22
|
+
// §54: разыменование предшествует исполнению; член перечисления §38 либо
|
|
23
|
+
// имя без объявления остаётся идентичностью, как в ключе атома оракула.
|
|
24
|
+
Object[] c = Model.CONSTS.get(Json.str(a.get("id")));
|
|
25
|
+
return c == null ? Runtime.sym(Json.str(a.get("id"))) : c[1];
|
|
26
|
+
}
|
|
27
|
+
if (!kind.equals("value")) throw new Runtime.Refused("аргумент вне среза раннера: " + kind);
|
|
28
|
+
String t = Json.str(Json.obj(a.get("type")).get("name"));
|
|
29
|
+
String v = Json.str(a.get("value"));
|
|
30
|
+
switch (t) {
|
|
31
|
+
case "urn:law:std#Money": return Runtime.money(v, Json.str(a.get("currency")));
|
|
32
|
+
case "urn:law:std#Decimal":
|
|
33
|
+
case "urn:law:std#Integer": return Runtime.dec(v);
|
|
34
|
+
case "urn:law:std#Text": return v;
|
|
35
|
+
case "urn:law:std#Date": return Runtime.date(v);
|
|
36
|
+
default: throw new Runtime.Refused("аргумент вне среза раннера: " + t);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Тип аргумента, если известен: у value — из узла, у const_ref — из объявления
|
|
41
|
+
// константы; entity_ref типа не несёт (как у оракула).
|
|
42
|
+
static String argType(Map<String, Object> a) {
|
|
43
|
+
String kind = Json.str(a.get("kind"));
|
|
44
|
+
if (kind.equals("value")) return Json.str(Json.obj(a.get("type")).get("name"));
|
|
45
|
+
if (kind.equals("const_ref")) { Object[] c = Model.CONSTS.get(Json.str(a.get("id"))); return c == null ? null : (String) c[0]; }
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Совпадение типа с наследованием сущностей вверх от фактического.
|
|
50
|
+
static boolean conforms(String actual, String expected) {
|
|
51
|
+
Set<String> seen = new HashSet<>();
|
|
52
|
+
String cur = actual;
|
|
53
|
+
while (cur != null && !seen.contains(cur)) {
|
|
54
|
+
if (cur.equals(expected)) return true;
|
|
55
|
+
seen.add(cur);
|
|
56
|
+
cur = Model.BASES.get(cur);
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static List<Object> args(Map<String, Object> lit) { return Json.arr(lit.get("args")); }
|
|
62
|
+
|
|
63
|
+
// Арность и типы аргументов утверждения против сигнатуры §189 (E-0046).
|
|
64
|
+
static boolean wellTyped(Map<String, Object> lit) {
|
|
65
|
+
String[] params = Model.PARAMS.get(Json.str(lit.get("predicate")));
|
|
66
|
+
if (params == null) return true;
|
|
67
|
+
List<Object> as = args(lit);
|
|
68
|
+
if (as.size() != params.length) { ISSUES.add("TYPE_ERROR"); return false; }
|
|
69
|
+
for (int i = 0; i < params.length; i++) {
|
|
70
|
+
String actual = argType(Json.obj(as.get(i)));
|
|
71
|
+
if (actual != null && !conforms(actual, params[i])) { ISSUES.add("TYPE_ERROR"); return false; }
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Ключ атома у оракула §208 включает ТИП значения: несогласованный запрос
|
|
77
|
+
// атома не находит — NEITHER без issue.
|
|
78
|
+
static boolean queryTyped(Map<String, Object> lit) {
|
|
79
|
+
String[] params = Model.PARAMS.get(Json.str(lit.get("predicate")));
|
|
80
|
+
if (params == null) return false;
|
|
81
|
+
List<Object> as = args(lit);
|
|
82
|
+
if (as.size() != params.length) return false;
|
|
83
|
+
for (int i = 0; i < params.length; i++) {
|
|
84
|
+
String actual = argType(Json.obj(as.get(i)));
|
|
85
|
+
if (actual != null && !conforms(actual, params[i])) return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static boolean keyConflictBlocks(String predicate) {
|
|
91
|
+
Set<String> tainted = new HashSet<>(Model.keyConflictPredicates());
|
|
92
|
+
ArrayList<String> waiting = new ArrayList<>(tainted);
|
|
93
|
+
while (!waiting.isEmpty()) {
|
|
94
|
+
String source = waiting.remove(waiting.size() - 1);
|
|
95
|
+
for (String consumer : Model.KEY_READERS.getOrDefault(source, new String[0])) if (tainted.add(consumer)) waiting.add(consumer);
|
|
96
|
+
}
|
|
97
|
+
return tainted.contains(predicate);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static void run(String casePath, String queryPath) throws Exception {
|
|
101
|
+
Map<String, Object> kase = Json.obj(Json.parse(Files.readString(Path.of(casePath), StandardCharsets.UTF_8)));
|
|
102
|
+
Map<String, Object> query = Json.obj(Json.parse(Files.readString(Path.of(queryPath), StandardCharsets.UTF_8)));
|
|
103
|
+
Map<String, Object> ctx = kase.get("context") == null ? new LinkedHashMap<>() : Json.obj(kase.get("context"));
|
|
104
|
+
Runtime.setLegalTime(Json.str(ctx.get("legalTime")));
|
|
105
|
+
Model.packageFacts();
|
|
106
|
+
for (Object a : Json.arr(kase.get("assertions"))) {
|
|
107
|
+
Map<String, Object> lit = Json.obj(Json.obj(a).get("literal"));
|
|
108
|
+
if (!"positive".equals(Json.str(lit.get("polarity")))) throw new Runtime.Refused("§113: отрицательный факт вне L0-среза");
|
|
109
|
+
if (!wellTyped(lit)) continue;
|
|
110
|
+
String pred = Json.str(lit.get("predicate"));
|
|
111
|
+
if (!Model.PARAMS.containsKey(pred)) continue;
|
|
112
|
+
List<Object> row = new ArrayList<>();
|
|
113
|
+
for (Object x : args(lit)) row.add(arg(Json.obj(x)));
|
|
114
|
+
Model.register(pred, row);
|
|
115
|
+
}
|
|
116
|
+
Model.saturate();
|
|
117
|
+
for (int n = Model.keyConflicts(); n > 0; n--) ISSUES.add("KEY_CONFLICT");
|
|
118
|
+
if (!"truth".equals(Json.str(query.get("kind")))) throw new Runtime.Refused("вид запроса вне среза раннера: " + Json.str(query.get("kind")));
|
|
119
|
+
Map<String, Object> lit = Json.obj(query.get("literal"));
|
|
120
|
+
boolean hit = false;
|
|
121
|
+
if (queryTyped(lit)) {
|
|
122
|
+
List<Object> want = new ArrayList<>();
|
|
123
|
+
for (Object x : args(lit)) want.add(arg(Json.obj(x)));
|
|
124
|
+
List<List<Object>> rows = Model.TABLES.getOrDefault(Json.str(lit.get("predicate")), new ArrayList<>());
|
|
125
|
+
for (List<Object> r : rows) {
|
|
126
|
+
if (r.size() != want.size()) continue;
|
|
127
|
+
boolean all = true;
|
|
128
|
+
for (int i = 0; i < r.size(); i++) if (!Runtime.eq(r.get(i), want.get(i))) { all = false; break; }
|
|
129
|
+
if (all) { hit = true; break; }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Открытый мир: отсутствие строки — это NEITHER, а не FALSE_ONLY.
|
|
133
|
+
Map<String, Object> out = new LinkedHashMap<>();
|
|
134
|
+
out.put("queryId", query.get("queryId"));
|
|
135
|
+
out.put("truthStatus", hit ? "TRUE_ONLY" : "NEITHER");
|
|
136
|
+
out.put("evaluationStatus", keyConflictBlocks(Json.str(lit.get("predicate"))) ? "NON_EXECUTABLE" : "COMPUTED");
|
|
137
|
+
out.put("issues", ISSUES);
|
|
138
|
+
System.out.println(Json.write(out));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public static void main(String[] argv) throws Exception {
|
|
142
|
+
try {
|
|
143
|
+
run(argv[0], argv[1]);
|
|
144
|
+
} catch (Runtime.Refused e) {
|
|
145
|
+
Map<String, Object> out = new LinkedHashMap<>();
|
|
146
|
+
out.put("refused", e.getMessage());
|
|
147
|
+
System.out.println(Json.write(out));
|
|
148
|
+
System.exit(3);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// Рантайм L0 для Java. Тот же контракт, что у runtime.py: точная десятичная
|
|
2
|
+
// арифметика §58 на BigInteger, деньги с валютой, названный режим округления,
|
|
3
|
+
// календарный день, член перечисления. Отказ — исключение Refused; любое другое
|
|
4
|
+
// исключение — крах.
|
|
5
|
+
import java.math.BigInteger;
|
|
6
|
+
import java.util.List;
|
|
7
|
+
import java.util.ArrayList;
|
|
8
|
+
|
|
9
|
+
public final class Runtime {
|
|
10
|
+
private Runtime() {}
|
|
11
|
+
|
|
12
|
+
public static final class Refused extends RuntimeException {
|
|
13
|
+
public Refused(String m) { super(m); }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public static class Dec {
|
|
17
|
+
public final BigInteger u; public final int s;
|
|
18
|
+
public Dec(BigInteger u, int s) { this.u = u; this.s = s; }
|
|
19
|
+
}
|
|
20
|
+
public static final class Money extends Dec {
|
|
21
|
+
public final String cur;
|
|
22
|
+
public Money(BigInteger u, int s, String cur) { super(u, s); this.cur = cur; }
|
|
23
|
+
}
|
|
24
|
+
// Календарный день §2.15 в канонической записи ISO YYYY-MM-DD.
|
|
25
|
+
public static final class Date { public final String v; public Date(String v) { this.v = v; } }
|
|
26
|
+
// Член перечисления §38 либо ссылка const_ref без объявления.
|
|
27
|
+
public static final class Sym { public final String id; public Sym(String id) { this.id = id; } }
|
|
28
|
+
|
|
29
|
+
private static BigInteger p10(int n) { return BigInteger.TEN.pow(n); }
|
|
30
|
+
|
|
31
|
+
public static Dec dec(String lit) {
|
|
32
|
+
int dot = lit.indexOf('.');
|
|
33
|
+
String i = dot < 0 ? lit : lit.substring(0, dot);
|
|
34
|
+
String f = dot < 0 ? "" : lit.substring(dot + 1);
|
|
35
|
+
boolean neg = i.startsWith("-");
|
|
36
|
+
String digits = (neg ? i.substring(1) : i) + f;
|
|
37
|
+
BigInteger u = new BigInteger(digits);
|
|
38
|
+
return new Dec(neg ? u.negate() : u, f.length());
|
|
39
|
+
}
|
|
40
|
+
public static Money money(String lit, String cur) { Dec d = dec(lit); return new Money(d.u, d.s, cur); }
|
|
41
|
+
public static Date date(String lit) { return new Date(lit); }
|
|
42
|
+
public static Sym sym(String id) { return new Sym(id); }
|
|
43
|
+
|
|
44
|
+
// --- виды ---------------------------------------------------------------
|
|
45
|
+
private static boolean isNum(Object v) { return v instanceof Dec; }
|
|
46
|
+
private static boolean isMoney(Object v) { return v instanceof Money; }
|
|
47
|
+
|
|
48
|
+
// §48/§58: сложение, вычитание, min/max требуют совпадения видов; у денег — и валюты.
|
|
49
|
+
private static void same(Object a, Object b, String op) {
|
|
50
|
+
if (!isNum(a) || !isNum(b) || isMoney(a) != isMoney(b)) throw new Refused("§58: виды операндов не совпадают: " + op);
|
|
51
|
+
if (isMoney(a) && !((Money) a).cur.equals(((Money) b).cur)) throw new Refused("§58: разные валюты: " + op);
|
|
52
|
+
}
|
|
53
|
+
private static BigInteger[] pair(Dec a, Dec b) {
|
|
54
|
+
int s = Math.max(a.s, b.s);
|
|
55
|
+
return new BigInteger[]{a.u.multiply(p10(s - a.s)), b.u.multiply(p10(s - b.s)), BigInteger.valueOf(s)};
|
|
56
|
+
}
|
|
57
|
+
private static Dec mk(BigInteger u, int s, Object like) {
|
|
58
|
+
return isMoney(like) ? new Money(u, s, ((Money) like).cur) : new Dec(u, s);
|
|
59
|
+
}
|
|
60
|
+
// Целое значение: дробная часть нулевая (защита в глубину под статикой плана).
|
|
61
|
+
private static BigInteger integral(Object v) {
|
|
62
|
+
if (!(v instanceof Dec) || v instanceof Money) return null;
|
|
63
|
+
Dec d = (Dec) v;
|
|
64
|
+
BigInteger[] qr = d.u.divideAndRemainder(p10(d.s));
|
|
65
|
+
return qr[1].signum() == 0 ? qr[0] : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- арифметика §58 -----------------------------------------------------
|
|
69
|
+
public static Dec add(Object a, Object b) { same(a, b, "add"); BigInteger[] p = pair((Dec) a, (Dec) b); return mk(p[0].add(p[1]), p[2].intValue(), a); }
|
|
70
|
+
public static Dec sub(Object a, Object b) { same(a, b, "sub"); BigInteger[] p = pair((Dec) a, (Dec) b); return mk(p[0].subtract(p[1]), p[2].intValue(), a); }
|
|
71
|
+
|
|
72
|
+
// Правило скаляра §49: безразмерное масштабирует размерное; масштаб результата —
|
|
73
|
+
// сумма масштабов. Два размерных не перемножаются.
|
|
74
|
+
public static Dec mul(Object a, Object b) {
|
|
75
|
+
if (!isNum(a) || !isNum(b)) throw new Refused("§58: виды операндов не совпадают: mul");
|
|
76
|
+
if (isMoney(a) && isMoney(b)) throw new Refused("§58: произведение двух размерных величин не определено");
|
|
77
|
+
Dec x = (Dec) a, y = (Dec) b;
|
|
78
|
+
return mk(x.u.multiply(y.u), x.s + y.s, isMoney(a) ? a : b);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Точное десятичное частное num/den при 10-гладком знаменателе (§18), иначе null.
|
|
82
|
+
private static Object[] exact(BigInteger num, BigInteger den) {
|
|
83
|
+
BigInteger g = num.gcd(den);
|
|
84
|
+
if (g.signum() == 0) g = BigInteger.ONE;
|
|
85
|
+
num = num.divide(g); den = den.divide(g);
|
|
86
|
+
if (den.signum() < 0) { num = num.negate(); den = den.negate(); }
|
|
87
|
+
BigInteger two = BigInteger.TWO, five = BigInteger.valueOf(5);
|
|
88
|
+
int twos = 0, fives = 0;
|
|
89
|
+
while (den.mod(two).signum() == 0) { den = den.divide(two); twos++; }
|
|
90
|
+
while (den.mod(five).signum() == 0) { den = den.divide(five); fives++; }
|
|
91
|
+
if (!den.equals(BigInteger.ONE)) return null;
|
|
92
|
+
int k = Math.max(twos, fives);
|
|
93
|
+
return new Object[]{num.multiply(two.pow(k - twos)).multiply(five.pow(k - fives)), k};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Таблица делимых §58 в срезе: Decimal/Decimal, Money/Decimal, Money/Money одной
|
|
97
|
+
// валюты. Integer/Integer даёт Rational — вне среза, отвергается планом статически.
|
|
98
|
+
public static Dec div(Object a, Object b) {
|
|
99
|
+
if (!isNum(a) || !isNum(b)) throw new Refused("§58: деление вне таблицы делимых");
|
|
100
|
+
Dec x = (Dec) a, y = (Dec) b;
|
|
101
|
+
if (y.u.signum() == 0) throw new Refused("деление на ноль — evaluation error (§58)");
|
|
102
|
+
Money like = null;
|
|
103
|
+
if (isMoney(a) && isMoney(b)) {
|
|
104
|
+
if (!((Money) a).cur.equals(((Money) b).cur)) throw new Refused("CURRENCY_MISMATCH: §58 делит Money на Money только при тождественной валюте");
|
|
105
|
+
} else if (isMoney(a)) {
|
|
106
|
+
like = (Money) a;
|
|
107
|
+
} else if (isMoney(b)) {
|
|
108
|
+
throw new Refused("§58: деление вне таблицы делимых");
|
|
109
|
+
}
|
|
110
|
+
Object[] q = exact(x.u.multiply(p10(y.s)), y.u.multiply(p10(x.s)));
|
|
111
|
+
if (q == null) {
|
|
112
|
+
if (like != null) throw new Refused("INEXACT_DIVISION: округление только явной политикой (§50/§18)");
|
|
113
|
+
throw new Refused("§58: частное не 10-гладкое — Rational вне среза");
|
|
114
|
+
}
|
|
115
|
+
return like != null ? new Money((BigInteger) q[0], (Integer) q[1], like.cur) : new Dec((BigInteger) q[0], (Integer) q[1]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Евклидов остаток §58: 0 <= r < |b| при любых знаках операндов.
|
|
119
|
+
public static Dec mod(Object a, Object b) {
|
|
120
|
+
BigInteger x = integral(a), y = integral(b);
|
|
121
|
+
if (x == null || y == null) throw new Refused("§58: mod определён только для Integer");
|
|
122
|
+
if (y.signum() == 0) throw new Refused("остаток от деления на ноль — evaluation error (§58)");
|
|
123
|
+
return new Dec(x.mod(y.abs()), 0); // BigInteger.mod — неотрицательный остаток
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public static Dec min(Object a, Object b) { same(a, b, "min"); BigInteger[] p = pair((Dec) a, (Dec) b); return p[0].compareTo(p[1]) <= 0 ? (Dec) a : (Dec) b; }
|
|
127
|
+
public static Dec max(Object a, Object b) { same(a, b, "max"); BigInteger[] p = pair((Dec) a, (Dec) b); return p[0].compareTo(p[1]) >= 0 ? (Dec) a : (Dec) b; }
|
|
128
|
+
|
|
129
|
+
public static Dec abs(Object v) {
|
|
130
|
+
if (!isNum(v)) throw new Refused("§58: abs — не числовой вид");
|
|
131
|
+
Dec d = (Dec) v;
|
|
132
|
+
return mk(d.u.abs(), d.s, v);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private static final BigInteger POW_MIN = BigInteger.valueOf(Long.MIN_VALUE);
|
|
136
|
+
private static final BigInteger POW_MAX = BigInteger.valueOf(Long.MAX_VALUE);
|
|
137
|
+
|
|
138
|
+
// §58 (E-0075): только Integer pow Integer; порядок проверок — знак, предел,
|
|
139
|
+
// вычисление, диапазон — часть контракта.
|
|
140
|
+
public static Dec pow(Object base, Object exp) {
|
|
141
|
+
BigInteger x = integral(base), e = integral(exp);
|
|
142
|
+
if (x == null || e == null) throw new Refused("§58: pow определён только для Integer");
|
|
143
|
+
if (e.signum() < 0) throw new Refused("§58: pow — показатель отрицателен");
|
|
144
|
+
if (e.compareTo(BigInteger.valueOf(4096)) > 0) throw new Refused("RESOURCE_LIMIT: pow — показатель превышает 4096");
|
|
145
|
+
BigInteger r = x.pow(e.intValue());
|
|
146
|
+
if (r.compareTo(POW_MIN) < 0 || r.compareTo(POW_MAX) > 0) throw new Refused("RESOURCE_LIMIT: pow — результат вне 64-битного диапазона");
|
|
147
|
+
return new Dec(r, 0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public static Dec round(Object m, Object digits, String mode) {
|
|
151
|
+
if (!mode.equals("HALF_UP")) throw new Refused("§50: режим округления не реализован: " + mode);
|
|
152
|
+
if (!isNum(m)) throw new Refused("§50: round — не числовой вид");
|
|
153
|
+
Dec x = (Dec) m;
|
|
154
|
+
int n = 0;
|
|
155
|
+
if (digits instanceof Dec) { Dec d = (Dec) digits; n = d.u.divide(p10(d.s)).intValue(); }
|
|
156
|
+
if (x.s <= n) return mk(x.u.multiply(p10(n - x.s)), n, m);
|
|
157
|
+
BigInteger drop = p10(x.s - n);
|
|
158
|
+
boolean neg = x.u.signum() < 0;
|
|
159
|
+
BigInteger[] qr = x.u.abs().divideAndRemainder(drop);
|
|
160
|
+
BigInteger up = qr[1].shiftLeft(1).compareTo(drop) >= 0 ? qr[0].add(BigInteger.ONE) : qr[0]; // HALF_UP — от нуля
|
|
161
|
+
return mk(neg ? up.negate() : up, n, m);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// --- сравнение и равенство -----------------------------------------------
|
|
165
|
+
private static boolean ordered(String op, int c) {
|
|
166
|
+
switch (op) {
|
|
167
|
+
case "lt": return c < 0;
|
|
168
|
+
case "le": return c <= 0;
|
|
169
|
+
case "gt": return c > 0;
|
|
170
|
+
case "ge": return c >= 0;
|
|
171
|
+
case "eq": return c == 0;
|
|
172
|
+
case "ne": return c != 0;
|
|
173
|
+
default: throw new Refused("сравнение вне среза рантайма: " + op);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public static boolean cmp(Object a, String op, Object b) {
|
|
178
|
+
if (a instanceof Sym || b instanceof Sym) {
|
|
179
|
+
boolean same = a instanceof Sym && b instanceof Sym && ((Sym) a).id.equals(((Sym) b).id);
|
|
180
|
+
if (op.equals("eq")) return same;
|
|
181
|
+
if (op.equals("ne")) return !same;
|
|
182
|
+
throw new Refused("§57: вид enum не упорядочен");
|
|
183
|
+
}
|
|
184
|
+
if (a instanceof Date || b instanceof Date) {
|
|
185
|
+
if (a instanceof Date && b instanceof Date) return ordered(op, ((Date) a).v.compareTo(((Date) b).v));
|
|
186
|
+
if (op.equals("eq")) return false;
|
|
187
|
+
if (op.equals("ne")) return true;
|
|
188
|
+
throw new Refused("§57: Date сравнивается только с Date");
|
|
189
|
+
}
|
|
190
|
+
if (a instanceof String || b instanceof String) {
|
|
191
|
+
boolean same = a instanceof String && b instanceof String && a.equals(b);
|
|
192
|
+
if (op.equals("eq")) return same;
|
|
193
|
+
if (op.equals("ne")) return !same;
|
|
194
|
+
throw new Refused("порядок на Text вне среза рантайма: " + op);
|
|
195
|
+
}
|
|
196
|
+
if (isMoney(a) != isMoney(b)) throw new Refused("§49: сравнение денег с безразмерным");
|
|
197
|
+
if (isMoney(a) && !((Money) a).cur.equals(((Money) b).cur)) throw new Refused("§49: разные валюты");
|
|
198
|
+
BigInteger[] p = pair((Dec) a, (Dec) b);
|
|
199
|
+
return ordered(op, p[0].compareTo(p[1]));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public static boolean eq(Object a, Object b) {
|
|
203
|
+
if (a instanceof Sym || b instanceof Sym) return a instanceof Sym && b instanceof Sym && ((Sym) a).id.equals(((Sym) b).id);
|
|
204
|
+
if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && ((Date) a).v.equals(((Date) b).v);
|
|
205
|
+
if (a instanceof String || b instanceof String) return a instanceof String && b instanceof String && a.equals(b);
|
|
206
|
+
if (isMoney(a) != isMoney(b)) return false;
|
|
207
|
+
if (isMoney(a) && !((Money) a).cur.equals(((Money) b).cur)) return false;
|
|
208
|
+
BigInteger[] p = pair((Dec) a, (Dec) b);
|
|
209
|
+
return p[0].equals(p[1]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private static String norm(Dec d) {
|
|
213
|
+
BigInteger u = d.u; int s = d.s;
|
|
214
|
+
while (s > 0 && u.mod(BigInteger.TEN).signum() == 0) { u = u.divide(BigInteger.TEN); s--; }
|
|
215
|
+
return u + "e-" + s;
|
|
216
|
+
}
|
|
217
|
+
// Разделитель и экран внутри текста экранируются: иначе ["a|e:b","c"] и
|
|
218
|
+
// ["a","b|e:c"] склеивались в один ключ, и второй факт терялся.
|
|
219
|
+
private static String esc(String t) { return t.replace("\\", "\\\\").replace("|", "\\|"); }
|
|
220
|
+
|
|
221
|
+
public static String key(List<Object> row) {
|
|
222
|
+
StringBuilder sb = new StringBuilder();
|
|
223
|
+
for (int i = 0; i < row.size(); i++) {
|
|
224
|
+
if (i > 0) sb.append('|');
|
|
225
|
+
Object v = row.get(i);
|
|
226
|
+
if (v instanceof String) sb.append("e:").append(esc((String) v));
|
|
227
|
+
else if (v instanceof Date) sb.append("t:").append(((Date) v).v);
|
|
228
|
+
else if (v instanceof Sym) sb.append("c:").append(esc(((Sym) v).id));
|
|
229
|
+
else if (v instanceof Money) sb.append("m:").append(((Money) v).cur).append(':').append(norm((Money) v));
|
|
230
|
+
else sb.append("d:").append(norm((Dec) v));
|
|
231
|
+
}
|
|
232
|
+
return sb.toString();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private static String legalTimeValue = "";
|
|
236
|
+
public static void setLegalTime(String t) { legalTimeValue = t; }
|
|
237
|
+
public static String legalTime() {
|
|
238
|
+
if (legalTimeValue.isEmpty()) throw new Refused("legalTime не подан делом");
|
|
239
|
+
return legalTimeValue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public static List<Object> row(Object... vs) { List<Object> r = new ArrayList<>(); for (Object v : vs) r.add(v); return r; }
|
|
243
|
+
}
|