libpetri 2.12.1 → 3.0.0
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 +47 -136
- package/dist/{chunk-V3WTQRHC.js → chunk-5W6SVYPD.js} +170 -20
- package/dist/chunk-5W6SVYPD.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 +1 -1
- package/dist/doclet/index.js +3 -3
- package/dist/dot-exporter-3STXYK74.js +9 -0
- package/dist/{event-store--nMbAO1v.d.ts → event-store-Df_sAVQ_.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 +383 -368
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-Byc3gSgJ.d.ts → petri-net-UQBBkvLl.d.ts} +39 -36
- package/dist/verification/index.d.ts +13 -6
- package/dist/verification/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-E3ZWB645.js.map +0 -1
- package/dist/chunk-V3WTQRHC.js.map +0 -1
- package/dist/dot-exporter-SHBYMMJ3.js +0 -9
- /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,33 @@
|
|
|
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,
|
|
11
|
+
fork,
|
|
7
12
|
forwardInput,
|
|
13
|
+
isPassthrough,
|
|
8
14
|
isProven,
|
|
9
15
|
isViolated,
|
|
16
|
+
one,
|
|
10
17
|
outPlace,
|
|
18
|
+
passthrough,
|
|
19
|
+
produce,
|
|
20
|
+
requireOutputProducingActions,
|
|
21
|
+
requiredCount,
|
|
11
22
|
timeout,
|
|
12
23
|
timeoutPlace,
|
|
24
|
+
transform,
|
|
25
|
+
transformAsync,
|
|
26
|
+
transformFrom,
|
|
27
|
+
withTimeout,
|
|
13
28
|
xor,
|
|
14
29
|
xorPlaces
|
|
15
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-5W6SVYPD.js";
|
|
16
31
|
import {
|
|
17
32
|
eventTransitionName,
|
|
18
33
|
isFailureEvent
|
|
@@ -22,7 +37,7 @@ import {
|
|
|
22
37
|
matchCorrelates,
|
|
23
38
|
matchKey,
|
|
24
39
|
matchSpec
|
|
25
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-YVIPJ6KM.js";
|
|
26
41
|
import {
|
|
27
42
|
MAX_DURATION_MS,
|
|
28
43
|
deadline,
|
|
@@ -62,8 +77,8 @@ function environmentPlace(name) {
|
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
// src/core/arc.ts
|
|
65
|
-
function inputArc(place2
|
|
66
|
-
return
|
|
80
|
+
function inputArc(place2) {
|
|
81
|
+
return { type: "input", place: place2 };
|
|
67
82
|
}
|
|
68
83
|
function outputArc(place2) {
|
|
69
84
|
return { type: "output", place: place2 };
|
|
@@ -80,139 +95,12 @@ function resetArc(place2) {
|
|
|
80
95
|
function arcPlace(arc) {
|
|
81
96
|
return arc.place;
|
|
82
97
|
}
|
|
83
|
-
function hasGuard(arc) {
|
|
84
|
-
return arc.guard !== void 0;
|
|
85
|
-
}
|
|
86
|
-
function matchesGuard(arc, value) {
|
|
87
|
-
if (arc.guard === void 0) return true;
|
|
88
|
-
return arc.guard(value);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/core/in.ts
|
|
92
|
-
function one(place2, guard) {
|
|
93
|
-
return guard !== void 0 ? { type: "one", place: place2, guard } : { type: "one", place: place2 };
|
|
94
|
-
}
|
|
95
|
-
function exactly(count, place2, guard) {
|
|
96
|
-
if (count < 1) {
|
|
97
|
-
throw new Error(`count must be >= 1, got: ${count}`);
|
|
98
|
-
}
|
|
99
|
-
return guard !== void 0 ? { type: "exactly", place: place2, count, guard } : { type: "exactly", place: place2, count };
|
|
100
|
-
}
|
|
101
|
-
function all(place2, guard) {
|
|
102
|
-
return guard !== void 0 ? { type: "all", place: place2, guard } : { type: "all", place: place2 };
|
|
103
|
-
}
|
|
104
|
-
function atLeast(minimum, place2, guard) {
|
|
105
|
-
if (minimum < 1) {
|
|
106
|
-
throw new Error(`minimum must be >= 1, got: ${minimum}`);
|
|
107
|
-
}
|
|
108
|
-
return guard !== void 0 ? { type: "at-least", place: place2, minimum, guard } : { type: "at-least", place: place2, minimum };
|
|
109
|
-
}
|
|
110
|
-
function requiredCount(spec) {
|
|
111
|
-
switch (spec.type) {
|
|
112
|
-
case "one":
|
|
113
|
-
return 1;
|
|
114
|
-
case "exactly":
|
|
115
|
-
return spec.count;
|
|
116
|
-
case "all":
|
|
117
|
-
return 1;
|
|
118
|
-
case "at-least":
|
|
119
|
-
return spec.minimum;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function consumptionCount(spec, available) {
|
|
123
|
-
if (available < requiredCount(spec)) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`Cannot consume from '${spec.place.name}': available=${available}, required=${requiredCount(spec)}`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
switch (spec.type) {
|
|
129
|
-
case "one":
|
|
130
|
-
return 1;
|
|
131
|
-
case "exactly":
|
|
132
|
-
return spec.count;
|
|
133
|
-
case "all":
|
|
134
|
-
return available;
|
|
135
|
-
case "at-least":
|
|
136
|
-
return available;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
98
|
|
|
140
99
|
// src/core/name.ts
|
|
141
100
|
function nameId(value) {
|
|
142
101
|
return value;
|
|
143
102
|
}
|
|
144
103
|
|
|
145
|
-
// src/core/transition-action.ts
|
|
146
|
-
function passthrough() {
|
|
147
|
-
return PASSTHROUGH;
|
|
148
|
-
}
|
|
149
|
-
var PASSTHROUGH = async () => {
|
|
150
|
-
};
|
|
151
|
-
function transform(fn) {
|
|
152
|
-
return async (ctx) => {
|
|
153
|
-
const result = fn(ctx);
|
|
154
|
-
for (const outputPlace of ctx.outputPlaces()) {
|
|
155
|
-
ctx.output(outputPlace, result);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function fork() {
|
|
160
|
-
return transform((ctx) => {
|
|
161
|
-
const inputPlaces = ctx.inputPlaces();
|
|
162
|
-
if (inputPlaces.size !== 1) {
|
|
163
|
-
throw new Error(`Fork requires exactly 1 input place, found ${inputPlaces.size}`);
|
|
164
|
-
}
|
|
165
|
-
const inputPlace = inputPlaces.values().next().value;
|
|
166
|
-
return ctx.input(inputPlace);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
function transformFrom(inputPlace, fn) {
|
|
170
|
-
return transform((ctx) => fn(ctx.input(inputPlace)));
|
|
171
|
-
}
|
|
172
|
-
function transformAsync(fn) {
|
|
173
|
-
return async (ctx) => {
|
|
174
|
-
const result = await fn(ctx);
|
|
175
|
-
for (const outputPlace of ctx.outputPlaces()) {
|
|
176
|
-
ctx.output(outputPlace, result);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
function produce(place2, value) {
|
|
181
|
-
return async (ctx) => {
|
|
182
|
-
ctx.output(place2, value);
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
function withTimeout(action, timeoutMs, timeoutPlace2, timeoutValue) {
|
|
186
|
-
return (ctx) => {
|
|
187
|
-
return new Promise((resolve2, reject) => {
|
|
188
|
-
let completed = false;
|
|
189
|
-
const timer = setTimeout(() => {
|
|
190
|
-
if (!completed) {
|
|
191
|
-
completed = true;
|
|
192
|
-
ctx.output(timeoutPlace2, timeoutValue);
|
|
193
|
-
resolve2();
|
|
194
|
-
}
|
|
195
|
-
}, timeoutMs);
|
|
196
|
-
action(ctx).then(
|
|
197
|
-
() => {
|
|
198
|
-
if (!completed) {
|
|
199
|
-
completed = true;
|
|
200
|
-
clearTimeout(timer);
|
|
201
|
-
resolve2();
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
(err) => {
|
|
205
|
-
if (!completed) {
|
|
206
|
-
completed = true;
|
|
207
|
-
clearTimeout(timer);
|
|
208
|
-
reject(err);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
104
|
// src/core/token-output.ts
|
|
217
105
|
var TokenOutput = class {
|
|
218
106
|
_entries = [];
|
|
@@ -1063,7 +951,7 @@ function rewriteTransition(t, prefix, placeRemap) {
|
|
|
1063
951
|
function substitutePlaces(t, remap) {
|
|
1064
952
|
return rebuildWithName(t, t.name, remap);
|
|
1065
953
|
}
|
|
1066
|
-
function rebuildWithName(t, name, remap) {
|
|
954
|
+
function rebuildWithName(t, name, remap, normalizeInputs) {
|
|
1067
955
|
const builder = Transition.builder(name).timing(t.timing).priority(t.priority).action(t.action);
|
|
1068
956
|
const alias = buildPlaceAlias(t, remap);
|
|
1069
957
|
if (alias.size > 0) {
|
|
@@ -1074,7 +962,7 @@ function rebuildWithName(t, name, remap) {
|
|
|
1074
962
|
for (let i = 0; i < t.inputSpecs.length; i++) {
|
|
1075
963
|
rewrittenInputs[i] = rewriteIn(t.inputSpecs[i], remap);
|
|
1076
964
|
}
|
|
1077
|
-
builder.inputs(...rewrittenInputs);
|
|
965
|
+
builder.inputs(...normalizeInputs !== void 0 ? normalizeInputs(rewrittenInputs) : rewrittenInputs);
|
|
1078
966
|
}
|
|
1079
967
|
if (t.outputSpec !== null) {
|
|
1080
968
|
builder.outputs(rewriteOut(t.outputSpec, remap));
|
|
@@ -1134,13 +1022,13 @@ var EMPTY_ALIAS2 = /* @__PURE__ */ new Map();
|
|
|
1134
1022
|
function rewriteIn(spec, remap) {
|
|
1135
1023
|
switch (spec.type) {
|
|
1136
1024
|
case "one":
|
|
1137
|
-
return one(resolve(spec.place, remap)
|
|
1025
|
+
return one(resolve(spec.place, remap));
|
|
1138
1026
|
case "exactly":
|
|
1139
|
-
return exactly(spec.count, resolve(spec.place, remap)
|
|
1027
|
+
return exactly(spec.count, resolve(spec.place, remap));
|
|
1140
1028
|
case "all":
|
|
1141
|
-
return all(resolve(spec.place, remap)
|
|
1029
|
+
return all(resolve(spec.place, remap));
|
|
1142
1030
|
case "at-least":
|
|
1143
|
-
return atLeast(spec.minimum, resolve(spec.place, remap)
|
|
1031
|
+
return atLeast(spec.minimum, resolve(spec.place, remap));
|
|
1144
1032
|
}
|
|
1145
1033
|
}
|
|
1146
1034
|
function rewriteOut(out, remap) {
|
|
@@ -1195,7 +1083,11 @@ function mergeTransitions(caller, instance, mergedName) {
|
|
|
1195
1083
|
if (mergedAction !== void 0) {
|
|
1196
1084
|
builder.action(mergedAction);
|
|
1197
1085
|
}
|
|
1198
|
-
|
|
1086
|
+
rejectCrossSideKindConflicts(caller, instance, mergedName);
|
|
1087
|
+
const unionedInputs = normalizeInputArcs(
|
|
1088
|
+
[...caller.inputSpecs, ...instance.inputSpecs],
|
|
1089
|
+
() => `Channel composition '${mergedName}'`
|
|
1090
|
+
);
|
|
1199
1091
|
if (unionedInputs.length > 0) {
|
|
1200
1092
|
builder.inputs(...unionedInputs);
|
|
1201
1093
|
}
|
|
@@ -1326,48 +1218,128 @@ function describeTiming(t) {
|
|
|
1326
1218
|
return `Exact(atMs=${t.atMs})`;
|
|
1327
1219
|
}
|
|
1328
1220
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
return action === PASSTHROUGH_REF;
|
|
1221
|
+
function keyOfInhibitor(arc) {
|
|
1222
|
+
return `inh|${arc.place.name}`;
|
|
1332
1223
|
}
|
|
1333
|
-
function
|
|
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) {
|
|
1334
1276
|
switch (arc.type) {
|
|
1335
1277
|
case "one":
|
|
1336
|
-
return
|
|
1278
|
+
return "one()";
|
|
1337
1279
|
case "exactly":
|
|
1338
|
-
return
|
|
1280
|
+
return `exactly(${arc.count})`;
|
|
1339
1281
|
case "all":
|
|
1340
|
-
return
|
|
1282
|
+
return "all()";
|
|
1341
1283
|
case "at-least":
|
|
1342
|
-
return
|
|
1284
|
+
return `atLeast(${arc.minimum})`;
|
|
1343
1285
|
}
|
|
1344
1286
|
}
|
|
1345
|
-
function
|
|
1346
|
-
|
|
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
|
+
}
|
|
1347
1298
|
}
|
|
1348
|
-
function
|
|
1349
|
-
|
|
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;
|
|
1350
1314
|
}
|
|
1351
|
-
function
|
|
1352
|
-
return `
|
|
1315
|
+
function describeKinds(kinds) {
|
|
1316
|
+
return `[${[...kinds].sort().join(", ")}]`;
|
|
1353
1317
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
if (k === void 0) {
|
|
1359
|
-
k = String(++guardKeyCounter);
|
|
1360
|
-
GUARD_KEYS.set(g, k);
|
|
1361
|
-
}
|
|
1362
|
-
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;
|
|
1363
1322
|
}
|
|
1364
|
-
function applyFusion(transitions, fusionMap) {
|
|
1323
|
+
function applyFusion(transitions, fusionMap, seamOf) {
|
|
1324
|
+
const normalizeInputs = (inputs) => normalizeInputArcs(inputs, seamOf);
|
|
1365
1325
|
const rewritten = /* @__PURE__ */ new Set();
|
|
1366
1326
|
for (const t of transitions) {
|
|
1367
|
-
rewritten.add(
|
|
1327
|
+
rewritten.add(rebuildWithName(
|
|
1328
|
+
t,
|
|
1329
|
+
t.name,
|
|
1330
|
+
fusionMap,
|
|
1331
|
+
fusionTouchesInputs(t, fusionMap) ? normalizeInputs : void 0
|
|
1332
|
+
));
|
|
1368
1333
|
}
|
|
1369
1334
|
return rewritten;
|
|
1370
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
|
+
}
|
|
1371
1343
|
|
|
1372
1344
|
// src/verification/verification-harness.ts
|
|
1373
1345
|
function buildVerificationResult(syntheticNet, perProperty) {
|
|
@@ -1572,6 +1544,7 @@ var SubnetDef = class _SubnetDef {
|
|
|
1572
1544
|
}
|
|
1573
1545
|
}
|
|
1574
1546
|
const syntheticNet = PetriNet.builder("verify_" + this.name).compose(sut, portMappings).build();
|
|
1547
|
+
requireOutputProducingActions(syntheticNet);
|
|
1575
1548
|
const perProperty = /* @__PURE__ */ new Map();
|
|
1576
1549
|
for (const property of properties) {
|
|
1577
1550
|
const verifier = SmtVerifier.forNet(syntheticNet).property(property);
|
|
@@ -1923,6 +1896,10 @@ var PetriNet = class _PetriNet {
|
|
|
1923
1896
|
}
|
|
1924
1897
|
/**
|
|
1925
1898
|
* Creates a new PetriNet with actions bound via a resolver function.
|
|
1899
|
+
*
|
|
1900
|
+
* The resolver is called once per transition with its name. Returning `null`
|
|
1901
|
+
* defers that transition — it keeps whatever action it already carries — which
|
|
1902
|
+
* is what makes staged binding (**MOD-024** AC7) work.
|
|
1926
1903
|
*/
|
|
1927
1904
|
bindActionsWithResolver(actionResolver) {
|
|
1928
1905
|
const boundTransitions = /* @__PURE__ */ new Set();
|
|
@@ -2258,7 +2235,8 @@ var PetriNetBuilder = class _PetriNetBuilder {
|
|
|
2258
2235
|
* Map<string, Place<unknown>> convention — TypeScript Place identity is
|
|
2259
2236
|
* name-based per `runtime/compiled-net.ts`).
|
|
2260
2237
|
* 3. Walk every transition through {@link applyFusion} to rewrite arc place
|
|
2261
|
-
* references
|
|
2238
|
+
* references; input arcs colliding on a canonical place merge per
|
|
2239
|
+
* [MOD-021] (additive where summable, rejected otherwise).
|
|
2262
2240
|
* 4. Re-derive the place set from the rewritten transitions plus any
|
|
2263
2241
|
* caller-declared standalone places, dropping non-canonical members.
|
|
2264
2242
|
* Caller-declared standalone places that happen to be non-canonical
|
|
@@ -2344,7 +2322,10 @@ var PetriNetBuilder = class _PetriNetBuilder {
|
|
|
2344
2322
|
nonCanonicalNames.add(nc.name);
|
|
2345
2323
|
}
|
|
2346
2324
|
}
|
|
2347
|
-
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
|
+
});
|
|
2348
2329
|
const rebuiltPlaces = /* @__PURE__ */ new Set();
|
|
2349
2330
|
for (const p of this._places) {
|
|
2350
2331
|
if (!nonCanonicalNames.has(p.name)) {
|
|
@@ -2445,21 +2426,21 @@ var Marking = class _Marking {
|
|
|
2445
2426
|
return result;
|
|
2446
2427
|
}
|
|
2447
2428
|
/**
|
|
2448
|
-
* Removes and returns the first token whose value satisfies the
|
|
2429
|
+
* Removes and returns the first token whose value satisfies the predicate.
|
|
2449
2430
|
*
|
|
2450
|
-
* Performs a linear scan of the place's FIFO queue. If no
|
|
2451
|
-
* 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
|
|
2452
2433
|
* tokens and splices the first match out of the queue (O(n) worst case).
|
|
2453
2434
|
*/
|
|
2454
2435
|
removeFirstMatching(spec) {
|
|
2455
2436
|
const queue = this.tokens.get(spec.place.name);
|
|
2456
2437
|
if (!queue || queue.length === 0) return null;
|
|
2457
|
-
if (!spec.
|
|
2438
|
+
if (!spec.predicate) {
|
|
2458
2439
|
return queue.shift();
|
|
2459
2440
|
}
|
|
2460
2441
|
for (let i = 0; i < queue.length; i++) {
|
|
2461
2442
|
const token = queue[i];
|
|
2462
|
-
if (spec.
|
|
2443
|
+
if (spec.predicate(token.value)) {
|
|
2463
2444
|
if (i === 0) queue.shift();
|
|
2464
2445
|
else queue.splice(i, 1);
|
|
2465
2446
|
return token;
|
|
@@ -2468,27 +2449,27 @@ var Marking = class _Marking {
|
|
|
2468
2449
|
return null;
|
|
2469
2450
|
}
|
|
2470
2451
|
// ======================== Token Inspection ========================
|
|
2471
|
-
/** Check if any token
|
|
2452
|
+
/** Check if any token satisfies the predicate. */
|
|
2472
2453
|
hasMatchingToken(spec) {
|
|
2473
2454
|
const queue = this.tokens.get(spec.place.name);
|
|
2474
2455
|
if (!queue || queue.length === 0) return false;
|
|
2475
|
-
if (!spec.
|
|
2476
|
-
return queue.some((t) => spec.
|
|
2456
|
+
if (!spec.predicate) return true;
|
|
2457
|
+
return queue.some((t) => spec.predicate(t.value));
|
|
2477
2458
|
}
|
|
2478
2459
|
/**
|
|
2479
|
-
* Counts tokens in a place whose values satisfy the
|
|
2460
|
+
* Counts tokens in a place whose values satisfy the predicate.
|
|
2480
2461
|
*
|
|
2481
|
-
* If no
|
|
2482
|
-
* With a
|
|
2483
|
-
* 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.
|
|
2484
2465
|
*/
|
|
2485
2466
|
countMatching(spec) {
|
|
2486
2467
|
const queue = this.tokens.get(spec.place.name);
|
|
2487
2468
|
if (!queue || queue.length === 0) return 0;
|
|
2488
|
-
if (!spec.
|
|
2469
|
+
if (!spec.predicate) return queue.length;
|
|
2489
2470
|
let count = 0;
|
|
2490
2471
|
for (const t of queue) {
|
|
2491
|
-
if (spec.
|
|
2472
|
+
if (spec.predicate(t.value)) count++;
|
|
2492
2473
|
}
|
|
2493
2474
|
return count;
|
|
2494
2475
|
}
|
|
@@ -2547,15 +2528,15 @@ var CompiledNet = class _CompiledNet {
|
|
|
2547
2528
|
_placeToTransitions;
|
|
2548
2529
|
// Consumption place IDs per transition (input + reset places)
|
|
2549
2530
|
_consumptionPlaceIds;
|
|
2550
|
-
// Cardinality
|
|
2531
|
+
// Cardinality flags
|
|
2551
2532
|
_cardinalityChecks;
|
|
2552
|
-
_hasGuards;
|
|
2553
2533
|
// ν-net join flag: the transition carries a MatchSpec (NU-020). Precomputed
|
|
2554
2534
|
// so the hot enablement loop can skip the match check on non-ν transitions
|
|
2555
|
-
// (zero-cost gating)
|
|
2535
|
+
// (zero-cost gating).
|
|
2556
2536
|
_hasMatch;
|
|
2557
2537
|
constructor(net) {
|
|
2558
2538
|
this.net = net;
|
|
2539
|
+
requireOutputProducingActions(net);
|
|
2559
2540
|
const allPlacesSet = /* @__PURE__ */ new Map();
|
|
2560
2541
|
for (const t of net.transitions) {
|
|
2561
2542
|
for (const spec of t.inputSpecs) allPlacesSet.set(spec.place.name, spec.place);
|
|
@@ -2584,7 +2565,6 @@ var CompiledNet = class _CompiledNet {
|
|
|
2584
2565
|
this._inhibitorMask = new Array(this.transitionCount);
|
|
2585
2566
|
this._consumptionPlaceIds = new Array(this.transitionCount);
|
|
2586
2567
|
this._cardinalityChecks = new Array(this.transitionCount).fill(null);
|
|
2587
|
-
this._hasGuards = new Array(this.transitionCount).fill(false);
|
|
2588
2568
|
this._hasMatch = new Array(this.transitionCount).fill(false);
|
|
2589
2569
|
const placeToTransitionsList = new Array(this.placeCount);
|
|
2590
2570
|
for (let i = 0; i < this.placeCount; i++) {
|
|
@@ -2596,16 +2576,20 @@ var CompiledNet = class _CompiledNet {
|
|
|
2596
2576
|
const needs = new Uint32Array(this.wordCount);
|
|
2597
2577
|
const inhibitors = new Uint32Array(this.wordCount);
|
|
2598
2578
|
let needsCardinality = false;
|
|
2579
|
+
const seenInputPlaces = /* @__PURE__ */ new Set();
|
|
2599
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);
|
|
2600
2587
|
const pid = this._placeIndex.get(inSpec.place.name);
|
|
2601
2588
|
setBit(needs, pid);
|
|
2602
2589
|
placeToTransitionsList[pid].push(tid);
|
|
2603
2590
|
if (inSpec.type !== "one") {
|
|
2604
2591
|
needsCardinality = true;
|
|
2605
2592
|
}
|
|
2606
|
-
if (inSpec.guard) {
|
|
2607
|
-
this._hasGuards[tid] = true;
|
|
2608
|
-
}
|
|
2609
2593
|
}
|
|
2610
2594
|
if (needsCardinality) {
|
|
2611
2595
|
const pids = [];
|
|
@@ -2657,6 +2641,14 @@ var CompiledNet = class _CompiledNet {
|
|
|
2657
2641
|
if (id === void 0) throw new Error(`Unknown place: ${place2.name}`);
|
|
2658
2642
|
return id;
|
|
2659
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
|
+
}
|
|
2660
2652
|
transitionId(t) {
|
|
2661
2653
|
const id = this._transitionIndex.get(t);
|
|
2662
2654
|
if (id === void 0) throw new Error(`Unknown transition: ${t.name}`);
|
|
@@ -2671,9 +2663,6 @@ var CompiledNet = class _CompiledNet {
|
|
|
2671
2663
|
cardinalityCheck(tid) {
|
|
2672
2664
|
return this._cardinalityChecks[tid];
|
|
2673
2665
|
}
|
|
2674
|
-
hasGuards(tid) {
|
|
2675
|
-
return this._hasGuards[tid];
|
|
2676
|
-
}
|
|
2677
2666
|
hasMatch(tid) {
|
|
2678
2667
|
return this._hasMatch[tid];
|
|
2679
2668
|
}
|
|
@@ -2685,8 +2674,9 @@ var CompiledNet = class _CompiledNet {
|
|
|
2685
2674
|
* 2. **Inhibitor check**: verifies no inhibitor places have tokens
|
|
2686
2675
|
* via `!intersects(snapshot, inhibitorMask)`.
|
|
2687
2676
|
*
|
|
2688
|
-
* This is a necessary but not sufficient condition — cardinality and
|
|
2689
|
-
* 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.
|
|
2690
2680
|
*/
|
|
2691
2681
|
canEnableBitmap(tid, markingSnapshot) {
|
|
2692
2682
|
if (!containsAll(markingSnapshot, this._needsMask[tid])) return false;
|
|
@@ -2811,10 +2801,9 @@ function selectMatchName(perPlace, requireds) {
|
|
|
2811
2801
|
}
|
|
2812
2802
|
return bestName;
|
|
2813
2803
|
}
|
|
2814
|
-
function buildNameIndex(tokens, key
|
|
2804
|
+
function buildNameIndex(tokens, key) {
|
|
2815
2805
|
const index = /* @__PURE__ */ new Map();
|
|
2816
2806
|
for (const token of tokens) {
|
|
2817
|
-
if (guard && !guard(token.value)) continue;
|
|
2818
2807
|
const name = key(token.value);
|
|
2819
2808
|
if (name === void 0 || name === null) continue;
|
|
2820
2809
|
const ts = token.createdAt;
|
|
@@ -2844,17 +2833,11 @@ function findBinding(t, getTokens) {
|
|
|
2844
2833
|
const perPlace = [];
|
|
2845
2834
|
const requireds = [];
|
|
2846
2835
|
for (const k of ms.keys) {
|
|
2847
|
-
perPlace.push(buildNameIndex(getTokens(k.place), k.key
|
|
2836
|
+
perPlace.push(buildNameIndex(getTokens(k.place), k.key));
|
|
2848
2837
|
requireds.push(requiredFor(t, k.place.name));
|
|
2849
2838
|
}
|
|
2850
2839
|
return selectMatchName(perPlace, requireds);
|
|
2851
2840
|
}
|
|
2852
|
-
function guardFor(t, placeName) {
|
|
2853
|
-
for (const inSpec of t.inputSpecs) {
|
|
2854
|
-
if (inSpec.place.name === placeName) return inSpec.guard;
|
|
2855
|
-
}
|
|
2856
|
-
return void 0;
|
|
2857
|
-
}
|
|
2858
2841
|
var MinQueue = class {
|
|
2859
2842
|
inStack = [];
|
|
2860
2843
|
inMin = [];
|
|
@@ -2950,7 +2933,7 @@ var IncrementalMatcher = class {
|
|
|
2950
2933
|
heaped = false;
|
|
2951
2934
|
heap = new ReadyHeap();
|
|
2952
2935
|
ready = /* @__PURE__ */ new Map();
|
|
2953
|
-
/** Add one
|
|
2936
|
+
/** Add one token carrying `name` at `createdAt` to input `i`. */
|
|
2954
2937
|
add(i, name, createdAt) {
|
|
2955
2938
|
let q = this.ts[i].get(name);
|
|
2956
2939
|
if (!q) {
|
|
@@ -3112,9 +3095,7 @@ function validateOutSpec(tName, spec, producedPlaceNames) {
|
|
|
3112
3095
|
);
|
|
3113
3096
|
}
|
|
3114
3097
|
if (satisfied.length > 1) {
|
|
3115
|
-
|
|
3116
|
-
`'${tName}': XOR violation - multiple branches produced`
|
|
3117
|
-
);
|
|
3098
|
+
return resolveXorAmbiguity(tName, satisfied);
|
|
3118
3099
|
}
|
|
3119
3100
|
return satisfied[0];
|
|
3120
3101
|
}
|
|
@@ -3122,14 +3103,26 @@ function validateOutSpec(tName, spec, producedPlaceNames) {
|
|
|
3122
3103
|
return validateOutSpec(tName, spec.child, producedPlaceNames);
|
|
3123
3104
|
}
|
|
3124
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
|
+
}
|
|
3125
3117
|
function produceTimeoutOutput(context, timeoutChild) {
|
|
3126
3118
|
switch (timeoutChild.type) {
|
|
3127
3119
|
case "place":
|
|
3128
3120
|
context.outputToHarvest(timeoutChild.place, null);
|
|
3129
3121
|
break;
|
|
3130
3122
|
case "forward-input": {
|
|
3131
|
-
const value
|
|
3132
|
-
|
|
3123
|
+
for (const value of context.inputs(timeoutChild.from)) {
|
|
3124
|
+
context.outputToHarvest(timeoutChild.to, value);
|
|
3125
|
+
}
|
|
3133
3126
|
break;
|
|
3134
3127
|
}
|
|
3135
3128
|
case "and":
|
|
@@ -3170,7 +3163,7 @@ var BitmapNetExecutor = class {
|
|
|
3170
3163
|
allSamePriority;
|
|
3171
3164
|
eventStoreEnabled;
|
|
3172
3165
|
// Bitmaps (Uint32Array, direct writes)
|
|
3173
|
-
|
|
3166
|
+
markingBitmap;
|
|
3174
3167
|
dirtySet;
|
|
3175
3168
|
markingSnapBuffer;
|
|
3176
3169
|
dirtySnapBuffer;
|
|
@@ -3199,6 +3192,11 @@ var BitmapNetExecutor = class {
|
|
|
3199
3192
|
readyBuffer = [];
|
|
3200
3193
|
// Pending reset places for clock-restart detection
|
|
3201
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();
|
|
3202
3200
|
transitionInputPlaceNames;
|
|
3203
3201
|
running = false;
|
|
3204
3202
|
draining = false;
|
|
@@ -3218,7 +3216,7 @@ var BitmapNetExecutor = class {
|
|
|
3218
3216
|
}
|
|
3219
3217
|
this.startMs = performance.now();
|
|
3220
3218
|
const wordCount = this.compiled.wordCount;
|
|
3221
|
-
this.
|
|
3219
|
+
this.markingBitmap = new Uint32Array(wordCount);
|
|
3222
3220
|
this.markingSnapBuffer = new Uint32Array(wordCount);
|
|
3223
3221
|
this.firingSnapBuffer = new Uint32Array(wordCount);
|
|
3224
3222
|
const dirtyWords = this.compiled.transitionCount + BIT_MASK >>> WORD_SHIFT;
|
|
@@ -3254,6 +3252,11 @@ var BitmapNetExecutor = class {
|
|
|
3254
3252
|
for (const spec of t.inputSpecs) names.add(spec.place.name);
|
|
3255
3253
|
this.transitionInputPlaceNames.set(t, names);
|
|
3256
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
|
+
}
|
|
3257
3260
|
this.initMatchCaches();
|
|
3258
3261
|
}
|
|
3259
3262
|
/**
|
|
@@ -3312,9 +3315,7 @@ var BitmapNetExecutor = class {
|
|
|
3312
3315
|
for (let keyIdx = 0; keyIdx < ms.keys.length; keyIdx++) {
|
|
3313
3316
|
const mk = ms.keys[keyIdx];
|
|
3314
3317
|
const pid = compiled.placeId(mk.place);
|
|
3315
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
3316
3318
|
for (const token of this.marking.peekTokens(mk.place)) {
|
|
3317
|
-
if (guard && !guard(token.value)) continue;
|
|
3318
3319
|
const name = mk.key(token.value);
|
|
3319
3320
|
if (name !== void 0 && name !== null) matcher.add(keyIdx, name, token.createdAt);
|
|
3320
3321
|
}
|
|
@@ -3333,8 +3334,6 @@ var BitmapNetExecutor = class {
|
|
|
3333
3334
|
if (cache == null) continue;
|
|
3334
3335
|
const t = compiled.transition(tid);
|
|
3335
3336
|
const mk = t.matchSpec.keys[keyIdx];
|
|
3336
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
3337
|
-
if (guard && !guard(token.value)) continue;
|
|
3338
3337
|
const name = mk.key(token.value);
|
|
3339
3338
|
if (name !== void 0 && name !== null) cache.add(keyIdx, name, token.createdAt);
|
|
3340
3339
|
}
|
|
@@ -3362,7 +3361,7 @@ var BitmapNetExecutor = class {
|
|
|
3362
3361
|
netName: this.compiled.net.name,
|
|
3363
3362
|
executionId: this.executionId()
|
|
3364
3363
|
});
|
|
3365
|
-
this.
|
|
3364
|
+
this.initializeMarkingBitmap();
|
|
3366
3365
|
this.markAllDirty();
|
|
3367
3366
|
this.emitEvent({
|
|
3368
3367
|
type: "marking-snapshot",
|
|
@@ -3417,11 +3416,11 @@ var BitmapNetExecutor = class {
|
|
|
3417
3416
|
return this.inject(envPlace, tokenOf(value));
|
|
3418
3417
|
}
|
|
3419
3418
|
// ======================== Initialize ========================
|
|
3420
|
-
|
|
3419
|
+
initializeMarkingBitmap() {
|
|
3421
3420
|
for (let pid = 0; pid < this.compiled.placeCount; pid++) {
|
|
3422
3421
|
const place2 = this.compiled.place(pid);
|
|
3423
3422
|
if (this.marking.hasTokens(place2)) {
|
|
3424
|
-
setBit(this.
|
|
3423
|
+
setBit(this.markingBitmap, pid);
|
|
3425
3424
|
}
|
|
3426
3425
|
}
|
|
3427
3426
|
}
|
|
@@ -3449,7 +3448,7 @@ var BitmapNetExecutor = class {
|
|
|
3449
3448
|
updateDirtyTransitions() {
|
|
3450
3449
|
const nowMs = performance.now();
|
|
3451
3450
|
const markingSnap = this.markingSnapBuffer;
|
|
3452
|
-
markingSnap.set(this.
|
|
3451
|
+
markingSnap.set(this.markingBitmap);
|
|
3453
3452
|
const dirtyWords = this.dirtySet.length;
|
|
3454
3453
|
const dirtySnap = this.dirtySnapBuffer;
|
|
3455
3454
|
for (let w = 0; w < dirtyWords; w++) {
|
|
@@ -3534,17 +3533,6 @@ var BitmapNetExecutor = class {
|
|
|
3534
3533
|
if (this.marking.tokenCount(place2) < required) return false;
|
|
3535
3534
|
}
|
|
3536
3535
|
}
|
|
3537
|
-
if (this.compiled.hasGuards(tid)) {
|
|
3538
|
-
const t = this.compiled.transition(tid);
|
|
3539
|
-
const cache = this.matchCaches[tid];
|
|
3540
|
-
const ms = t.matchSpec;
|
|
3541
|
-
for (const spec of t.inputSpecs) {
|
|
3542
|
-
if (!spec.guard) continue;
|
|
3543
|
-
if (cache != null && ms && keyForPlace(ms, spec.place.name) !== void 0) continue;
|
|
3544
|
-
const requiredCount2 = spec.type === "one" ? 1 : spec.type === "exactly" ? spec.count : spec.type === "at-least" ? spec.minimum : 1;
|
|
3545
|
-
if (this.marking.countMatching(spec) < requiredCount2) return false;
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
3536
|
if (this.compiled.hasMatch(tid)) {
|
|
3549
3537
|
const cache = this.matchCaches[tid];
|
|
3550
3538
|
const noBinding = cache != null ? cache.best() === null : findBinding(this.compiled.transition(tid), (p) => this.marking.peekTokens(p)) === null;
|
|
@@ -3575,7 +3563,7 @@ var BitmapNetExecutor = class {
|
|
|
3575
3563
|
* Fast path for nets where all transitions are immediate and same priority.
|
|
3576
3564
|
* Skips timing checks, sorting, and snapshot buffer — just scan and fire.
|
|
3577
3565
|
*
|
|
3578
|
-
* Uses live `
|
|
3566
|
+
* Uses live `markingBitmap` instead of a snapshot. Safe because
|
|
3579
3567
|
* `updateBitmapAfterConsumption()` synchronously updates the bitmap before the next
|
|
3580
3568
|
* iteration. For equal-priority immediate transitions, tid scan order satisfies
|
|
3581
3569
|
* FIFO-by-enablement-time (all enabled in the same cycle).
|
|
@@ -3583,7 +3571,7 @@ var BitmapNetExecutor = class {
|
|
|
3583
3571
|
fireReadyImmediate() {
|
|
3584
3572
|
for (let tid = 0; tid < this.compiled.transitionCount; tid++) {
|
|
3585
3573
|
if (!this.enabledFlags[tid] || this.inFlightFlags[tid]) continue;
|
|
3586
|
-
if (this.canEnable(tid, this.
|
|
3574
|
+
if (this.canEnable(tid, this.markingBitmap)) {
|
|
3587
3575
|
this.fireTransitionContained(tid);
|
|
3588
3576
|
} else {
|
|
3589
3577
|
this.enabledFlags[tid] = 0;
|
|
@@ -3612,12 +3600,12 @@ var BitmapNetExecutor = class {
|
|
|
3612
3600
|
return a.enabledAtMs - b.enabledAtMs;
|
|
3613
3601
|
});
|
|
3614
3602
|
const freshSnap = this.firingSnapBuffer;
|
|
3615
|
-
freshSnap.set(this.
|
|
3603
|
+
freshSnap.set(this.markingBitmap);
|
|
3616
3604
|
for (const entry of ready) {
|
|
3617
3605
|
const { tid } = entry;
|
|
3618
3606
|
if (this.enabledFlags[tid] && this.canEnable(tid, freshSnap)) {
|
|
3619
3607
|
this.fireTransitionContained(tid);
|
|
3620
|
-
freshSnap.set(this.
|
|
3608
|
+
freshSnap.set(this.markingBitmap);
|
|
3621
3609
|
} else {
|
|
3622
3610
|
this.enabledFlags[tid] = 0;
|
|
3623
3611
|
this.enabledTransitionCount--;
|
|
@@ -3633,7 +3621,7 @@ var BitmapNetExecutor = class {
|
|
|
3633
3621
|
* EventStore.append on a token-removed or transition-started emit, or an error thrown while
|
|
3634
3622
|
* consuming the matched tokens — unwinds out of the orchestrator loop and kills the executor.
|
|
3635
3623
|
* The transition is instead failed and marked dirty for re-evaluation, the same treatment an
|
|
3636
|
-
* asynchronously-reported failure gets. Enablement-phase throws (a
|
|
3624
|
+
* asynchronously-reported failure gets. Enablement-phase throws (a key function that
|
|
3637
3625
|
* throws inside canEnable, before the firing) run outside this boundary and are not contained
|
|
3638
3626
|
* here.
|
|
3639
3627
|
*
|
|
@@ -3684,10 +3672,9 @@ var BitmapNetExecutor = class {
|
|
|
3684
3672
|
const keyFn = ms ? keyForPlace(ms, inSpec.place.name) : void 0;
|
|
3685
3673
|
let spec;
|
|
3686
3674
|
if (keyFn && chosen !== null) {
|
|
3687
|
-
const baseGuard = inSpec.guard;
|
|
3688
3675
|
spec = {
|
|
3689
3676
|
place: inSpec.place,
|
|
3690
|
-
|
|
3677
|
+
predicate: (v) => keyFn(v) === chosen
|
|
3691
3678
|
};
|
|
3692
3679
|
} else {
|
|
3693
3680
|
spec = inSpec;
|
|
@@ -3702,11 +3689,11 @@ var BitmapNetExecutor = class {
|
|
|
3702
3689
|
break;
|
|
3703
3690
|
case "all":
|
|
3704
3691
|
case "at-least":
|
|
3705
|
-
toConsume = spec.
|
|
3692
|
+
toConsume = spec.predicate ? this.marking.countMatching(spec) : this.marking.tokenCount(inSpec.place);
|
|
3706
3693
|
break;
|
|
3707
3694
|
}
|
|
3708
3695
|
for (let i = 0; i < toConsume; i++) {
|
|
3709
|
-
const token = spec.
|
|
3696
|
+
const token = spec.predicate ? this.marking.removeFirstMatching(spec) : this.marking.removeFirst(inSpec.place);
|
|
3710
3697
|
if (token === null) break;
|
|
3711
3698
|
consumed.push(token);
|
|
3712
3699
|
inputs.add(inSpec.place, token);
|
|
@@ -3830,7 +3817,7 @@ var BitmapNetExecutor = class {
|
|
|
3830
3817
|
for (const pid of pids) {
|
|
3831
3818
|
const place2 = this.compiled.place(pid);
|
|
3832
3819
|
if (!this.marking.hasTokens(place2)) {
|
|
3833
|
-
clearBit(this.
|
|
3820
|
+
clearBit(this.markingBitmap, pid);
|
|
3834
3821
|
}
|
|
3835
3822
|
this.markDirty(pid);
|
|
3836
3823
|
}
|
|
@@ -3872,12 +3859,16 @@ var BitmapNetExecutor = class {
|
|
|
3872
3859
|
}
|
|
3873
3860
|
const produced = [];
|
|
3874
3861
|
for (const entry of outputs.entries()) {
|
|
3875
|
-
const pid = this.compiled.
|
|
3876
|
-
this.cacheAddToken(pid, entry.token);
|
|
3862
|
+
const pid = this.compiled.tryPlaceId(entry.place);
|
|
3877
3863
|
this.marking.addToken(entry.place, entry.token);
|
|
3878
3864
|
produced.push(entry.token);
|
|
3879
|
-
|
|
3880
|
-
|
|
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
|
+
}
|
|
3881
3872
|
this.emitEvent({
|
|
3882
3873
|
type: "token-added",
|
|
3883
3874
|
timestamp: Date.now(),
|
|
@@ -3916,11 +3907,15 @@ var BitmapNetExecutor = class {
|
|
|
3916
3907
|
for (let i = 0; i < len; i++) {
|
|
3917
3908
|
const event = this.externalQueue[i];
|
|
3918
3909
|
try {
|
|
3919
|
-
const pid = this.compiled.
|
|
3920
|
-
this.cacheAddToken(pid, event.token);
|
|
3910
|
+
const pid = this.compiled.tryPlaceId(event.place);
|
|
3921
3911
|
this.marking.addToken(event.place, event.token);
|
|
3922
|
-
|
|
3923
|
-
|
|
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
|
+
}
|
|
3924
3919
|
this.emitEvent({
|
|
3925
3920
|
type: "token-added",
|
|
3926
3921
|
timestamp: Date.now(),
|
|
@@ -4051,6 +4046,25 @@ var BitmapNetExecutor = class {
|
|
|
4051
4046
|
this.wakeUp();
|
|
4052
4047
|
}
|
|
4053
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
|
+
}
|
|
4054
4068
|
emitEvent(event) {
|
|
4055
4069
|
if (this.eventStoreEnabled) {
|
|
4056
4070
|
try {
|
|
@@ -4088,6 +4102,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4088
4102
|
// ==================== Opcode Programs ====================
|
|
4089
4103
|
/** Per-transition consume opcode sequences. */
|
|
4090
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;
|
|
4091
4111
|
/** Per-transition read-arc place IDs. */
|
|
4092
4112
|
readOps;
|
|
4093
4113
|
// ==================== Enablement Masks ====================
|
|
@@ -4126,11 +4146,10 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4126
4146
|
// ==================== Reverse Index ====================
|
|
4127
4147
|
placeToTransitions;
|
|
4128
4148
|
consumptionPlaceIds;
|
|
4129
|
-
// ==================== Cardinality
|
|
4149
|
+
// ==================== Cardinality ====================
|
|
4130
4150
|
cardinalityChecks;
|
|
4131
|
-
hasGuards;
|
|
4132
4151
|
// ν-net join flag (NU-020): gates the match-binding check off the hot
|
|
4133
|
-
// enablement path for non-ν transitions
|
|
4152
|
+
// enablement path for non-ν transitions.
|
|
4134
4153
|
hasMatch;
|
|
4135
4154
|
// ==================== Global Flags ====================
|
|
4136
4155
|
allImmediate;
|
|
@@ -4207,9 +4226,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4207
4226
|
this.inhibitorSparseMasks = inhibitorSparseMasks;
|
|
4208
4227
|
const consumeOps = new Array(tc);
|
|
4209
4228
|
const readOps = new Array(tc);
|
|
4229
|
+
this.resetOpsStart = new Uint32Array(tc);
|
|
4210
4230
|
for (let tid = 0; tid < tc; tid++) {
|
|
4211
4231
|
const t = compiled.transition(tid);
|
|
4212
|
-
|
|
4232
|
+
const program = compileConsumeProgram(t, compiled);
|
|
4233
|
+
consumeOps[tid] = program.ops;
|
|
4234
|
+
this.resetOpsStart[tid] = program.resetOpsStart;
|
|
4213
4235
|
readOps[tid] = compileReadProgram(t, compiled);
|
|
4214
4236
|
}
|
|
4215
4237
|
this.consumeOps = consumeOps;
|
|
@@ -4225,15 +4247,12 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
4225
4247
|
this.placeToTransitions = placeToTransitions;
|
|
4226
4248
|
this.consumptionPlaceIds = consumptionPlaceIds;
|
|
4227
4249
|
const cardinalityChecks = new Array(tc);
|
|
4228
|
-
const hasGuards = new Array(tc);
|
|
4229
4250
|
const hasMatch = new Array(tc);
|
|
4230
4251
|
for (let tid = 0; tid < tc; tid++) {
|
|
4231
4252
|
cardinalityChecks[tid] = compiled.cardinalityCheck(tid);
|
|
4232
|
-
hasGuards[tid] = compiled.hasGuards(tid);
|
|
4233
4253
|
hasMatch[tid] = compiled.hasMatch(tid);
|
|
4234
4254
|
}
|
|
4235
4255
|
this.cardinalityChecks = cardinalityChecks;
|
|
4236
|
-
this.hasGuards = hasGuards;
|
|
4237
4256
|
this.hasMatch = hasMatch;
|
|
4238
4257
|
this.earliestMs = new Float64Array(tc);
|
|
4239
4258
|
this.latestMs = new Float64Array(tc);
|
|
@@ -4396,11 +4415,12 @@ function compileConsumeProgram(t, compiled) {
|
|
|
4396
4415
|
break;
|
|
4397
4416
|
}
|
|
4398
4417
|
}
|
|
4418
|
+
const resetOpsStart = ops.length;
|
|
4399
4419
|
for (const arc of t.resets) {
|
|
4400
4420
|
const pid = compiled.placeId(arc.place);
|
|
4401
4421
|
ops.push(RESET, pid);
|
|
4402
4422
|
}
|
|
4403
|
-
return ops;
|
|
4423
|
+
return { ops, resetOpsStart };
|
|
4404
4424
|
}
|
|
4405
4425
|
function compileReadProgram(t, compiled) {
|
|
4406
4426
|
const pids = [];
|
|
@@ -4430,6 +4450,14 @@ var PrecompiledNetExecutor = class {
|
|
|
4430
4450
|
// ==================== Token Storage ====================
|
|
4431
4451
|
/** Per-place token arrays, indexed by pid. */
|
|
4432
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();
|
|
4433
4461
|
/** Monotonic source for ν-name minting (ctx.freshName(), NU-010). */
|
|
4434
4462
|
freshNameCounter = 0;
|
|
4435
4463
|
/**
|
|
@@ -4515,7 +4543,13 @@ var PrecompiledNetExecutor = class {
|
|
|
4515
4543
|
this.tokenQueues[pid] = [];
|
|
4516
4544
|
}
|
|
4517
4545
|
for (const [place2, tokens] of initialTokens) {
|
|
4518
|
-
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
|
+
}
|
|
4519
4553
|
const q = this.tokenQueues[pid];
|
|
4520
4554
|
for (const token of tokens) {
|
|
4521
4555
|
q.push(token);
|
|
@@ -4598,9 +4632,7 @@ var PrecompiledNetExecutor = class {
|
|
|
4598
4632
|
for (let keyIdx = 0; keyIdx < ms.keys.length; keyIdx++) {
|
|
4599
4633
|
const mk = ms.keys[keyIdx];
|
|
4600
4634
|
const pid = prog.compiled.placeId(mk.place);
|
|
4601
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
4602
4635
|
for (const token of this.tokenQueues[pid]) {
|
|
4603
|
-
if (guard && !guard(token.value)) continue;
|
|
4604
4636
|
const name = mk.key(token.value);
|
|
4605
4637
|
if (name !== void 0 && name !== null) matcher.add(keyIdx, name, token.createdAt);
|
|
4606
4638
|
}
|
|
@@ -4619,8 +4651,6 @@ var PrecompiledNetExecutor = class {
|
|
|
4619
4651
|
if (cache == null) continue;
|
|
4620
4652
|
const t = prog.compiled.transition(tid);
|
|
4621
4653
|
const mk = t.matchSpec.keys[keyIdx];
|
|
4622
|
-
const guard = t.inputSpecs.find((s) => s.place.name === mk.place.name)?.guard;
|
|
4623
|
-
if (guard && !guard(token.value)) continue;
|
|
4624
4654
|
const name = mk.key(token.value);
|
|
4625
4655
|
if (name !== void 0 && name !== null) cache.add(keyIdx, name, token.createdAt);
|
|
4626
4656
|
}
|
|
@@ -4832,17 +4862,6 @@ var PrecompiledNetExecutor = class {
|
|
|
4832
4862
|
if (this.tokenQueues[pid].length < cardCheck.requiredCounts[i]) return false;
|
|
4833
4863
|
}
|
|
4834
4864
|
}
|
|
4835
|
-
if (prog.hasGuards[tid]) {
|
|
4836
|
-
const t = prog.compiled.transition(tid);
|
|
4837
|
-
const cache = this.matchCaches[tid];
|
|
4838
|
-
const ms = t.matchSpec;
|
|
4839
|
-
for (const spec of t.inputSpecs) {
|
|
4840
|
-
if (!spec.guard) continue;
|
|
4841
|
-
if (cache != null && ms && keyForPlace(ms, spec.place.name) !== void 0) continue;
|
|
4842
|
-
const required = spec.type === "one" ? 1 : spec.type === "exactly" ? spec.count : spec.type === "at-least" ? spec.minimum : 1;
|
|
4843
|
-
if (this.countMatching(prog.compiled.placeId(spec.place), spec.guard) < required) return false;
|
|
4844
|
-
}
|
|
4845
|
-
}
|
|
4846
4865
|
if (prog.hasMatch[tid]) {
|
|
4847
4866
|
const cache = this.matchCaches[tid];
|
|
4848
4867
|
const noBinding = cache != null ? cache.best() === null : findBinding(prog.compiled.transition(tid), (p) => this.tokenQueues[prog.compiled.placeId(p)]) === null;
|
|
@@ -4852,18 +4871,18 @@ var PrecompiledNetExecutor = class {
|
|
|
4852
4871
|
}
|
|
4853
4872
|
return true;
|
|
4854
4873
|
}
|
|
4855
|
-
countMatching(pid,
|
|
4874
|
+
countMatching(pid, predicate) {
|
|
4856
4875
|
const q = this.tokenQueues[pid];
|
|
4857
4876
|
let matching = 0;
|
|
4858
4877
|
for (let i = 0; i < q.length; i++) {
|
|
4859
|
-
if (
|
|
4878
|
+
if (predicate(q[i].value)) matching++;
|
|
4860
4879
|
}
|
|
4861
4880
|
return matching;
|
|
4862
4881
|
}
|
|
4863
|
-
removeFirstMatching(pid,
|
|
4882
|
+
removeFirstMatching(pid, predicate) {
|
|
4864
4883
|
const q = this.tokenQueues[pid];
|
|
4865
4884
|
for (let i = 0; i < q.length; i++) {
|
|
4866
|
-
if (
|
|
4885
|
+
if (predicate(q[i].value)) {
|
|
4867
4886
|
return i === 0 ? q.shift() : q.splice(i, 1)[0];
|
|
4868
4887
|
}
|
|
4869
4888
|
}
|
|
@@ -4942,7 +4961,7 @@ var PrecompiledNetExecutor = class {
|
|
|
4942
4961
|
* EventStore.append on a token-removed or transition-started emit, or an error thrown while
|
|
4943
4962
|
* consuming the matched tokens — unwinds out of the orchestrator loop and kills the executor.
|
|
4944
4963
|
* The transition is instead failed and marked dirty for re-evaluation, the same treatment an
|
|
4945
|
-
* asynchronously-reported failure gets. Enablement-phase throws (a
|
|
4964
|
+
* asynchronously-reported failure gets. Enablement-phase throws (a key function that
|
|
4946
4965
|
* throws inside canEnable, before the firing) run outside this boundary and are not contained
|
|
4947
4966
|
* here.
|
|
4948
4967
|
*
|
|
@@ -4950,10 +4969,9 @@ var PrecompiledNetExecutor = class {
|
|
|
4950
4969
|
* so a throw inside that window would leave bits asserting tokens that are gone; the
|
|
4951
4970
|
* recovery re-runs updateBitmapAfterConsumption against the true queue lengths.
|
|
4952
4971
|
*
|
|
4953
|
-
*
|
|
4954
|
-
*
|
|
4955
|
-
*
|
|
4956
|
-
* 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.
|
|
4957
4975
|
*/
|
|
4958
4976
|
fireTransitionContained(tid) {
|
|
4959
4977
|
try {
|
|
@@ -4995,12 +5013,11 @@ var PrecompiledNetExecutor = class {
|
|
|
4995
5013
|
const inputs = new TokenInput();
|
|
4996
5014
|
if (t.matchSpec) {
|
|
4997
5015
|
this.fireTransitionMatched(tid, t, inputs, consumed);
|
|
4998
|
-
} else if (prog.hasGuards[tid]) {
|
|
4999
|
-
this.fireTransitionGuarded(tid, t, inputs, consumed);
|
|
5000
5016
|
} else {
|
|
5001
5017
|
const ops = prog.consumeOps[tid];
|
|
5018
|
+
const resetOpsStart = prog.resetOpsStart[tid];
|
|
5002
5019
|
let pc = 0;
|
|
5003
|
-
while (pc <
|
|
5020
|
+
while (pc < resetOpsStart) {
|
|
5004
5021
|
const opcode = ops[pc++];
|
|
5005
5022
|
switch (opcode) {
|
|
5006
5023
|
case CONSUME_ONE: {
|
|
@@ -5070,32 +5087,30 @@ var PrecompiledNetExecutor = class {
|
|
|
5070
5087
|
}
|
|
5071
5088
|
break;
|
|
5072
5089
|
}
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
const place2 = prog.places[pid];
|
|
5076
|
-
const tokens = this.tokenQueues[pid].splice(0);
|
|
5077
|
-
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
5078
|
-
this.hasPendingResets = true;
|
|
5079
|
-
for (const token of tokens) {
|
|
5080
|
-
consumed.push(token);
|
|
5081
|
-
this.emitEvent({
|
|
5082
|
-
type: "token-removed",
|
|
5083
|
-
timestamp: Date.now(),
|
|
5084
|
-
placeName: place2.name,
|
|
5085
|
-
token
|
|
5086
|
-
});
|
|
5087
|
-
}
|
|
5088
|
-
break;
|
|
5089
|
-
}
|
|
5090
|
+
default:
|
|
5091
|
+
throw new Error(`Unknown opcode: ${opcode}`);
|
|
5090
5092
|
}
|
|
5091
5093
|
}
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
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
|
+
}
|
|
5099
5114
|
}
|
|
5100
5115
|
}
|
|
5101
5116
|
this.updateBitmapAfterConsumption(tid);
|
|
@@ -5192,9 +5207,8 @@ var PrecompiledNetExecutor = class {
|
|
|
5192
5207
|
}
|
|
5193
5208
|
/**
|
|
5194
5209
|
* Consumes the name-matched tokens for a ν-net join (NU-020): correlated
|
|
5195
|
-
* inputs take tokens whose projected name equals the chosen binding (
|
|
5196
|
-
*
|
|
5197
|
-
* 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.
|
|
5198
5212
|
*/
|
|
5199
5213
|
fireTransitionMatched(tid, t, inputs, consumed) {
|
|
5200
5214
|
const prog = this.program;
|
|
@@ -5205,8 +5219,7 @@ var PrecompiledNetExecutor = class {
|
|
|
5205
5219
|
for (const inSpec of t.inputSpecs) {
|
|
5206
5220
|
const pid = prog.compiled.placeId(inSpec.place);
|
|
5207
5221
|
const keyFn = keyForPlace(ms, inSpec.place.name);
|
|
5208
|
-
const
|
|
5209
|
-
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;
|
|
5210
5223
|
let toConsume;
|
|
5211
5224
|
switch (inSpec.type) {
|
|
5212
5225
|
case "one":
|
|
@@ -5233,6 +5246,7 @@ var PrecompiledNetExecutor = class {
|
|
|
5233
5246
|
});
|
|
5234
5247
|
}
|
|
5235
5248
|
}
|
|
5249
|
+
this.peekReadArcs(tid, inputs);
|
|
5236
5250
|
for (const arc of t.resets) {
|
|
5237
5251
|
const pid = prog.compiled.placeId(arc.place);
|
|
5238
5252
|
const tokens = this.tokenQueues[pid].splice(0);
|
|
@@ -5249,52 +5263,19 @@ var PrecompiledNetExecutor = class {
|
|
|
5249
5263
|
}
|
|
5250
5264
|
}
|
|
5251
5265
|
}
|
|
5252
|
-
|
|
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) {
|
|
5253
5272
|
const prog = this.program;
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
break;
|
|
5261
|
-
case "exactly":
|
|
5262
|
-
toConsume = inSpec.count;
|
|
5263
|
-
break;
|
|
5264
|
-
case "all":
|
|
5265
|
-
toConsume = inSpec.guard ? this.countMatching(pid, inSpec.guard) : this.tokenQueues[pid].length;
|
|
5266
|
-
break;
|
|
5267
|
-
case "at-least":
|
|
5268
|
-
toConsume = inSpec.guard ? this.countMatching(pid, inSpec.guard) : this.tokenQueues[pid].length;
|
|
5269
|
-
break;
|
|
5270
|
-
}
|
|
5271
|
-
const guardFn = inSpec.guard;
|
|
5272
|
-
for (let i = 0; i < toConsume; i++) {
|
|
5273
|
-
const token = guardFn ? this.removeFirstMatching(pid, guardFn) : this.tokenQueues[pid].shift() ?? null;
|
|
5274
|
-
if (token === null) break;
|
|
5275
|
-
consumed.push(token);
|
|
5276
|
-
inputs.add(inSpec.place, token);
|
|
5277
|
-
this.emitEvent({
|
|
5278
|
-
type: "token-removed",
|
|
5279
|
-
timestamp: Date.now(),
|
|
5280
|
-
placeName: inSpec.place.name,
|
|
5281
|
-
token
|
|
5282
|
-
});
|
|
5283
|
-
}
|
|
5284
|
-
}
|
|
5285
|
-
for (const arc of t.resets) {
|
|
5286
|
-
const pid = prog.compiled.placeId(arc.place);
|
|
5287
|
-
const tokens = this.tokenQueues[pid].splice(0);
|
|
5288
|
-
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
5289
|
-
this.hasPendingResets = true;
|
|
5290
|
-
for (const token of tokens) {
|
|
5291
|
-
consumed.push(token);
|
|
5292
|
-
this.emitEvent({
|
|
5293
|
-
type: "token-removed",
|
|
5294
|
-
timestamp: Date.now(),
|
|
5295
|
-
placeName: arc.place.name,
|
|
5296
|
-
token
|
|
5297
|
-
});
|
|
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]);
|
|
5298
5279
|
}
|
|
5299
5280
|
}
|
|
5300
5281
|
}
|
|
@@ -5362,12 +5343,8 @@ var PrecompiledNetExecutor = class {
|
|
|
5362
5343
|
}
|
|
5363
5344
|
const produced = [];
|
|
5364
5345
|
for (const entry of outputs.entries()) {
|
|
5365
|
-
|
|
5366
|
-
this.cacheAddToken(pid, entry.token);
|
|
5367
|
-
this.tokenQueues[pid].push(entry.token);
|
|
5346
|
+
this.produceToken(entry.place, entry.token, t.name);
|
|
5368
5347
|
produced.push(entry.token);
|
|
5369
|
-
this.setMarkingBit(pid);
|
|
5370
|
-
this.markDirty(pid);
|
|
5371
5348
|
this.emitEvent({
|
|
5372
5349
|
type: "token-added",
|
|
5373
5350
|
timestamp: Date.now(),
|
|
@@ -5402,16 +5379,11 @@ var PrecompiledNetExecutor = class {
|
|
|
5402
5379
|
processExternalEvents() {
|
|
5403
5380
|
if (this.externalQueue.length === 0) return;
|
|
5404
5381
|
if (this.closed) return;
|
|
5405
|
-
const prog = this.program;
|
|
5406
5382
|
const len = this.externalQueue.length;
|
|
5407
5383
|
for (let i = 0; i < len; i++) {
|
|
5408
5384
|
const event = this.externalQueue[i];
|
|
5409
5385
|
try {
|
|
5410
|
-
|
|
5411
|
-
this.cacheAddToken(pid, event.token);
|
|
5412
|
-
this.tokenQueues[pid].push(event.token);
|
|
5413
|
-
this.setMarkingBit(pid);
|
|
5414
|
-
this.markDirty(pid);
|
|
5386
|
+
this.produceToken(event.place, event.token, "");
|
|
5415
5387
|
this.emitEvent({
|
|
5416
5388
|
type: "token-added",
|
|
5417
5389
|
timestamp: Date.now(),
|
|
@@ -5425,6 +5397,45 @@ var PrecompiledNetExecutor = class {
|
|
|
5425
5397
|
}
|
|
5426
5398
|
this.externalQueue.length = 0;
|
|
5427
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
|
+
}
|
|
5428
5439
|
drainPendingExternalEvents() {
|
|
5429
5440
|
while (this.externalQueue.length > 0) {
|
|
5430
5441
|
this.externalQueue.shift().resolve(false);
|
|
@@ -5508,6 +5519,11 @@ var PrecompiledNetExecutor = class {
|
|
|
5508
5519
|
m.addToken(place2, q[i]);
|
|
5509
5520
|
}
|
|
5510
5521
|
}
|
|
5522
|
+
for (const { place: place2, tokens } of this.unknownPlaceTokens.values()) {
|
|
5523
|
+
for (const token of tokens) {
|
|
5524
|
+
m.addToken(place2, token);
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5511
5527
|
this.marking = m;
|
|
5512
5528
|
return m;
|
|
5513
5529
|
}
|
|
@@ -5605,20 +5621,19 @@ export {
|
|
|
5605
5621
|
fork,
|
|
5606
5622
|
forwardInput,
|
|
5607
5623
|
hasDeadline,
|
|
5608
|
-
hasGuard,
|
|
5609
5624
|
immediate,
|
|
5610
5625
|
inMemoryEventStore,
|
|
5611
5626
|
inhibitorArc,
|
|
5612
5627
|
inputArc,
|
|
5613
5628
|
intersects,
|
|
5614
5629
|
isFailureEvent,
|
|
5630
|
+
isPassthrough,
|
|
5615
5631
|
isUnit,
|
|
5616
5632
|
keyForPlace,
|
|
5617
5633
|
latest,
|
|
5618
5634
|
matchCorrelates,
|
|
5619
5635
|
matchKey,
|
|
5620
5636
|
matchSpec,
|
|
5621
|
-
matchesGuard,
|
|
5622
5637
|
nameId,
|
|
5623
5638
|
noopEventStore,
|
|
5624
5639
|
one,
|