libpetri 2.13.0 → 3.0.1
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.
- package/README.md +89 -130
- package/dist/chunk-6NH64RCU.js +1016 -0
- package/dist/chunk-6NH64RCU.js.map +1 -0
- package/dist/{chunk-7VJ5CYUU.js → chunk-JZIEWVAV.js} +905 -62
- package/dist/chunk-JZIEWVAV.js.map +1 -0
- package/dist/{chunk-JVI5HFRX.js → chunk-VCDOKWVU.js} +2 -2
- package/dist/{chunk-E3ZWB645.js → chunk-YVIPJ6KM.js} +1 -1
- package/dist/chunk-YVIPJ6KM.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/debug/index.js +2 -2
- package/dist/doclet/index.d.ts +12 -3
- package/dist/doclet/index.js +8 -4
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +21 -0
- package/dist/doclet/resources/petrinet-diagrams.js +3575 -3573
- package/dist/dot-exporter-3STXYK74.js +9 -0
- package/dist/{render-ZGZEZ5RK.js → elk-place-YVNQFGXI.js} +3 -258
- package/dist/elk-place-YVNQFGXI.js.map +1 -0
- package/dist/{event-store-DKTenPbC.d.ts → event-store-BFX_yJ8I.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/export/index.js +2 -2
- package/dist/index.d.ts +91 -38
- package/dist/index.js +368 -294
- package/dist/index.js.map +1 -1
- package/dist/pan-zoom-Cp51IkDl.d.ts +33 -0
- package/dist/{petri-net-C3LSY-vm.d.ts → petri-net-WSScMyDL.d.ts} +43 -29
- package/dist/preprocess-FN3F75JR.js +193 -0
- package/dist/preprocess-FN3F75JR.js.map +1 -0
- package/dist/render-QOHGDWNE.js +78 -0
- package/dist/render-QOHGDWNE.js.map +1 -0
- package/dist/render-dom/index.d.ts +28 -29
- package/dist/render-dom/index.js +14 -21
- package/dist/render-dom/index.js.map +1 -1
- package/dist/verification/index.d.ts +273 -7
- package/dist/verification/index.js +7 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +24 -32
- package/dist/viewer/index.js +9 -1003
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer.css +21 -0
- package/dist/viewer/viewer.iife.js +3575 -3573
- package/package.json +2 -2
- package/dist/chunk-7VJ5CYUU.js.map +0 -1
- package/dist/chunk-E3ZWB645.js.map +0 -1
- package/dist/dot-exporter-SHBYMMJ3.js +0 -9
- package/dist/render-ZGZEZ5RK.js.map +0 -1
- /package/dist/{chunk-JVI5HFRX.js.map → chunk-VCDOKWVU.js.map} +0 -0
- /package/dist/{dot-exporter-SHBYMMJ3.js.map → dot-exporter-3STXYK74.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SmtVerifier,
|
|
3
|
+
all,
|
|
3
4
|
allPlaces,
|
|
4
5
|
and,
|
|
5
6
|
andPlaces,
|
|
7
|
+
atLeast,
|
|
8
|
+
consumptionCount,
|
|
6
9
|
enumerateBranches,
|
|
10
|
+
exactly,
|
|
7
11
|
fork,
|
|
8
12
|
forwardInput,
|
|
9
13
|
isPassthrough,
|
|
10
14
|
isProven,
|
|
11
15
|
isViolated,
|
|
16
|
+
one,
|
|
12
17
|
outPlace,
|
|
13
18
|
passthrough,
|
|
14
19
|
produce,
|
|
15
20
|
requireOutputProducingActions,
|
|
21
|
+
requiredCount,
|
|
16
22
|
timeout,
|
|
17
23
|
timeoutPlace,
|
|
18
24
|
transform,
|
|
@@ -21,7 +27,7 @@ import {
|
|
|
21
27
|
withTimeout,
|
|
22
28
|
xor,
|
|
23
29
|
xorPlaces
|
|
24
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-JZIEWVAV.js";
|
|
25
31
|
import {
|
|
26
32
|
eventTransitionName,
|
|
27
33
|
isFailureEvent
|
|
@@ -31,7 +37,7 @@ import {
|
|
|
31
37
|
matchCorrelates,
|
|
32
38
|
matchKey,
|
|
33
39
|
matchSpec
|
|
34
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-YVIPJ6KM.js";
|
|
35
41
|
import {
|
|
36
42
|
MAX_DURATION_MS,
|
|
37
43
|
deadline,
|
|
@@ -71,8 +77,8 @@ function environmentPlace(name) {
|
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
// src/core/arc.ts
|
|
74
|
-
function inputArc(place2
|
|
75
|
-
return
|
|
80
|
+
function inputArc(place2) {
|
|
81
|
+
return { type: "input", place: place2 };
|
|
76
82
|
}
|
|
77
83
|
function outputArc(place2) {
|
|
78
84
|
return { type: "output", place: place2 };
|
|
@@ -89,62 +95,6 @@ function resetArc(place2) {
|
|
|
89
95
|
function arcPlace(arc) {
|
|
90
96
|
return arc.place;
|
|
91
97
|
}
|
|
92
|
-
function hasGuard(arc) {
|
|
93
|
-
return arc.guard !== void 0;
|
|
94
|
-
}
|
|
95
|
-
function matchesGuard(arc, value) {
|
|
96
|
-
if (arc.guard === void 0) return true;
|
|
97
|
-
return arc.guard(value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/core/in.ts
|
|
101
|
-
function one(place2, guard) {
|
|
102
|
-
return guard !== void 0 ? { type: "one", place: place2, guard } : { type: "one", place: place2 };
|
|
103
|
-
}
|
|
104
|
-
function exactly(count, place2, guard) {
|
|
105
|
-
if (count < 1) {
|
|
106
|
-
throw new Error(`count must be >= 1, got: ${count}`);
|
|
107
|
-
}
|
|
108
|
-
return guard !== void 0 ? { type: "exactly", place: place2, count, guard } : { type: "exactly", place: place2, count };
|
|
109
|
-
}
|
|
110
|
-
function all(place2, guard) {
|
|
111
|
-
return guard !== void 0 ? { type: "all", place: place2, guard } : { type: "all", place: place2 };
|
|
112
|
-
}
|
|
113
|
-
function atLeast(minimum, place2, guard) {
|
|
114
|
-
if (minimum < 1) {
|
|
115
|
-
throw new Error(`minimum must be >= 1, got: ${minimum}`);
|
|
116
|
-
}
|
|
117
|
-
return guard !== void 0 ? { type: "at-least", place: place2, minimum, guard } : { type: "at-least", place: place2, minimum };
|
|
118
|
-
}
|
|
119
|
-
function requiredCount(spec) {
|
|
120
|
-
switch (spec.type) {
|
|
121
|
-
case "one":
|
|
122
|
-
return 1;
|
|
123
|
-
case "exactly":
|
|
124
|
-
return spec.count;
|
|
125
|
-
case "all":
|
|
126
|
-
return 1;
|
|
127
|
-
case "at-least":
|
|
128
|
-
return spec.minimum;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function consumptionCount(spec, available) {
|
|
132
|
-
if (available < requiredCount(spec)) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`Cannot consume from '${spec.place.name}': available=${available}, required=${requiredCount(spec)}`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
switch (spec.type) {
|
|
138
|
-
case "one":
|
|
139
|
-
return 1;
|
|
140
|
-
case "exactly":
|
|
141
|
-
return spec.count;
|
|
142
|
-
case "all":
|
|
143
|
-
return available;
|
|
144
|
-
case "at-least":
|
|
145
|
-
return available;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
98
|
|
|
149
99
|
// src/core/name.ts
|
|
150
100
|
function nameId(value) {
|
|
@@ -1001,7 +951,7 @@ function rewriteTransition(t, prefix, placeRemap) {
|
|
|
1001
951
|
function substitutePlaces(t, remap) {
|
|
1002
952
|
return rebuildWithName(t, t.name, remap);
|
|
1003
953
|
}
|
|
1004
|
-
function rebuildWithName(t, name, remap) {
|
|
954
|
+
function rebuildWithName(t, name, remap, normalizeInputs) {
|
|
1005
955
|
const builder = Transition.builder(name).timing(t.timing).priority(t.priority).action(t.action);
|
|
1006
956
|
const alias = buildPlaceAlias(t, remap);
|
|
1007
957
|
if (alias.size > 0) {
|
|
@@ -1012,7 +962,7 @@ function rebuildWithName(t, name, remap) {
|
|
|
1012
962
|
for (let i = 0; i < t.inputSpecs.length; i++) {
|
|
1013
963
|
rewrittenInputs[i] = rewriteIn(t.inputSpecs[i], remap);
|
|
1014
964
|
}
|
|
1015
|
-
builder.inputs(...rewrittenInputs);
|
|
965
|
+
builder.inputs(...normalizeInputs !== void 0 ? normalizeInputs(rewrittenInputs) : rewrittenInputs);
|
|
1016
966
|
}
|
|
1017
967
|
if (t.outputSpec !== null) {
|
|
1018
968
|
builder.outputs(rewriteOut(t.outputSpec, remap));
|
|
@@ -1072,13 +1022,13 @@ var EMPTY_ALIAS2 = /* @__PURE__ */ new Map();
|
|
|
1072
1022
|
function rewriteIn(spec, remap) {
|
|
1073
1023
|
switch (spec.type) {
|
|
1074
1024
|
case "one":
|
|
1075
|
-
return one(resolve(spec.place, remap)
|
|
1025
|
+
return one(resolve(spec.place, remap));
|
|
1076
1026
|
case "exactly":
|
|
1077
|
-
return exactly(spec.count, resolve(spec.place, remap)
|
|
1027
|
+
return exactly(spec.count, resolve(spec.place, remap));
|
|
1078
1028
|
case "all":
|
|
1079
|
-
return all(resolve(spec.place, remap)
|
|
1029
|
+
return all(resolve(spec.place, remap));
|
|
1080
1030
|
case "at-least":
|
|
1081
|
-
return atLeast(spec.minimum, resolve(spec.place, remap)
|
|
1031
|
+
return atLeast(spec.minimum, resolve(spec.place, remap));
|
|
1082
1032
|
}
|
|
1083
1033
|
}
|
|
1084
1034
|
function rewriteOut(out, remap) {
|
|
@@ -1133,7 +1083,11 @@ function mergeTransitions(caller, instance, mergedName) {
|
|
|
1133
1083
|
if (mergedAction !== void 0) {
|
|
1134
1084
|
builder.action(mergedAction);
|
|
1135
1085
|
}
|
|
1136
|
-
|
|
1086
|
+
rejectCrossSideKindConflicts(caller, instance, mergedName);
|
|
1087
|
+
const unionedInputs = normalizeInputArcs(
|
|
1088
|
+
[...caller.inputSpecs, ...instance.inputSpecs],
|
|
1089
|
+
() => `Channel composition '${mergedName}'`
|
|
1090
|
+
);
|
|
1137
1091
|
if (unionedInputs.length > 0) {
|
|
1138
1092
|
builder.inputs(...unionedInputs);
|
|
1139
1093
|
}
|
|
@@ -1264,44 +1218,128 @@ function describeTiming(t) {
|
|
|
1264
1218
|
return `Exact(atMs=${t.atMs})`;
|
|
1265
1219
|
}
|
|
1266
1220
|
}
|
|
1267
|
-
function
|
|
1221
|
+
function keyOfInhibitor(arc) {
|
|
1222
|
+
return `inh|${arc.place.name}`;
|
|
1223
|
+
}
|
|
1224
|
+
function keyOfRead(arc) {
|
|
1225
|
+
return `read|${arc.place.name}`;
|
|
1226
|
+
}
|
|
1227
|
+
function keyOfReset(arc) {
|
|
1228
|
+
return `reset|${arc.place.name}`;
|
|
1229
|
+
}
|
|
1230
|
+
function normalizeInputArcs(arcs, seamOf) {
|
|
1231
|
+
if (arcs.length < 2) return arcs;
|
|
1232
|
+
const byPlace = /* @__PURE__ */ new Map();
|
|
1233
|
+
let collided = false;
|
|
1234
|
+
for (let i2 = 0; i2 < arcs.length; i2++) {
|
|
1235
|
+
const arc = arcs[i2];
|
|
1236
|
+
const name = arc.place.name;
|
|
1237
|
+
const prior = byPlace.get(name);
|
|
1238
|
+
if (prior === void 0) {
|
|
1239
|
+
byPlace.set(name, arc);
|
|
1240
|
+
} else {
|
|
1241
|
+
collided = true;
|
|
1242
|
+
byPlace.set(name, mergeInPair(prior, arc, seamOf(name)));
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
if (!collided) return arcs;
|
|
1246
|
+
const result = new Array(byPlace.size);
|
|
1247
|
+
let i = 0;
|
|
1248
|
+
for (const arc of byPlace.values()) result[i++] = arc;
|
|
1249
|
+
return result;
|
|
1250
|
+
}
|
|
1251
|
+
function mergeInPair(a, b, seam) {
|
|
1252
|
+
if (a.type === "all" && b.type === "all") return a;
|
|
1253
|
+
if (a.type === "at-least" && b.type === "at-least") {
|
|
1254
|
+
return a.minimum >= b.minimum ? a : b;
|
|
1255
|
+
}
|
|
1256
|
+
const countA = summableCount(a);
|
|
1257
|
+
const countB = summableCount(b);
|
|
1258
|
+
if (countA !== -1 && countB !== -1) {
|
|
1259
|
+
return exactly(countA + countB, a.place);
|
|
1260
|
+
}
|
|
1261
|
+
throw new Error(
|
|
1262
|
+
`${seam}: input arcs ${describeIn(a)} and ${describeIn(b)} collide on place '${a.place.name}' and have no additive merge (MOD-021 rule (c)). Use a single arc with exactly(n) / atLeast(n).`
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
function summableCount(arc) {
|
|
1266
|
+
switch (arc.type) {
|
|
1267
|
+
case "one":
|
|
1268
|
+
return 1;
|
|
1269
|
+
case "exactly":
|
|
1270
|
+
return arc.count;
|
|
1271
|
+
default:
|
|
1272
|
+
return -1;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
function describeIn(arc) {
|
|
1268
1276
|
switch (arc.type) {
|
|
1269
1277
|
case "one":
|
|
1270
|
-
return
|
|
1278
|
+
return "one()";
|
|
1271
1279
|
case "exactly":
|
|
1272
|
-
return
|
|
1280
|
+
return `exactly(${arc.count})`;
|
|
1273
1281
|
case "all":
|
|
1274
|
-
return
|
|
1282
|
+
return "all()";
|
|
1275
1283
|
case "at-least":
|
|
1276
|
-
return
|
|
1284
|
+
return `atLeast(${arc.minimum})`;
|
|
1277
1285
|
}
|
|
1278
1286
|
}
|
|
1279
|
-
function
|
|
1280
|
-
|
|
1287
|
+
function rejectCrossSideKindConflicts(caller, instance, mergedName) {
|
|
1288
|
+
const callerKinds = arcKindsByPlace(caller);
|
|
1289
|
+
if (callerKinds.size === 0) return;
|
|
1290
|
+
for (const [place2, instanceSet] of arcKindsByPlace(instance)) {
|
|
1291
|
+
const callerSet = callerKinds.get(place2);
|
|
1292
|
+
if (callerSet !== void 0 && !sameKindSet(callerSet, instanceSet)) {
|
|
1293
|
+
throw new Error(
|
|
1294
|
+
`Channel composition '${mergedName}': conflicting arc kinds on place '${place2}' \u2014 caller-side ${describeKinds(callerSet)} vs instance-side ${describeKinds(instanceSet)}. Different arc types on one place cannot be merged (MOD-021 rule (d)). Resolve explicitly.`
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1281
1298
|
}
|
|
1282
|
-
function
|
|
1283
|
-
|
|
1299
|
+
function arcKindsByPlace(t) {
|
|
1300
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1301
|
+
const add = (name, kind) => {
|
|
1302
|
+
let set = kinds.get(name);
|
|
1303
|
+
if (set === void 0) {
|
|
1304
|
+
set = /* @__PURE__ */ new Set();
|
|
1305
|
+
kinds.set(name, set);
|
|
1306
|
+
}
|
|
1307
|
+
set.add(kind);
|
|
1308
|
+
};
|
|
1309
|
+
for (const s of t.inputSpecs) add(s.place.name, "input");
|
|
1310
|
+
for (const a of t.inhibitors) add(a.place.name, "inhibitor");
|
|
1311
|
+
for (const a of t.reads) add(a.place.name, "read");
|
|
1312
|
+
for (const a of t.resets) add(a.place.name, "reset");
|
|
1313
|
+
return kinds;
|
|
1284
1314
|
}
|
|
1285
|
-
function
|
|
1286
|
-
return `
|
|
1315
|
+
function describeKinds(kinds) {
|
|
1316
|
+
return `[${[...kinds].sort().join(", ")}]`;
|
|
1287
1317
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
if (k === void 0) {
|
|
1293
|
-
k = String(++guardKeyCounter);
|
|
1294
|
-
GUARD_KEYS.set(g, k);
|
|
1295
|
-
}
|
|
1296
|
-
return k;
|
|
1318
|
+
function sameKindSet(a, b) {
|
|
1319
|
+
if (a.size !== b.size) return false;
|
|
1320
|
+
for (const k of a) if (!b.has(k)) return false;
|
|
1321
|
+
return true;
|
|
1297
1322
|
}
|
|
1298
|
-
function applyFusion(transitions, fusionMap) {
|
|
1323
|
+
function applyFusion(transitions, fusionMap, seamOf) {
|
|
1324
|
+
const normalizeInputs = (inputs) => normalizeInputArcs(inputs, seamOf);
|
|
1299
1325
|
const rewritten = /* @__PURE__ */ new Set();
|
|
1300
1326
|
for (const t of transitions) {
|
|
1301
|
-
rewritten.add(
|
|
1327
|
+
rewritten.add(rebuildWithName(
|
|
1328
|
+
t,
|
|
1329
|
+
t.name,
|
|
1330
|
+
fusionMap,
|
|
1331
|
+
fusionTouchesInputs(t, fusionMap) ? normalizeInputs : void 0
|
|
1332
|
+
));
|
|
1302
1333
|
}
|
|
1303
1334
|
return rewritten;
|
|
1304
1335
|
}
|
|
1336
|
+
function fusionTouchesInputs(t, fusionMap) {
|
|
1337
|
+
if (fusionMap.size === 0) return false;
|
|
1338
|
+
for (let i = 0; i < t.inputSpecs.length; i++) {
|
|
1339
|
+
if (fusionMap.has(t.inputSpecs[i].place.name)) return true;
|
|
1340
|
+
}
|
|
1341
|
+
return false;
|
|
1342
|
+
}
|
|
1305
1343
|
|
|
1306
1344
|
// src/verification/verification-harness.ts
|
|
1307
1345
|
function buildVerificationResult(syntheticNet, perProperty) {
|
|
@@ -2197,7 +2235,8 @@ var PetriNetBuilder = class _PetriNetBuilder {
|
|
|
2197
2235
|
* Map<string, Place<unknown>> convention — TypeScript Place identity is
|
|
2198
2236
|
* name-based per `runtime/compiled-net.ts`).
|
|
2199
2237
|
* 3. Walk every transition through {@link applyFusion} to rewrite arc place
|
|
2200
|
-
* references
|
|
2238
|
+
* references; input arcs colliding on a canonical place merge per
|
|
2239
|
+
* [MOD-021] (additive where summable, rejected otherwise).
|
|
2201
2240
|
* 4. Re-derive the place set from the rewritten transitions plus any
|
|
2202
2241
|
* caller-declared standalone places, dropping non-canonical members.
|
|
2203
2242
|
* Caller-declared standalone places that happen to be non-canonical
|
|
@@ -2283,7 +2322,10 @@ var PetriNetBuilder = class _PetriNetBuilder {
|
|
|
2283
2322
|
nonCanonicalNames.add(nc.name);
|
|
2284
2323
|
}
|
|
2285
2324
|
}
|
|
2286
|
-
const rewrittenTransitions = applyFusion(this._transitions, fusionMap)
|
|
2325
|
+
const rewrittenTransitions = applyFusion(this._transitions, fusionMap, (canonicalName) => {
|
|
2326
|
+
const owner = ownership.get(canonicalName);
|
|
2327
|
+
return `Fusion set '${owner !== void 0 ? owner.name : canonicalName}'`;
|
|
2328
|
+
});
|
|
2287
2329
|
const rebuiltPlaces = /* @__PURE__ */ new Set();
|
|
2288
2330
|
for (const p of this._places) {
|
|
2289
2331
|
if (!nonCanonicalNames.has(p.name)) {
|
|
@@ -2384,21 +2426,21 @@ var Marking = class _Marking {
|
|
|
2384
2426
|
return result;
|
|
2385
2427
|
}
|
|
2386
2428
|
/**
|
|
2387
|
-
* Removes and returns the first token whose value satisfies the
|
|
2429
|
+
* Removes and returns the first token whose value satisfies the predicate.
|
|
2388
2430
|
*
|
|
2389
|
-
* Performs a linear scan of the place's FIFO queue. If no
|
|
2390
|
-
* behaves like `removeFirst()`.
|
|
2431
|
+
* Performs a linear scan of the place's FIFO queue. If no predicate is
|
|
2432
|
+
* provided, behaves like `removeFirst()`. Otherwise skips non-matching
|
|
2391
2433
|
* tokens and splices the first match out of the queue (O(n) worst case).
|
|
2392
2434
|
*/
|
|
2393
2435
|
removeFirstMatching(spec) {
|
|
2394
2436
|
const queue = this.tokens.get(spec.place.name);
|
|
2395
2437
|
if (!queue || queue.length === 0) return null;
|
|
2396
|
-
if (!spec.
|
|
2438
|
+
if (!spec.predicate) {
|
|
2397
2439
|
return queue.shift();
|
|
2398
2440
|
}
|
|
2399
2441
|
for (let i = 0; i < queue.length; i++) {
|
|
2400
2442
|
const token = queue[i];
|
|
2401
|
-
if (spec.
|
|
2443
|
+
if (spec.predicate(token.value)) {
|
|
2402
2444
|
if (i === 0) queue.shift();
|
|
2403
2445
|
else queue.splice(i, 1);
|
|
2404
2446
|
return token;
|
|
@@ -2407,27 +2449,27 @@ var Marking = class _Marking {
|
|
|
2407
2449
|
return null;
|
|
2408
2450
|
}
|
|
2409
2451
|
// ======================== Token Inspection ========================
|
|
2410
|
-
/** Check if any token
|
|
2452
|
+
/** Check if any token satisfies the predicate. */
|
|
2411
2453
|
hasMatchingToken(spec) {
|
|
2412
2454
|
const queue = this.tokens.get(spec.place.name);
|
|
2413
2455
|
if (!queue || queue.length === 0) return false;
|
|
2414
|
-
if (!spec.
|
|
2415
|
-
return queue.some((t) => spec.
|
|
2456
|
+
if (!spec.predicate) return true;
|
|
2457
|
+
return queue.some((t) => spec.predicate(t.value));
|
|
2416
2458
|
}
|
|
2417
2459
|
/**
|
|
2418
|
-
* Counts tokens in a place whose values satisfy the
|
|
2460
|
+
* Counts tokens in a place whose values satisfy the predicate.
|
|
2419
2461
|
*
|
|
2420
|
-
* If no
|
|
2421
|
-
* With a
|
|
2422
|
-
* Used by the executor
|
|
2462
|
+
* If no predicate is provided, returns the total token count (O(1)).
|
|
2463
|
+
* With a predicate, performs a linear scan over all tokens (O(n)).
|
|
2464
|
+
* Used by the executor to size a ν-net correlated `all`/`at-least` consume.
|
|
2423
2465
|
*/
|
|
2424
2466
|
countMatching(spec) {
|
|
2425
2467
|
const queue = this.tokens.get(spec.place.name);
|
|
2426
2468
|
if (!queue || queue.length === 0) return 0;
|
|
2427
|
-
if (!spec.
|
|
2469
|
+
if (!spec.predicate) return queue.length;
|
|
2428
2470
|
let count = 0;
|
|
2429
2471
|
for (const t of queue) {
|
|
2430
|
-
if (spec.
|
|
2472
|
+
if (spec.predicate(t.value)) count++;
|
|
2431
2473
|
}
|
|
2432
2474
|
return count;
|
|
2433
2475
|
}
|
|
@@ -2486,12 +2528,11 @@ var CompiledNet = class _CompiledNet {
|
|
|
2486
2528
|
_placeToTransitions;
|
|
2487
2529
|
// Consumption place IDs per transition (input + reset places)
|
|
2488
2530
|
_consumptionPlaceIds;
|
|
2489
|
-
// Cardinality
|
|
2531
|
+
// Cardinality flags
|
|
2490
2532
|
_cardinalityChecks;
|
|
2491
|
-
_hasGuards;
|
|
2492
2533
|
// ν-net join flag: the transition carries a MatchSpec (NU-020). Precomputed
|
|
2493
2534
|
// so the hot enablement loop can skip the match check on non-ν transitions
|
|
2494
|
-
// (zero-cost gating)
|
|
2535
|
+
// (zero-cost gating).
|
|
2495
2536
|
_hasMatch;
|
|
2496
2537
|
constructor(net) {
|
|
2497
2538
|
this.net = net;
|
|
@@ -2524,7 +2565,6 @@ var CompiledNet = class _CompiledNet {
|
|
|
2524
2565
|
this._inhibitorMask = new Array(this.transitionCount);
|
|
2525
2566
|
this._consumptionPlaceIds = new Array(this.transitionCount);
|
|
2526
2567
|
this._cardinalityChecks = new Array(this.transitionCount).fill(null);
|
|
2527
|
-
this._hasGuards = new Array(this.transitionCount).fill(false);
|
|
2528
2568
|
this._hasMatch = new Array(this.transitionCount).fill(false);
|
|
2529
2569
|
const placeToTransitionsList = new Array(this.placeCount);
|
|
2530
2570
|
for (let i = 0; i < this.placeCount; i++) {
|
|
@@ -2536,16 +2576,20 @@ var CompiledNet = class _CompiledNet {
|
|
|
2536
2576
|
const needs = new Uint32Array(this.wordCount);
|
|
2537
2577
|
const inhibitors = new Uint32Array(this.wordCount);
|
|
2538
2578
|
let needsCardinality = false;
|
|
2579
|
+
const seenInputPlaces = /* @__PURE__ */ new Set();
|
|
2539
2580
|
for (const inSpec of t.inputSpecs) {
|
|
2581
|
+
if (seenInputPlaces.has(inSpec.place.name)) {
|
|
2582
|
+
throw new Error(
|
|
2583
|
+
`Transition '${t.name}' declares two input arcs on place '${inSpec.place.name}'. Duplicate input places have no coherent consumption semantics and are rejected at compile time (CORE-030). Use a single arc with exactly(n) / atLeast(n) instead.`
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
seenInputPlaces.add(inSpec.place.name);
|
|
2540
2587
|
const pid = this._placeIndex.get(inSpec.place.name);
|
|
2541
2588
|
setBit(needs, pid);
|
|
2542
2589
|
placeToTransitionsList[pid].push(tid);
|
|
2543
2590
|
if (inSpec.type !== "one") {
|
|
2544
2591
|
needsCardinality = true;
|
|
2545
2592
|
}
|
|
2546
|
-
if (inSpec.guard) {
|
|
2547
|
-
this._hasGuards[tid] = true;
|
|
2548
|
-
}
|
|
2549
2593
|
}
|
|
2550
2594
|
if (needsCardinality) {
|
|
2551
2595
|
const pids = [];
|
|
@@ -2597,6 +2641,14 @@ var CompiledNet = class _CompiledNet {
|
|
|
2597
2641
|
if (id === void 0) throw new Error(`Unknown place: ${place2.name}`);
|
|
2598
2642
|
return id;
|
|
2599
2643
|
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Non-throwing variant of {@link placeId}: `undefined` when the compiled net
|
|
2646
|
+
* does not know the place. Lets callers retain tokens on undeclared places
|
|
2647
|
+
* (CORE-072) instead of rejecting or dropping them.
|
|
2648
|
+
*/
|
|
2649
|
+
tryPlaceId(place2) {
|
|
2650
|
+
return this._placeIndex.get(place2.name);
|
|
2651
|
+
}
|
|
2600
2652
|
transitionId(t) {
|
|
2601
2653
|
const id = this._transitionIndex.get(t);
|
|
2602
2654
|
if (id === void 0) throw new Error(`Unknown transition: ${t.name}`);
|
|
@@ -2611,9 +2663,6 @@ var CompiledNet = class _CompiledNet {
|
|
|
2611
2663
|
cardinalityCheck(tid) {
|
|
2612
2664
|
return this._cardinalityChecks[tid];
|
|
2613
2665
|
}
|
|
2614
|
-
hasGuards(tid) {
|
|
2615
|
-
return this._hasGuards[tid];
|
|
2616
|
-
}
|
|
2617
2666
|
hasMatch(tid) {
|
|
2618
2667
|
return this._hasMatch[tid];
|
|
2619
2668
|
}
|
|
@@ -2625,8 +2674,9 @@ var CompiledNet = class _CompiledNet {
|
|
|
2625
2674
|
* 2. **Inhibitor check**: verifies no inhibitor places have tokens
|
|
2626
2675
|
* via `!intersects(snapshot, inhibitorMask)`.
|
|
2627
2676
|
*
|
|
2628
|
-
* This is a necessary but not sufficient condition — cardinality and
|
|
2629
|
-
* are performed separately by the executor for transitions that pass this
|
|
2677
|
+
* This is a necessary but not sufficient condition — cardinality and ν-net match
|
|
2678
|
+
* checks are performed separately by the executor for transitions that pass this
|
|
2679
|
+
* fast path.
|
|
2630
2680
|
*/
|
|
2631
2681
|
canEnableBitmap(tid, markingSnapshot) {
|
|
2632
2682
|
if (!containsAll(markingSnapshot, this._needsMask[tid])) return false;
|
|
@@ -2751,10 +2801,9 @@ function selectMatchName(perPlace, requireds) {
|
|
|
2751
2801
|
}
|
|
2752
2802
|
return bestName;
|
|
2753
2803
|
}
|
|
2754
|
-
function buildNameIndex(tokens, key
|
|
2804
|
+
function buildNameIndex(tokens, key) {
|
|
2755
2805
|
const index = /* @__PURE__ */ new Map();
|
|
2756
2806
|
for (const token of tokens) {
|
|
2757
|
-
if (guard && !guard(token.value)) continue;
|
|
2758
2807
|
const name = key(token.value);
|
|
2759
2808
|
if (name === void 0 || name === null) continue;
|
|
2760
2809
|
const ts = token.createdAt;
|
|
@@ -2784,17 +2833,11 @@ function findBinding(t, getTokens) {
|
|
|
2784
2833
|
const perPlace = [];
|
|
2785
2834
|
const requireds = [];
|
|
2786
2835
|
for (const k of ms.keys) {
|
|
2787
|
-
perPlace.push(buildNameIndex(getTokens(k.place), k.key
|
|
2836
|
+
perPlace.push(buildNameIndex(getTokens(k.place), k.key));
|
|
2788
2837
|
requireds.push(requiredFor(t, k.place.name));
|
|
2789
2838
|
}
|
|
2790
2839
|
return selectMatchName(perPlace, requireds);
|
|
2791
2840
|
}
|
|
2792
|
-
function guardFor(t, placeName) {
|
|
2793
|
-
for (const inSpec of t.inputSpecs) {
|
|
2794
|
-
if (inSpec.place.name === placeName) return inSpec.guard;
|
|
2795
|
-
}
|
|
2796
|
-
return void 0;
|
|
2797
|
-
}
|
|
2798
2841
|
var MinQueue = class {
|
|
2799
2842
|
inStack = [];
|
|
2800
2843
|
inMin = [];
|
|
@@ -2890,7 +2933,7 @@ var IncrementalMatcher = class {
|
|
|
2890
2933
|
heaped = false;
|
|
2891
2934
|
heap = new ReadyHeap();
|
|
2892
2935
|
ready = /* @__PURE__ */ new Map();
|
|
2893
|
-
/** Add one
|
|
2936
|
+
/** Add one token carrying `name` at `createdAt` to input `i`. */
|
|
2894
2937
|
add(i, name, createdAt) {
|
|
2895
2938
|
let q = this.ts[i].get(name);
|
|
2896
2939
|
if (!q) {
|
|
@@ -3052,9 +3095,7 @@ function validateOutSpec(tName, spec, producedPlaceNames) {
|
|
|
3052
3095
|
);
|
|
3053
3096
|
}
|
|
3054
3097
|
if (satisfied.length > 1) {
|
|
3055
|
-
|
|
3056
|
-
`'${tName}': XOR violation - multiple branches produced`
|
|
3057
|
-
);
|
|
3098
|
+
return resolveXorAmbiguity(tName, satisfied);
|
|
3058
3099
|
}
|
|
3059
3100
|
return satisfied[0];
|
|
3060
3101
|
}
|
|
@@ -3062,14 +3103,26 @@ function validateOutSpec(tName, spec, producedPlaceNames) {
|
|
|
3062
3103
|
return validateOutSpec(tName, spec.child, producedPlaceNames);
|
|
3063
3104
|
}
|
|
3064
3105
|
}
|
|
3106
|
+
function resolveXorAmbiguity(tName, satisfied) {
|
|
3107
|
+
const subsuming = satisfied.filter(
|
|
3108
|
+
(candidate) => satisfied.every(
|
|
3109
|
+
(other) => other === candidate || [...other].every((p) => candidate.has(p))
|
|
3110
|
+
)
|
|
3111
|
+
);
|
|
3112
|
+
if (subsuming.length === 1) return subsuming[0];
|
|
3113
|
+
throw new OutViolationError(
|
|
3114
|
+
`'${tName}': XOR violation - multiple branches produced`
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3065
3117
|
function produceTimeoutOutput(context, timeoutChild) {
|
|
3066
3118
|
switch (timeoutChild.type) {
|
|
3067
3119
|
case "place":
|
|
3068
3120
|
context.outputToHarvest(timeoutChild.place, null);
|
|
3069
3121
|
break;
|
|
3070
3122
|
case "forward-input": {
|
|
3071
|
-
const value
|
|
3072
|
-
|
|
3123
|
+
for (const value of context.inputs(timeoutChild.from)) {
|
|
3124
|
+
context.outputToHarvest(timeoutChild.to, value);
|
|
3125
|
+
}
|
|
3073
3126
|
break;
|
|
3074
3127
|
}
|
|
3075
3128
|
case "and":
|
|
@@ -3110,7 +3163,7 @@ var BitmapNetExecutor = class {
|
|
|
3110
3163
|
allSamePriority;
|
|
3111
3164
|
eventStoreEnabled;
|
|
3112
3165
|
// Bitmaps (Uint32Array, direct writes)
|
|
3113
|
-
|
|
3166
|
+
markingBitmap;
|
|
3114
3167
|
dirtySet;
|
|
3115
3168
|
markingSnapBuffer;
|
|
3116
3169
|
dirtySnapBuffer;
|
|
@@ -3139,6 +3192,11 @@ var BitmapNetExecutor = class {
|
|
|
3139
3192
|
readyBuffer = [];
|
|
3140
3193
|
// Pending reset places for clock-restart detection
|
|
3141
3194
|
pendingResetPlaces = /* @__PURE__ */ new Set();
|
|
3195
|
+
/**
|
|
3196
|
+
* Undeclared place names already reported (CORE-072 AC4). Keyed by name — TS
|
|
3197
|
+
* Place identity is name-based — so a hot loop warns once, not per token.
|
|
3198
|
+
*/
|
|
3199
|
+
warnedUnknownPlaces = /* @__PURE__ */ new Set();
|
|
3142
3200
|
transitionInputPlaceNames;
|
|
3143
3201
|
running = false;
|
|
3144
3202
|
draining = false;
|
|
@@ -3158,7 +3216,7 @@ var BitmapNetExecutor = class {
|
|
|
3158
3216
|
}
|
|
3159
3217
|
this.startMs = performance.now();
|
|
3160
3218
|
const wordCount = this.compiled.wordCount;
|
|
3161
|
-
this.
|
|
3219
|
+
this.markingBitmap = new Uint32Array(wordCount);
|
|
3162
3220
|
this.markingSnapBuffer = new Uint32Array(wordCount);
|
|
3163
3221
|
this.firingSnapBuffer = new Uint32Array(wordCount);
|
|
3164
3222
|
const dirtyWords = this.compiled.transitionCount + BIT_MASK >>> WORD_SHIFT;
|
|
@@ -3194,6 +3252,11 @@ var BitmapNetExecutor = class {
|
|
|
3194
3252
|
for (const spec of t.inputSpecs) names.add(spec.place.name);
|
|
3195
3253
|
this.transitionInputPlaceNames.set(t, names);
|
|
3196
3254
|
}
|
|
3255
|
+
for (const [place2, tokens] of initialTokens) {
|
|
3256
|
+
if (tokens.length > 0 && this.compiled.tryPlaceId(place2) === void 0) {
|
|
3257
|
+
this.warnUnknownPlace(place2, "");
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3197
3260
|
this.initMatchCaches();
|
|
3198
3261
|
}
|
|
3199
3262
|
/**
|
|
@@ -3252,9 +3315,7 @@ var BitmapNetExecutor = class {
|
|
|
3252
3315
|
for (let keyIdx = 0; keyIdx < ms.keys.length; keyIdx++) {
|
|
3253
3316
|
const mk = ms.keys[keyIdx];
|
|
3254
3317
|
const pid = compiled.placeId(mk.place);
|
|
3255
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
3256
3318
|
for (const token of this.marking.peekTokens(mk.place)) {
|
|
3257
|
-
if (guard && !guard(token.value)) continue;
|
|
3258
3319
|
const name = mk.key(token.value);
|
|
3259
3320
|
if (name !== void 0 && name !== null) matcher.add(keyIdx, name, token.createdAt);
|
|
3260
3321
|
}
|
|
@@ -3273,8 +3334,6 @@ var BitmapNetExecutor = class {
|
|
|
3273
3334
|
if (cache == null) continue;
|
|
3274
3335
|
const t = compiled.transition(tid);
|
|
3275
3336
|
const mk = t.matchSpec.keys[keyIdx];
|
|
3276
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
3277
|
-
if (guard && !guard(token.value)) continue;
|
|
3278
3337
|
const name = mk.key(token.value);
|
|
3279
3338
|
if (name !== void 0 && name !== null) cache.add(keyIdx, name, token.createdAt);
|
|
3280
3339
|
}
|
|
@@ -3302,7 +3361,7 @@ var BitmapNetExecutor = class {
|
|
|
3302
3361
|
netName: this.compiled.net.name,
|
|
3303
3362
|
executionId: this.executionId()
|
|
3304
3363
|
});
|
|
3305
|
-
this.
|
|
3364
|
+
this.initializeMarkingBitmap();
|
|
3306
3365
|
this.markAllDirty();
|
|
3307
3366
|
this.emitEvent({
|
|
3308
3367
|
type: "marking-snapshot",
|
|
@@ -3357,11 +3416,11 @@ var BitmapNetExecutor = class {
|
|
|
3357
3416
|
return this.inject(envPlace, tokenOf(value));
|
|
3358
3417
|
}
|
|
3359
3418
|
// ======================== Initialize ========================
|
|
3360
|
-
|
|
3419
|
+
initializeMarkingBitmap() {
|
|
3361
3420
|
for (let pid = 0; pid < this.compiled.placeCount; pid++) {
|
|
3362
3421
|
const place2 = this.compiled.place(pid);
|
|
3363
3422
|
if (this.marking.hasTokens(place2)) {
|
|
3364
|
-
setBit(this.
|
|
3423
|
+
setBit(this.markingBitmap, pid);
|
|
3365
3424
|
}
|
|
3366
3425
|
}
|
|
3367
3426
|
}
|
|
@@ -3389,7 +3448,7 @@ var BitmapNetExecutor = class {
|
|
|
3389
3448
|
updateDirtyTransitions() {
|
|
3390
3449
|
const nowMs = performance.now();
|
|
3391
3450
|
const markingSnap = this.markingSnapBuffer;
|
|
3392
|
-
markingSnap.set(this.
|
|
3451
|
+
markingSnap.set(this.markingBitmap);
|
|
3393
3452
|
const dirtyWords = this.dirtySet.length;
|
|
3394
3453
|
const dirtySnap = this.dirtySnapBuffer;
|
|
3395
3454
|
for (let w = 0; w < dirtyWords; w++) {
|
|
@@ -3474,17 +3533,6 @@ var BitmapNetExecutor = class {
|
|
|
3474
3533
|
if (this.marking.tokenCount(place2) < required) return false;
|
|
3475
3534
|
}
|
|
3476
3535
|
}
|
|
3477
|
-
if (this.compiled.hasGuards(tid)) {
|
|
3478
|
-
const t = this.compiled.transition(tid);
|
|
3479
|
-
const cache = this.matchCaches[tid];
|
|
3480
|
-
const ms = t.matchSpec;
|
|
3481
|
-
for (const spec of t.inputSpecs) {
|
|
3482
|
-
if (!spec.guard) continue;
|
|
3483
|
-
if (cache != null && ms && keyForPlace(ms, spec.place.name) !== void 0) continue;
|
|
3484
|
-
const requiredCount2 = spec.type === "one" ? 1 : spec.type === "exactly" ? spec.count : spec.type === "at-least" ? spec.minimum : 1;
|
|
3485
|
-
if (this.marking.countMatching(spec) < requiredCount2) return false;
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
3536
|
if (this.compiled.hasMatch(tid)) {
|
|
3489
3537
|
const cache = this.matchCaches[tid];
|
|
3490
3538
|
const noBinding = cache != null ? cache.best() === null : findBinding(this.compiled.transition(tid), (p) => this.marking.peekTokens(p)) === null;
|
|
@@ -3515,7 +3563,7 @@ var BitmapNetExecutor = class {
|
|
|
3515
3563
|
* Fast path for nets where all transitions are immediate and same priority.
|
|
3516
3564
|
* Skips timing checks, sorting, and snapshot buffer — just scan and fire.
|
|
3517
3565
|
*
|
|
3518
|
-
* Uses live `
|
|
3566
|
+
* Uses live `markingBitmap` instead of a snapshot. Safe because
|
|
3519
3567
|
* `updateBitmapAfterConsumption()` synchronously updates the bitmap before the next
|
|
3520
3568
|
* iteration. For equal-priority immediate transitions, tid scan order satisfies
|
|
3521
3569
|
* FIFO-by-enablement-time (all enabled in the same cycle).
|
|
@@ -3523,7 +3571,7 @@ var BitmapNetExecutor = class {
|
|
|
3523
3571
|
fireReadyImmediate() {
|
|
3524
3572
|
for (let tid = 0; tid < this.compiled.transitionCount; tid++) {
|
|
3525
3573
|
if (!this.enabledFlags[tid] || this.inFlightFlags[tid]) continue;
|
|
3526
|
-
if (this.canEnable(tid, this.
|
|
3574
|
+
if (this.canEnable(tid, this.markingBitmap)) {
|
|
3527
3575
|
this.fireTransitionContained(tid);
|
|
3528
3576
|
} else {
|
|
3529
3577
|
this.enabledFlags[tid] = 0;
|
|
@@ -3552,12 +3600,12 @@ var BitmapNetExecutor = class {
|
|
|
3552
3600
|
return a.enabledAtMs - b.enabledAtMs;
|
|
3553
3601
|
});
|
|
3554
3602
|
const freshSnap = this.firingSnapBuffer;
|
|
3555
|
-
freshSnap.set(this.
|
|
3603
|
+
freshSnap.set(this.markingBitmap);
|
|
3556
3604
|
for (const entry of ready) {
|
|
3557
3605
|
const { tid } = entry;
|
|
3558
3606
|
if (this.enabledFlags[tid] && this.canEnable(tid, freshSnap)) {
|
|
3559
3607
|
this.fireTransitionContained(tid);
|
|
3560
|
-
freshSnap.set(this.
|
|
3608
|
+
freshSnap.set(this.markingBitmap);
|
|
3561
3609
|
} else {
|
|
3562
3610
|
this.enabledFlags[tid] = 0;
|
|
3563
3611
|
this.enabledTransitionCount--;
|
|
@@ -3573,7 +3621,7 @@ var BitmapNetExecutor = class {
|
|
|
3573
3621
|
* EventStore.append on a token-removed or transition-started emit, or an error thrown while
|
|
3574
3622
|
* consuming the matched tokens — unwinds out of the orchestrator loop and kills the executor.
|
|
3575
3623
|
* The transition is instead failed and marked dirty for re-evaluation, the same treatment an
|
|
3576
|
-
* asynchronously-reported failure gets. Enablement-phase throws (a
|
|
3624
|
+
* asynchronously-reported failure gets. Enablement-phase throws (a key function that
|
|
3577
3625
|
* throws inside canEnable, before the firing) run outside this boundary and are not contained
|
|
3578
3626
|
* here.
|
|
3579
3627
|
*
|
|
@@ -3624,10 +3672,9 @@ var BitmapNetExecutor = class {
|
|
|
3624
3672
|
const keyFn = ms ? keyForPlace(ms, inSpec.place.name) : void 0;
|
|
3625
3673
|
let spec;
|
|
3626
3674
|
if (keyFn && chosen !== null) {
|
|
3627
|
-
const baseGuard = inSpec.guard;
|
|
3628
3675
|
spec = {
|
|
3629
3676
|
place: inSpec.place,
|
|
3630
|
-
|
|
3677
|
+
predicate: (v) => keyFn(v) === chosen
|
|
3631
3678
|
};
|
|
3632
3679
|
} else {
|
|
3633
3680
|
spec = inSpec;
|
|
@@ -3642,11 +3689,11 @@ var BitmapNetExecutor = class {
|
|
|
3642
3689
|
break;
|
|
3643
3690
|
case "all":
|
|
3644
3691
|
case "at-least":
|
|
3645
|
-
toConsume = spec.
|
|
3692
|
+
toConsume = spec.predicate ? this.marking.countMatching(spec) : this.marking.tokenCount(inSpec.place);
|
|
3646
3693
|
break;
|
|
3647
3694
|
}
|
|
3648
3695
|
for (let i = 0; i < toConsume; i++) {
|
|
3649
|
-
const token = spec.
|
|
3696
|
+
const token = spec.predicate ? this.marking.removeFirstMatching(spec) : this.marking.removeFirst(inSpec.place);
|
|
3650
3697
|
if (token === null) break;
|
|
3651
3698
|
consumed.push(token);
|
|
3652
3699
|
inputs.add(inSpec.place, token);
|
|
@@ -3770,7 +3817,7 @@ var BitmapNetExecutor = class {
|
|
|
3770
3817
|
for (const pid of pids) {
|
|
3771
3818
|
const place2 = this.compiled.place(pid);
|
|
3772
3819
|
if (!this.marking.hasTokens(place2)) {
|
|
3773
|
-
clearBit(this.
|
|
3820
|
+
clearBit(this.markingBitmap, pid);
|
|
3774
3821
|
}
|
|
3775
3822
|
this.markDirty(pid);
|
|
3776
3823
|
}
|
|
@@ -3812,12 +3859,16 @@ var BitmapNetExecutor = class {
|
|
|
3812
3859
|
}
|
|
3813
3860
|
const produced = [];
|
|
3814
3861
|
for (const entry of outputs.entries()) {
|
|
3815
|
-
const pid = this.compiled.
|
|
3816
|
-
this.cacheAddToken(pid, entry.token);
|
|
3862
|
+
const pid = this.compiled.tryPlaceId(entry.place);
|
|
3817
3863
|
this.marking.addToken(entry.place, entry.token);
|
|
3818
3864
|
produced.push(entry.token);
|
|
3819
|
-
|
|
3820
|
-
|
|
3865
|
+
if (pid !== void 0) {
|
|
3866
|
+
this.cacheAddToken(pid, entry.token);
|
|
3867
|
+
setBit(this.markingBitmap, pid);
|
|
3868
|
+
this.markDirty(pid);
|
|
3869
|
+
} else {
|
|
3870
|
+
this.warnUnknownPlace(entry.place, t.name);
|
|
3871
|
+
}
|
|
3821
3872
|
this.emitEvent({
|
|
3822
3873
|
type: "token-added",
|
|
3823
3874
|
timestamp: Date.now(),
|
|
@@ -3856,11 +3907,15 @@ var BitmapNetExecutor = class {
|
|
|
3856
3907
|
for (let i = 0; i < len; i++) {
|
|
3857
3908
|
const event = this.externalQueue[i];
|
|
3858
3909
|
try {
|
|
3859
|
-
const pid = this.compiled.
|
|
3860
|
-
this.cacheAddToken(pid, event.token);
|
|
3910
|
+
const pid = this.compiled.tryPlaceId(event.place);
|
|
3861
3911
|
this.marking.addToken(event.place, event.token);
|
|
3862
|
-
|
|
3863
|
-
|
|
3912
|
+
if (pid !== void 0) {
|
|
3913
|
+
this.cacheAddToken(pid, event.token);
|
|
3914
|
+
setBit(this.markingBitmap, pid);
|
|
3915
|
+
this.markDirty(pid);
|
|
3916
|
+
} else {
|
|
3917
|
+
this.warnUnknownPlace(event.place, "");
|
|
3918
|
+
}
|
|
3864
3919
|
this.emitEvent({
|
|
3865
3920
|
type: "token-added",
|
|
3866
3921
|
timestamp: Date.now(),
|
|
@@ -3991,6 +4046,25 @@ var BitmapNetExecutor = class {
|
|
|
3991
4046
|
this.wakeUp();
|
|
3992
4047
|
}
|
|
3993
4048
|
// ======================== Event Emission ========================
|
|
4049
|
+
/**
|
|
4050
|
+
* Reports an undeclared place once (CORE-072 AC4, emitted as the EVT-013
|
|
4051
|
+
* log-message event). `transitionName` is the producer, empty at the
|
|
4052
|
+
* initial-marking and injection seams. Retention never depends on this.
|
|
4053
|
+
*/
|
|
4054
|
+
warnUnknownPlace(place2, transitionName) {
|
|
4055
|
+
if (this.warnedUnknownPlaces.has(place2.name)) return;
|
|
4056
|
+
this.warnedUnknownPlaces.add(place2.name);
|
|
4057
|
+
this.emitEvent({
|
|
4058
|
+
type: "log-message",
|
|
4059
|
+
timestamp: Date.now(),
|
|
4060
|
+
transitionName,
|
|
4061
|
+
logger: "libpetri.runtime",
|
|
4062
|
+
level: "WARN",
|
|
4063
|
+
message: `unknown place '${place2.name}': tokens are retained in the marking but inert (the net declares no arc on it)`,
|
|
4064
|
+
error: null,
|
|
4065
|
+
errorMessage: null
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
3994
4068
|
emitEvent(event) {
|
|
3995
4069
|
if (this.eventStoreEnabled) {
|
|
3996
4070
|
try {
|
|
@@ -4028,6 +4102,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4028
4102
|
// ==================== Opcode Programs ====================
|
|
4029
4103
|
/** Per-transition consume opcode sequences. */
|
|
4030
4104
|
consumeOps;
|
|
4105
|
+
/**
|
|
4106
|
+
* Per-transition index into {@link consumeOps} where the RESET tail begins; the
|
|
4107
|
+
* executor peeks read arcs at this boundary so reads observe the post-input,
|
|
4108
|
+
* pre-reset marking (EXEC-013 AC4).
|
|
4109
|
+
*/
|
|
4110
|
+
resetOpsStart;
|
|
4031
4111
|
/** Per-transition read-arc place IDs. */
|
|
4032
4112
|
readOps;
|
|
4033
4113
|
// ==================== Enablement Masks ====================
|
|
@@ -4066,11 +4146,10 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4066
4146
|
// ==================== Reverse Index ====================
|
|
4067
4147
|
placeToTransitions;
|
|
4068
4148
|
consumptionPlaceIds;
|
|
4069
|
-
// ==================== Cardinality
|
|
4149
|
+
// ==================== Cardinality ====================
|
|
4070
4150
|
cardinalityChecks;
|
|
4071
|
-
hasGuards;
|
|
4072
4151
|
// ν-net join flag (NU-020): gates the match-binding check off the hot
|
|
4073
|
-
// enablement path for non-ν transitions
|
|
4152
|
+
// enablement path for non-ν transitions.
|
|
4074
4153
|
hasMatch;
|
|
4075
4154
|
// ==================== Global Flags ====================
|
|
4076
4155
|
allImmediate;
|
|
@@ -4147,9 +4226,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4147
4226
|
this.inhibitorSparseMasks = inhibitorSparseMasks;
|
|
4148
4227
|
const consumeOps = new Array(tc);
|
|
4149
4228
|
const readOps = new Array(tc);
|
|
4229
|
+
this.resetOpsStart = new Uint32Array(tc);
|
|
4150
4230
|
for (let tid = 0; tid < tc; tid++) {
|
|
4151
4231
|
const t = compiled.transition(tid);
|
|
4152
|
-
|
|
4232
|
+
const program = compileConsumeProgram(t, compiled);
|
|
4233
|
+
consumeOps[tid] = program.ops;
|
|
4234
|
+
this.resetOpsStart[tid] = program.resetOpsStart;
|
|
4153
4235
|
readOps[tid] = compileReadProgram(t, compiled);
|
|
4154
4236
|
}
|
|
4155
4237
|
this.consumeOps = consumeOps;
|
|
@@ -4165,15 +4247,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4165
4247
|
this.placeToTransitions = placeToTransitions;
|
|
4166
4248
|
this.consumptionPlaceIds = consumptionPlaceIds;
|
|
4167
4249
|
const cardinalityChecks = new Array(tc);
|
|
4168
|
-
const hasGuards = new Array(tc);
|
|
4169
4250
|
const hasMatch = new Array(tc);
|
|
4170
4251
|
for (let tid = 0; tid < tc; tid++) {
|
|
4171
4252
|
cardinalityChecks[tid] = compiled.cardinalityCheck(tid);
|
|
4172
|
-
hasGuards[tid] = compiled.hasGuards(tid);
|
|
4173
4253
|
hasMatch[tid] = compiled.hasMatch(tid);
|
|
4174
4254
|
}
|
|
4175
4255
|
this.cardinalityChecks = cardinalityChecks;
|
|
4176
|
-
this.hasGuards = hasGuards;
|
|
4177
4256
|
this.hasMatch = hasMatch;
|
|
4178
4257
|
this.earliestMs = new Float64Array(tc);
|
|
4179
4258
|
this.latestMs = new Float64Array(tc);
|
|
@@ -4336,11 +4415,12 @@ function compileConsumeProgram(t, compiled) {
|
|
|
4336
4415
|
break;
|
|
4337
4416
|
}
|
|
4338
4417
|
}
|
|
4418
|
+
const resetOpsStart = ops.length;
|
|
4339
4419
|
for (const arc of t.resets) {
|
|
4340
4420
|
const pid = compiled.placeId(arc.place);
|
|
4341
4421
|
ops.push(RESET, pid);
|
|
4342
4422
|
}
|
|
4343
|
-
return ops;
|
|
4423
|
+
return { ops, resetOpsStart };
|
|
4344
4424
|
}
|
|
4345
4425
|
function compileReadProgram(t, compiled) {
|
|
4346
4426
|
const pids = [];
|
|
@@ -4370,6 +4450,14 @@ var PrecompiledNetExecutor = class {
|
|
|
4370
4450
|
// ==================== Token Storage ====================
|
|
4371
4451
|
/** Per-place token arrays, indexed by pid. */
|
|
4372
4452
|
tokenQueues;
|
|
4453
|
+
/**
|
|
4454
|
+
* Tokens on places the compiled net does not know (CORE-072 AC3). Retained —
|
|
4455
|
+
* never dropped — and merged into the materialized marking, matching the
|
|
4456
|
+
* BitmapNetExecutor reference, whose Marking keeps them naturally. Keyed by
|
|
4457
|
+
* place NAME (TS Place identity is name-based), carrying the Place so
|
|
4458
|
+
* materializing a Marking has one to hand.
|
|
4459
|
+
*/
|
|
4460
|
+
unknownPlaceTokens = /* @__PURE__ */ new Map();
|
|
4373
4461
|
/** Monotonic source for ν-name minting (ctx.freshName(), NU-010). */
|
|
4374
4462
|
freshNameCounter = 0;
|
|
4375
4463
|
/**
|
|
@@ -4455,7 +4543,13 @@ var PrecompiledNetExecutor = class {
|
|
|
4455
4543
|
this.tokenQueues[pid] = [];
|
|
4456
4544
|
}
|
|
4457
4545
|
for (const [place2, tokens] of initialTokens) {
|
|
4458
|
-
const pid = prog.compiled.
|
|
4546
|
+
const pid = prog.compiled.tryPlaceId(place2);
|
|
4547
|
+
if (pid === void 0) {
|
|
4548
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
4549
|
+
this.retainUnknownToken(place2, tokens[i], "");
|
|
4550
|
+
}
|
|
4551
|
+
continue;
|
|
4552
|
+
}
|
|
4459
4553
|
const q = this.tokenQueues[pid];
|
|
4460
4554
|
for (const token of tokens) {
|
|
4461
4555
|
q.push(token);
|
|
@@ -4538,9 +4632,7 @@ var PrecompiledNetExecutor = class {
|
|
|
4538
4632
|
for (let keyIdx = 0; keyIdx < ms.keys.length; keyIdx++) {
|
|
4539
4633
|
const mk = ms.keys[keyIdx];
|
|
4540
4634
|
const pid = prog.compiled.placeId(mk.place);
|
|
4541
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
4542
4635
|
for (const token of this.tokenQueues[pid]) {
|
|
4543
|
-
if (guard && !guard(token.value)) continue;
|
|
4544
4636
|
const name = mk.key(token.value);
|
|
4545
4637
|
if (name !== void 0 && name !== null) matcher.add(keyIdx, name, token.createdAt);
|
|
4546
4638
|
}
|
|
@@ -4559,8 +4651,6 @@ var PrecompiledNetExecutor = class {
|
|
|
4559
4651
|
if (cache == null) continue;
|
|
4560
4652
|
const t = prog.compiled.transition(tid);
|
|
4561
4653
|
const mk = t.matchSpec.keys[keyIdx];
|
|
4562
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
4563
|
-
if (guard && !guard(token.value)) continue;
|
|
4564
4654
|
const name = mk.key(token.value);
|
|
4565
4655
|
if (name !== void 0 && name !== null) cache.add(keyIdx, name, token.createdAt);
|
|
4566
4656
|
}
|
|
@@ -4772,17 +4862,6 @@ var PrecompiledNetExecutor = class {
|
|
|
4772
4862
|
if (this.tokenQueues[pid].length < cardCheck.requiredCounts[i]) return false;
|
|
4773
4863
|
}
|
|
4774
4864
|
}
|
|
4775
|
-
if (prog.hasGuards[tid]) {
|
|
4776
|
-
const t = prog.compiled.transition(tid);
|
|
4777
|
-
const cache = this.matchCaches[tid];
|
|
4778
|
-
const ms = t.matchSpec;
|
|
4779
|
-
for (const spec of t.inputSpecs) {
|
|
4780
|
-
if (!spec.guard) continue;
|
|
4781
|
-
if (cache != null && ms && keyForPlace(ms, spec.place.name) !== void 0) continue;
|
|
4782
|
-
const required = spec.type === "one" ? 1 : spec.type === "exactly" ? spec.count : spec.type === "at-least" ? spec.minimum : 1;
|
|
4783
|
-
if (this.countMatching(prog.compiled.placeId(spec.place), spec.guard) < required) return false;
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
4865
|
if (prog.hasMatch[tid]) {
|
|
4787
4866
|
const cache = this.matchCaches[tid];
|
|
4788
4867
|
const noBinding = cache != null ? cache.best() === null : findBinding(prog.compiled.transition(tid), (p) => this.tokenQueues[prog.compiled.placeId(p)]) === null;
|
|
@@ -4792,18 +4871,18 @@ var PrecompiledNetExecutor = class {
|
|
|
4792
4871
|
}
|
|
4793
4872
|
return true;
|
|
4794
4873
|
}
|
|
4795
|
-
countMatching(pid,
|
|
4874
|
+
countMatching(pid, predicate) {
|
|
4796
4875
|
const q = this.tokenQueues[pid];
|
|
4797
4876
|
let matching = 0;
|
|
4798
4877
|
for (let i = 0; i < q.length; i++) {
|
|
4799
|
-
if (
|
|
4878
|
+
if (predicate(q[i].value)) matching++;
|
|
4800
4879
|
}
|
|
4801
4880
|
return matching;
|
|
4802
4881
|
}
|
|
4803
|
-
removeFirstMatching(pid,
|
|
4882
|
+
removeFirstMatching(pid, predicate) {
|
|
4804
4883
|
const q = this.tokenQueues[pid];
|
|
4805
4884
|
for (let i = 0; i < q.length; i++) {
|
|
4806
|
-
if (
|
|
4885
|
+
if (predicate(q[i].value)) {
|
|
4807
4886
|
return i === 0 ? q.shift() : q.splice(i, 1)[0];
|
|
4808
4887
|
}
|
|
4809
4888
|
}
|
|
@@ -4882,7 +4961,7 @@ var PrecompiledNetExecutor = class {
|
|
|
4882
4961
|
* EventStore.append on a token-removed or transition-started emit, or an error thrown while
|
|
4883
4962
|
* consuming the matched tokens — unwinds out of the orchestrator loop and kills the executor.
|
|
4884
4963
|
* The transition is instead failed and marked dirty for re-evaluation, the same treatment an
|
|
4885
|
-
* asynchronously-reported failure gets. Enablement-phase throws (a
|
|
4964
|
+
* asynchronously-reported failure gets. Enablement-phase throws (a key function that
|
|
4886
4965
|
* throws inside canEnable, before the firing) run outside this boundary and are not contained
|
|
4887
4966
|
* here.
|
|
4888
4967
|
*
|
|
@@ -4890,10 +4969,9 @@ var PrecompiledNetExecutor = class {
|
|
|
4890
4969
|
* so a throw inside that window would leave bits asserting tokens that are gone; the
|
|
4891
4970
|
* recovery re-runs updateBitmapAfterConsumption against the true queue lengths.
|
|
4892
4971
|
*
|
|
4893
|
-
*
|
|
4894
|
-
*
|
|
4895
|
-
*
|
|
4896
|
-
* deliberately no fatal-rethrow escape hatch.
|
|
4972
|
+
* Unlike the Java runtime there is no VirtualMachineError/LinkageError analogue on
|
|
4973
|
+
* the JS side, so every throw is contained here — there is deliberately no
|
|
4974
|
+
* fatal-rethrow escape hatch.
|
|
4897
4975
|
*/
|
|
4898
4976
|
fireTransitionContained(tid) {
|
|
4899
4977
|
try {
|
|
@@ -4935,12 +5013,11 @@ var PrecompiledNetExecutor = class {
|
|
|
4935
5013
|
const inputs = new TokenInput();
|
|
4936
5014
|
if (t.matchSpec) {
|
|
4937
5015
|
this.fireTransitionMatched(tid, t, inputs, consumed);
|
|
4938
|
-
} else if (prog.hasGuards[tid]) {
|
|
4939
|
-
this.fireTransitionGuarded(tid, t, inputs, consumed);
|
|
4940
5016
|
} else {
|
|
4941
5017
|
const ops = prog.consumeOps[tid];
|
|
5018
|
+
const resetOpsStart = prog.resetOpsStart[tid];
|
|
4942
5019
|
let pc = 0;
|
|
4943
|
-
while (pc <
|
|
5020
|
+
while (pc < resetOpsStart) {
|
|
4944
5021
|
const opcode = ops[pc++];
|
|
4945
5022
|
switch (opcode) {
|
|
4946
5023
|
case CONSUME_ONE: {
|
|
@@ -5010,32 +5087,30 @@ var PrecompiledNetExecutor = class {
|
|
|
5010
5087
|
}
|
|
5011
5088
|
break;
|
|
5012
5089
|
}
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
const place2 = prog.places[pid];
|
|
5016
|
-
const tokens = this.tokenQueues[pid].splice(0);
|
|
5017
|
-
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
5018
|
-
this.hasPendingResets = true;
|
|
5019
|
-
for (const token of tokens) {
|
|
5020
|
-
consumed.push(token);
|
|
5021
|
-
this.emitEvent({
|
|
5022
|
-
type: "token-removed",
|
|
5023
|
-
timestamp: Date.now(),
|
|
5024
|
-
placeName: place2.name,
|
|
5025
|
-
token
|
|
5026
|
-
});
|
|
5027
|
-
}
|
|
5028
|
-
break;
|
|
5029
|
-
}
|
|
5090
|
+
default:
|
|
5091
|
+
throw new Error(`Unknown opcode: ${opcode}`);
|
|
5030
5092
|
}
|
|
5031
5093
|
}
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5094
|
+
this.peekReadArcs(tid, inputs);
|
|
5095
|
+
while (pc < ops.length) {
|
|
5096
|
+
const opcode = ops[pc++];
|
|
5097
|
+
if (opcode !== RESET) {
|
|
5098
|
+
throw new Error(`Unknown opcode: ${opcode} (expected RESET past resetOpsStart)`);
|
|
5099
|
+
}
|
|
5100
|
+
const pid = ops[pc++];
|
|
5101
|
+
const place2 = prog.places[pid];
|
|
5102
|
+
const tokens = this.tokenQueues[pid].splice(0);
|
|
5103
|
+
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
5104
|
+
this.hasPendingResets = true;
|
|
5105
|
+
for (const token of tokens) {
|
|
5106
|
+
consumed.push(token);
|
|
5107
|
+
this.emitEvent({
|
|
5108
|
+
type: "token-removed",
|
|
5109
|
+
timestamp: Date.now(),
|
|
5110
|
+
placeName: place2.name,
|
|
5111
|
+
token
|
|
5112
|
+
});
|
|
5113
|
+
}
|
|
5039
5114
|
}
|
|
5040
5115
|
}
|
|
5041
5116
|
this.updateBitmapAfterConsumption(tid);
|
|
@@ -5132,9 +5207,8 @@ var PrecompiledNetExecutor = class {
|
|
|
5132
5207
|
}
|
|
5133
5208
|
/**
|
|
5134
5209
|
* Consumes the name-matched tokens for a ν-net join (NU-020): correlated
|
|
5135
|
-
* inputs take tokens whose projected name equals the chosen binding (
|
|
5136
|
-
*
|
|
5137
|
-
* are honoured as on the opcode path. Mirrors {@link fireTransitionGuarded}.
|
|
5210
|
+
* inputs take tokens whose projected name equals the chosen binding (NU-021);
|
|
5211
|
+
* other inputs consume FIFO. Reset arcs are honoured as on the opcode path.
|
|
5138
5212
|
*/
|
|
5139
5213
|
fireTransitionMatched(tid, t, inputs, consumed) {
|
|
5140
5214
|
const prog = this.program;
|
|
@@ -5145,8 +5219,7 @@ var PrecompiledNetExecutor = class {
|
|
|
5145
5219
|
for (const inSpec of t.inputSpecs) {
|
|
5146
5220
|
const pid = prog.compiled.placeId(inSpec.place);
|
|
5147
5221
|
const keyFn = keyForPlace(ms, inSpec.place.name);
|
|
5148
|
-
const
|
|
5149
|
-
const pred = keyFn && chosen !== null ? (v) => (baseGuard ? baseGuard(v) : true) && keyFn(v) === chosen : baseGuard;
|
|
5222
|
+
const pred = keyFn && chosen !== null ? (v) => keyFn(v) === chosen : void 0;
|
|
5150
5223
|
let toConsume;
|
|
5151
5224
|
switch (inSpec.type) {
|
|
5152
5225
|
case "one":
|
|
@@ -5173,6 +5246,7 @@ var PrecompiledNetExecutor = class {
|
|
|
5173
5246
|
});
|
|
5174
5247
|
}
|
|
5175
5248
|
}
|
|
5249
|
+
this.peekReadArcs(tid, inputs);
|
|
5176
5250
|
for (const arc of t.resets) {
|
|
5177
5251
|
const pid = prog.compiled.placeId(arc.place);
|
|
5178
5252
|
const tokens = this.tokenQueues[pid].splice(0);
|
|
@@ -5189,52 +5263,19 @@ var PrecompiledNetExecutor = class {
|
|
|
5189
5263
|
}
|
|
5190
5264
|
}
|
|
5191
5265
|
}
|
|
5192
|
-
|
|
5266
|
+
/**
|
|
5267
|
+
* Peeks each read-arc place's front token into the context inputs. Called at
|
|
5268
|
+
* the input/reset boundary of a firing (EXEC-013): after input consumption,
|
|
5269
|
+
* before reset draining — so read(p)+reset(p) observes the pre-reset token.
|
|
5270
|
+
*/
|
|
5271
|
+
peekReadArcs(tid, inputs) {
|
|
5193
5272
|
const prog = this.program;
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
break;
|
|
5201
|
-
case "exactly":
|
|
5202
|
-
toConsume = inSpec.count;
|
|
5203
|
-
break;
|
|
5204
|
-
case "all":
|
|
5205
|
-
toConsume = inSpec.guard ? this.countMatching(pid, inSpec.guard) : this.tokenQueues[pid].length;
|
|
5206
|
-
break;
|
|
5207
|
-
case "at-least":
|
|
5208
|
-
toConsume = inSpec.guard ? this.countMatching(pid, inSpec.guard) : this.tokenQueues[pid].length;
|
|
5209
|
-
break;
|
|
5210
|
-
}
|
|
5211
|
-
const guardFn = inSpec.guard;
|
|
5212
|
-
for (let i = 0; i < toConsume; i++) {
|
|
5213
|
-
const token = guardFn ? this.removeFirstMatching(pid, guardFn) : this.tokenQueues[pid].shift() ?? null;
|
|
5214
|
-
if (token === null) break;
|
|
5215
|
-
consumed.push(token);
|
|
5216
|
-
inputs.add(inSpec.place, token);
|
|
5217
|
-
this.emitEvent({
|
|
5218
|
-
type: "token-removed",
|
|
5219
|
-
timestamp: Date.now(),
|
|
5220
|
-
placeName: inSpec.place.name,
|
|
5221
|
-
token
|
|
5222
|
-
});
|
|
5223
|
-
}
|
|
5224
|
-
}
|
|
5225
|
-
for (const arc of t.resets) {
|
|
5226
|
-
const pid = prog.compiled.placeId(arc.place);
|
|
5227
|
-
const tokens = this.tokenQueues[pid].splice(0);
|
|
5228
|
-
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
5229
|
-
this.hasPendingResets = true;
|
|
5230
|
-
for (const token of tokens) {
|
|
5231
|
-
consumed.push(token);
|
|
5232
|
-
this.emitEvent({
|
|
5233
|
-
type: "token-removed",
|
|
5234
|
-
timestamp: Date.now(),
|
|
5235
|
-
placeName: arc.place.name,
|
|
5236
|
-
token
|
|
5237
|
-
});
|
|
5273
|
+
const readPids = prog.readOps[tid];
|
|
5274
|
+
for (let i = 0; i < readPids.length; i++) {
|
|
5275
|
+
const pid = readPids[i];
|
|
5276
|
+
const q = this.tokenQueues[pid];
|
|
5277
|
+
if (q.length > 0) {
|
|
5278
|
+
inputs.add(prog.places[pid], q[0]);
|
|
5238
5279
|
}
|
|
5239
5280
|
}
|
|
5240
5281
|
}
|
|
@@ -5302,12 +5343,8 @@ var PrecompiledNetExecutor = class {
|
|
|
5302
5343
|
}
|
|
5303
5344
|
const produced = [];
|
|
5304
5345
|
for (const entry of outputs.entries()) {
|
|
5305
|
-
|
|
5306
|
-
this.cacheAddToken(pid, entry.token);
|
|
5307
|
-
this.tokenQueues[pid].push(entry.token);
|
|
5346
|
+
this.produceToken(entry.place, entry.token, t.name);
|
|
5308
5347
|
produced.push(entry.token);
|
|
5309
|
-
this.setMarkingBit(pid);
|
|
5310
|
-
this.markDirty(pid);
|
|
5311
5348
|
this.emitEvent({
|
|
5312
5349
|
type: "token-added",
|
|
5313
5350
|
timestamp: Date.now(),
|
|
@@ -5342,16 +5379,11 @@ var PrecompiledNetExecutor = class {
|
|
|
5342
5379
|
processExternalEvents() {
|
|
5343
5380
|
if (this.externalQueue.length === 0) return;
|
|
5344
5381
|
if (this.closed) return;
|
|
5345
|
-
const prog = this.program;
|
|
5346
5382
|
const len = this.externalQueue.length;
|
|
5347
5383
|
for (let i = 0; i < len; i++) {
|
|
5348
5384
|
const event = this.externalQueue[i];
|
|
5349
5385
|
try {
|
|
5350
|
-
|
|
5351
|
-
this.cacheAddToken(pid, event.token);
|
|
5352
|
-
this.tokenQueues[pid].push(event.token);
|
|
5353
|
-
this.setMarkingBit(pid);
|
|
5354
|
-
this.markDirty(pid);
|
|
5386
|
+
this.produceToken(event.place, event.token, "");
|
|
5355
5387
|
this.emitEvent({
|
|
5356
5388
|
type: "token-added",
|
|
5357
5389
|
timestamp: Date.now(),
|
|
@@ -5365,6 +5397,45 @@ var PrecompiledNetExecutor = class {
|
|
|
5365
5397
|
}
|
|
5366
5398
|
this.externalQueue.length = 0;
|
|
5367
5399
|
}
|
|
5400
|
+
/**
|
|
5401
|
+
* Adds a produced or injected token: queue/bitmap/dirty for compiled places,
|
|
5402
|
+
* or the retention side map when the program does not know it (CORE-072 AC3).
|
|
5403
|
+
* `transitionName` is the producer, empty at the injection seam.
|
|
5404
|
+
*/
|
|
5405
|
+
produceToken(place2, token, transitionName) {
|
|
5406
|
+
const pid = this.program.compiled.tryPlaceId(place2);
|
|
5407
|
+
if (pid === void 0) {
|
|
5408
|
+
this.retainUnknownToken(place2, token, transitionName);
|
|
5409
|
+
return;
|
|
5410
|
+
}
|
|
5411
|
+
this.cacheAddToken(pid, token);
|
|
5412
|
+
this.tokenQueues[pid].push(token);
|
|
5413
|
+
this.setMarkingBit(pid);
|
|
5414
|
+
this.markDirty(pid);
|
|
5415
|
+
}
|
|
5416
|
+
/**
|
|
5417
|
+
* Retains a token on an undeclared place (CORE-072 AC3) and reports the place
|
|
5418
|
+
* once — the first insert is the only one that creates a map entry, so a hot
|
|
5419
|
+
* loop cannot flood (AC4, emitted as the EVT-013 log-message event).
|
|
5420
|
+
*/
|
|
5421
|
+
retainUnknownToken(place2, token, transitionName) {
|
|
5422
|
+
const retained = this.unknownPlaceTokens.get(place2.name);
|
|
5423
|
+
if (retained !== void 0) {
|
|
5424
|
+
retained.tokens.push(token);
|
|
5425
|
+
return;
|
|
5426
|
+
}
|
|
5427
|
+
this.unknownPlaceTokens.set(place2.name, { place: place2, tokens: [token] });
|
|
5428
|
+
this.emitEvent({
|
|
5429
|
+
type: "log-message",
|
|
5430
|
+
timestamp: Date.now(),
|
|
5431
|
+
transitionName,
|
|
5432
|
+
logger: "libpetri.runtime",
|
|
5433
|
+
level: "WARN",
|
|
5434
|
+
message: `unknown place '${place2.name}': tokens are retained in the marking but inert (the net declares no arc on it)`,
|
|
5435
|
+
error: null,
|
|
5436
|
+
errorMessage: null
|
|
5437
|
+
});
|
|
5438
|
+
}
|
|
5368
5439
|
drainPendingExternalEvents() {
|
|
5369
5440
|
while (this.externalQueue.length > 0) {
|
|
5370
5441
|
this.externalQueue.shift().resolve(false);
|
|
@@ -5448,6 +5519,11 @@ var PrecompiledNetExecutor = class {
|
|
|
5448
5519
|
m.addToken(place2, q[i]);
|
|
5449
5520
|
}
|
|
5450
5521
|
}
|
|
5522
|
+
for (const { place: place2, tokens } of this.unknownPlaceTokens.values()) {
|
|
5523
|
+
for (const token of tokens) {
|
|
5524
|
+
m.addToken(place2, token);
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5451
5527
|
this.marking = m;
|
|
5452
5528
|
return m;
|
|
5453
5529
|
}
|
|
@@ -5545,7 +5621,6 @@ export {
|
|
|
5545
5621
|
fork,
|
|
5546
5622
|
forwardInput,
|
|
5547
5623
|
hasDeadline,
|
|
5548
|
-
hasGuard,
|
|
5549
5624
|
immediate,
|
|
5550
5625
|
inMemoryEventStore,
|
|
5551
5626
|
inhibitorArc,
|
|
@@ -5559,7 +5634,6 @@ export {
|
|
|
5559
5634
|
matchCorrelates,
|
|
5560
5635
|
matchKey,
|
|
5561
5636
|
matchSpec,
|
|
5562
|
-
matchesGuard,
|
|
5563
5637
|
nameId,
|
|
5564
5638
|
noopEventStore,
|
|
5565
5639
|
one,
|