libpetri 2.7.1 → 2.8.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/dist/chunk-E3ZWB645.js +27 -0
- package/dist/chunk-E3ZWB645.js.map +1 -0
- package/dist/{chunk-ULX3OG6H.js → chunk-JVI5HFRX.js} +12 -6
- package/dist/chunk-JVI5HFRX.js.map +1 -0
- package/dist/chunk-M3KT7BFO.js +2773 -0
- package/dist/chunk-M3KT7BFO.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/debug/index.js +2 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/doclet/index.d.ts +1 -1
- package/dist/doclet/index.js +3 -2
- package/dist/doclet/index.js.map +1 -1
- package/dist/dot-exporter-SHBYMMJ3.js +9 -0
- package/dist/{event-store-8XkpYUeU.d.ts → event-store-CAkN_ayv.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/export/index.js +2 -1
- package/dist/index.d.ts +27 -4
- package/dist/index.js +277 -8
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-D73-PO6d.d.ts → petri-net-B4wQwsUj.d.ts} +140 -3
- package/dist/verification/index.d.ts +40 -2
- package/dist/verification/index.js +9 -508
- package/dist/verification/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ETNHEAS6.js +0 -1458
- package/dist/chunk-ETNHEAS6.js.map +0 -1
- package/dist/chunk-ULX3OG6H.js.map +0 -1
- package/dist/dot-exporter-TYC6FUAD.js +0 -8
- /package/dist/{dot-exporter-TYC6FUAD.js.map → dot-exporter-SHBYMMJ3.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -12,11 +12,17 @@ import {
|
|
|
12
12
|
timeoutPlace,
|
|
13
13
|
xor,
|
|
14
14
|
xorPlaces
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-M3KT7BFO.js";
|
|
16
16
|
import {
|
|
17
17
|
eventTransitionName,
|
|
18
18
|
isFailureEvent
|
|
19
19
|
} from "./chunk-SXK2Z45Z.js";
|
|
20
|
+
import {
|
|
21
|
+
keyForPlace,
|
|
22
|
+
matchCorrelates,
|
|
23
|
+
matchKey,
|
|
24
|
+
matchSpec
|
|
25
|
+
} from "./chunk-E3ZWB645.js";
|
|
20
26
|
import {
|
|
21
27
|
MAX_DURATION_MS,
|
|
22
28
|
deadline,
|
|
@@ -131,6 +137,11 @@ function consumptionCount(spec, available) {
|
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
// src/core/name.ts
|
|
141
|
+
function nameId(value) {
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
// src/core/transition-action.ts
|
|
135
146
|
function passthrough() {
|
|
136
147
|
return PASSTHROUGH;
|
|
@@ -204,6 +215,7 @@ function withTimeout(action, timeoutMs, timeoutPlace2, timeoutValue) {
|
|
|
204
215
|
|
|
205
216
|
// src/core/transition-context.ts
|
|
206
217
|
var EMPTY_ALIAS = /* @__PURE__ */ new Map();
|
|
218
|
+
var GLOBAL_FRESH_NAME_COUNTER = 0;
|
|
207
219
|
var TransitionContext = class {
|
|
208
220
|
rawInput;
|
|
209
221
|
_rawOutput;
|
|
@@ -217,6 +229,7 @@ var TransitionContext = class {
|
|
|
217
229
|
executionCtx;
|
|
218
230
|
_logFn;
|
|
219
231
|
placeAlias;
|
|
232
|
+
_freshNameSupplier;
|
|
220
233
|
constructor(transitionName, rawInput, rawOutput, inputPlaces, readPlaces, outputPlaces, executionContext, logFn, placeAlias) {
|
|
221
234
|
this._transitionName = transitionName;
|
|
222
235
|
this.rawInput = rawInput;
|
|
@@ -358,6 +371,28 @@ var TransitionContext = class {
|
|
|
358
371
|
);
|
|
359
372
|
}
|
|
360
373
|
}
|
|
374
|
+
// ==================== ν-name minting (NU-010) ====================
|
|
375
|
+
/**
|
|
376
|
+
* @internal Installs the ν-name minter. Wired by the executor at firing time
|
|
377
|
+
* so names minted by {@link freshName} are monotonic across the run and
|
|
378
|
+
* instance-prefixed (spec NU-010, NU-030).
|
|
379
|
+
*/
|
|
380
|
+
setFreshNameSupplier(supplier) {
|
|
381
|
+
this._freshNameSupplier = supplier;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Mints a fresh ν-name (the ν-binder primitive — spec NU-010).
|
|
385
|
+
*
|
|
386
|
+
* An action calls this on the fork side to create a correlation id, then
|
|
387
|
+
* writes it into the sibling output payloads; a later join correlates those
|
|
388
|
+
* siblings via a {@link import('./match-spec.js').MatchSpec}. Uses the
|
|
389
|
+
* executor-installed minter when present; otherwise falls back to a
|
|
390
|
+
* process-global counter prefixed by the transition name.
|
|
391
|
+
*/
|
|
392
|
+
freshName() {
|
|
393
|
+
if (this._freshNameSupplier) return this._freshNameSupplier();
|
|
394
|
+
return nameId(`${this._transitionName}#${GLOBAL_FRESH_NAME_COUNTER++}`);
|
|
395
|
+
}
|
|
361
396
|
// ==================== Structure Info ====================
|
|
362
397
|
/** Returns the transition name. */
|
|
363
398
|
transitionName() {
|
|
@@ -472,6 +507,11 @@ var Transition = class {
|
|
|
472
507
|
actionTimeout;
|
|
473
508
|
action;
|
|
474
509
|
priority;
|
|
510
|
+
/**
|
|
511
|
+
* ν-net join correlation: a subset of `inputSpecs` that must be correlated by
|
|
512
|
+
* name equality on firing (spec NU-020). `null` for ordinary transitions.
|
|
513
|
+
*/
|
|
514
|
+
matchSpec;
|
|
475
515
|
/**
|
|
476
516
|
* Per-transition **declared → actual** place correspondence (per
|
|
477
517
|
* **MOD-031**), keyed by the author-original declared place **name** →
|
|
@@ -488,7 +528,7 @@ var Transition = class {
|
|
|
488
528
|
_readPlaces;
|
|
489
529
|
_outputPlaces;
|
|
490
530
|
/** @internal Use {@link Transition.builder} to create instances. */
|
|
491
|
-
constructor(key, name, inputSpecs, outputSpec, inhibitors, reads, resets, timing, action, priority, placeAlias = EMPTY_PLACE_ALIAS) {
|
|
531
|
+
constructor(key, name, inputSpecs, outputSpec, inhibitors, reads, resets, timing, action, priority, placeAlias = EMPTY_PLACE_ALIAS, matchSpec2 = null) {
|
|
492
532
|
if (key !== TRANSITION_KEY) throw new Error("Use Transition.builder() to create instances");
|
|
493
533
|
this.name = name;
|
|
494
534
|
this.inputSpecs = inputSpecs;
|
|
@@ -501,6 +541,7 @@ var Transition = class {
|
|
|
501
541
|
this.action = action;
|
|
502
542
|
this.priority = priority;
|
|
503
543
|
this.placeAlias = placeAlias.size === 0 ? EMPTY_PLACE_ALIAS : placeAlias;
|
|
544
|
+
this.matchSpec = matchSpec2;
|
|
504
545
|
const inputPlaces = /* @__PURE__ */ new Set();
|
|
505
546
|
for (const spec of inputSpecs) {
|
|
506
547
|
inputPlaces.add(spec.place);
|
|
@@ -553,6 +594,7 @@ var TransitionBuilder = class {
|
|
|
553
594
|
_action = passthrough();
|
|
554
595
|
_priority = 0;
|
|
555
596
|
_placeAlias = EMPTY_PLACE_ALIAS;
|
|
597
|
+
_matchSpec = null;
|
|
556
598
|
constructor(name) {
|
|
557
599
|
this._name = name;
|
|
558
600
|
}
|
|
@@ -617,6 +659,15 @@ var TransitionBuilder = class {
|
|
|
617
659
|
this._priority = priority;
|
|
618
660
|
return this;
|
|
619
661
|
}
|
|
662
|
+
/**
|
|
663
|
+
* Sets the ν-net join correlation spec: the named input places must be
|
|
664
|
+
* correlated by name equality on firing (spec NU-020). Every place referenced
|
|
665
|
+
* by the spec must also be declared as an input.
|
|
666
|
+
*/
|
|
667
|
+
match(spec) {
|
|
668
|
+
this._matchSpec = spec;
|
|
669
|
+
return this;
|
|
670
|
+
}
|
|
620
671
|
/**
|
|
621
672
|
* Sets the per-transition declared→actual place correspondence (per
|
|
622
673
|
* **MOD-031**). Populated by the subnet rewriter during the compose-time
|
|
@@ -638,6 +689,16 @@ var TransitionBuilder = class {
|
|
|
638
689
|
}
|
|
639
690
|
}
|
|
640
691
|
}
|
|
692
|
+
if (this._matchSpec !== null) {
|
|
693
|
+
const inputPlaceNames = new Set(this._inputSpecs.map((s) => s.place.name));
|
|
694
|
+
for (const k of this._matchSpec.keys) {
|
|
695
|
+
if (!inputPlaceNames.has(k.place.name)) {
|
|
696
|
+
throw new Error(
|
|
697
|
+
`Transition '${this._name}': MatchSpec correlates non-input place '${k.place.name}'`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
641
702
|
return new Transition(
|
|
642
703
|
TRANSITION_KEY,
|
|
643
704
|
this._name,
|
|
@@ -649,7 +710,8 @@ var TransitionBuilder = class {
|
|
|
649
710
|
this._timing,
|
|
650
711
|
this._action,
|
|
651
712
|
this._priority,
|
|
652
|
-
this._placeAlias
|
|
713
|
+
this._placeAlias,
|
|
714
|
+
this._matchSpec
|
|
653
715
|
);
|
|
654
716
|
}
|
|
655
717
|
};
|
|
@@ -967,6 +1029,14 @@ function rebuildWithName(t, name, remap) {
|
|
|
967
1029
|
for (let i = 0; i < t.resets.length; i++) {
|
|
968
1030
|
builder.reset(rewriteReset(t.resets[i], remap).place);
|
|
969
1031
|
}
|
|
1032
|
+
if (t.matchSpec !== null) {
|
|
1033
|
+
builder.match({
|
|
1034
|
+
keys: t.matchSpec.keys.map((k) => ({
|
|
1035
|
+
place: remap.get(k.place.name) ?? k.place,
|
|
1036
|
+
key: k.key
|
|
1037
|
+
}))
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
970
1040
|
return builder.build();
|
|
971
1041
|
}
|
|
972
1042
|
function buildPlaceAlias(t, remap) {
|
|
@@ -2387,6 +2457,10 @@ var CompiledNet = class _CompiledNet {
|
|
|
2387
2457
|
// Cardinality and guard flags
|
|
2388
2458
|
_cardinalityChecks;
|
|
2389
2459
|
_hasGuards;
|
|
2460
|
+
// ν-net join flag: the transition carries a MatchSpec (NU-020). Precomputed
|
|
2461
|
+
// so the hot enablement loop can skip the match check on non-ν transitions
|
|
2462
|
+
// (zero-cost gating), mirroring `_hasGuards`.
|
|
2463
|
+
_hasMatch;
|
|
2390
2464
|
constructor(net) {
|
|
2391
2465
|
this.net = net;
|
|
2392
2466
|
const allPlacesSet = /* @__PURE__ */ new Map();
|
|
@@ -2418,12 +2492,14 @@ var CompiledNet = class _CompiledNet {
|
|
|
2418
2492
|
this._consumptionPlaceIds = new Array(this.transitionCount);
|
|
2419
2493
|
this._cardinalityChecks = new Array(this.transitionCount).fill(null);
|
|
2420
2494
|
this._hasGuards = new Array(this.transitionCount).fill(false);
|
|
2495
|
+
this._hasMatch = new Array(this.transitionCount).fill(false);
|
|
2421
2496
|
const placeToTransitionsList = new Array(this.placeCount);
|
|
2422
2497
|
for (let i = 0; i < this.placeCount; i++) {
|
|
2423
2498
|
placeToTransitionsList[i] = [];
|
|
2424
2499
|
}
|
|
2425
2500
|
for (let tid = 0; tid < this.transitionCount; tid++) {
|
|
2426
2501
|
const t = this._transitionsById[tid];
|
|
2502
|
+
this._hasMatch[tid] = t.matchSpec !== null;
|
|
2427
2503
|
const needs = new Uint32Array(this.wordCount);
|
|
2428
2504
|
const inhibitors = new Uint32Array(this.wordCount);
|
|
2429
2505
|
let needsCardinality = false;
|
|
@@ -2505,6 +2581,9 @@ var CompiledNet = class _CompiledNet {
|
|
|
2505
2581
|
hasGuards(tid) {
|
|
2506
2582
|
return this._hasGuards[tid];
|
|
2507
2583
|
}
|
|
2584
|
+
hasMatch(tid) {
|
|
2585
|
+
return this._hasMatch[tid];
|
|
2586
|
+
}
|
|
2508
2587
|
// ==================== Enablement Check ====================
|
|
2509
2588
|
/**
|
|
2510
2589
|
* Two-phase bitmap enablement check for a transition:
|
|
@@ -2609,6 +2688,81 @@ function inMemoryEventStore() {
|
|
|
2609
2688
|
return new InMemoryEventStore();
|
|
2610
2689
|
}
|
|
2611
2690
|
|
|
2691
|
+
// src/runtime/match-engine.ts
|
|
2692
|
+
function selectMatchName(perPlace, requireds) {
|
|
2693
|
+
if (perPlace.length === 0) return null;
|
|
2694
|
+
let seed = 0;
|
|
2695
|
+
for (let i = 1; i < perPlace.length; i++) {
|
|
2696
|
+
if (perPlace[i].size < perPlace[seed].size) seed = i;
|
|
2697
|
+
}
|
|
2698
|
+
let bestName = null;
|
|
2699
|
+
let bestTs = 0;
|
|
2700
|
+
for (const [name, stat] of perPlace[seed]) {
|
|
2701
|
+
if (stat.count < requireds[seed]) continue;
|
|
2702
|
+
let repTs = Number.MAX_SAFE_INTEGER;
|
|
2703
|
+
let satisfied = true;
|
|
2704
|
+
for (let j = 0; j < perPlace.length; j++) {
|
|
2705
|
+
const s = perPlace[j].get(name);
|
|
2706
|
+
if (s === void 0 || s.count < requireds[j]) {
|
|
2707
|
+
satisfied = false;
|
|
2708
|
+
break;
|
|
2709
|
+
}
|
|
2710
|
+
repTs = Math.min(repTs, s.minCreatedAt);
|
|
2711
|
+
}
|
|
2712
|
+
if (!satisfied) continue;
|
|
2713
|
+
const take = bestName === null || repTs < bestTs || repTs === bestTs && name < bestName;
|
|
2714
|
+
if (take) {
|
|
2715
|
+
bestName = name;
|
|
2716
|
+
bestTs = repTs;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
return bestName;
|
|
2720
|
+
}
|
|
2721
|
+
function buildNameIndex(tokens, key, guard) {
|
|
2722
|
+
const index = /* @__PURE__ */ new Map();
|
|
2723
|
+
for (const token of tokens) {
|
|
2724
|
+
if (guard && !guard(token.value)) continue;
|
|
2725
|
+
const name = key(token.value);
|
|
2726
|
+
if (name === void 0 || name === null) continue;
|
|
2727
|
+
const ts = token.createdAt;
|
|
2728
|
+
const prev = index.get(name);
|
|
2729
|
+
if (prev) {
|
|
2730
|
+
prev.count++;
|
|
2731
|
+
if (ts < prev.minCreatedAt) prev.minCreatedAt = ts;
|
|
2732
|
+
} else {
|
|
2733
|
+
index.set(name, { count: 1, minCreatedAt: ts });
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
return index;
|
|
2737
|
+
}
|
|
2738
|
+
function requiredFor(t, placeName) {
|
|
2739
|
+
for (const inSpec of t.inputSpecs) {
|
|
2740
|
+
if (inSpec.place.name === placeName) {
|
|
2741
|
+
if (inSpec.type === "exactly") return inSpec.count;
|
|
2742
|
+
if (inSpec.type === "at-least") return inSpec.minimum;
|
|
2743
|
+
return 1;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
return 1;
|
|
2747
|
+
}
|
|
2748
|
+
function findBinding(t, getTokens) {
|
|
2749
|
+
const ms = t.matchSpec;
|
|
2750
|
+
if (!ms) return null;
|
|
2751
|
+
const perPlace = [];
|
|
2752
|
+
const requireds = [];
|
|
2753
|
+
for (const k of ms.keys) {
|
|
2754
|
+
perPlace.push(buildNameIndex(getTokens(k.place), k.key, guardFor(t, k.place.name)));
|
|
2755
|
+
requireds.push(requiredFor(t, k.place.name));
|
|
2756
|
+
}
|
|
2757
|
+
return selectMatchName(perPlace, requireds);
|
|
2758
|
+
}
|
|
2759
|
+
function guardFor(t, placeName) {
|
|
2760
|
+
for (const inSpec of t.inputSpecs) {
|
|
2761
|
+
if (inSpec.place.name === placeName) return inSpec.guard;
|
|
2762
|
+
}
|
|
2763
|
+
return void 0;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2612
2766
|
// src/runtime/out-violation-error.ts
|
|
2613
2767
|
var OutViolationError = class extends Error {
|
|
2614
2768
|
constructor(message) {
|
|
@@ -2682,6 +2836,8 @@ function produceTimeoutOutput(context, timeoutChild) {
|
|
|
2682
2836
|
var BitmapNetExecutor = class {
|
|
2683
2837
|
compiled;
|
|
2684
2838
|
marking;
|
|
2839
|
+
/** Monotonic source for ν-name minting (ctx.freshName(), NU-010). */
|
|
2840
|
+
freshNameCounter = 0;
|
|
2685
2841
|
eventStore;
|
|
2686
2842
|
environmentPlaces;
|
|
2687
2843
|
hasEnvironmentPlaces;
|
|
@@ -2980,6 +3136,12 @@ var BitmapNetExecutor = class {
|
|
|
2980
3136
|
if (this.marking.countMatching(spec) < requiredCount2) return false;
|
|
2981
3137
|
}
|
|
2982
3138
|
}
|
|
3139
|
+
if (this.compiled.hasMatch(tid)) {
|
|
3140
|
+
const tMatch = this.compiled.transition(tid);
|
|
3141
|
+
if (findBinding(tMatch, (p) => this.marking.peekTokens(p)) === null) {
|
|
3142
|
+
return false;
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
2983
3145
|
return true;
|
|
2984
3146
|
}
|
|
2985
3147
|
hasInputFromResetPlace(t) {
|
|
@@ -3057,7 +3219,20 @@ var BitmapNetExecutor = class {
|
|
|
3057
3219
|
const t = this.compiled.transition(tid);
|
|
3058
3220
|
const inputs = new TokenInput();
|
|
3059
3221
|
const consumed = [];
|
|
3222
|
+
const ms = t.matchSpec;
|
|
3223
|
+
const chosen = ms ? findBinding(t, (p) => this.marking.peekTokens(p)) : null;
|
|
3060
3224
|
for (const inSpec of t.inputSpecs) {
|
|
3225
|
+
const keyFn = ms ? keyForPlace(ms, inSpec.place.name) : void 0;
|
|
3226
|
+
let spec;
|
|
3227
|
+
if (keyFn && chosen !== null) {
|
|
3228
|
+
const baseGuard = inSpec.guard;
|
|
3229
|
+
spec = {
|
|
3230
|
+
place: inSpec.place,
|
|
3231
|
+
guard: (v) => (baseGuard ? baseGuard(v) : true) && keyFn(v) === chosen
|
|
3232
|
+
};
|
|
3233
|
+
} else {
|
|
3234
|
+
spec = inSpec;
|
|
3235
|
+
}
|
|
3061
3236
|
let toConsume;
|
|
3062
3237
|
switch (inSpec.type) {
|
|
3063
3238
|
case "one":
|
|
@@ -3067,14 +3242,12 @@ var BitmapNetExecutor = class {
|
|
|
3067
3242
|
toConsume = inSpec.count;
|
|
3068
3243
|
break;
|
|
3069
3244
|
case "all":
|
|
3070
|
-
toConsume = inSpec.guard ? this.marking.countMatching(inSpec) : this.marking.tokenCount(inSpec.place);
|
|
3071
|
-
break;
|
|
3072
3245
|
case "at-least":
|
|
3073
|
-
toConsume =
|
|
3246
|
+
toConsume = spec.guard ? this.marking.countMatching(spec) : this.marking.tokenCount(inSpec.place);
|
|
3074
3247
|
break;
|
|
3075
3248
|
}
|
|
3076
3249
|
for (let i = 0; i < toConsume; i++) {
|
|
3077
|
-
const token =
|
|
3250
|
+
const token = spec.guard ? this.marking.removeFirstMatching(spec) : this.marking.removeFirst(inSpec.place);
|
|
3078
3251
|
if (token === null) break;
|
|
3079
3252
|
consumed.push(token);
|
|
3080
3253
|
inputs.add(inSpec.place, token);
|
|
@@ -3136,6 +3309,8 @@ var BitmapNetExecutor = class {
|
|
|
3136
3309
|
logFn,
|
|
3137
3310
|
t.placeAlias
|
|
3138
3311
|
);
|
|
3312
|
+
const freshNameBase = t.name;
|
|
3313
|
+
context.setFreshNameSupplier(() => nameId(`${freshNameBase}#${this.freshNameCounter++}`));
|
|
3139
3314
|
let actionPromise = t.action(context);
|
|
3140
3315
|
if (t.hasActionTimeout()) {
|
|
3141
3316
|
const timeoutSpec = t.actionTimeout;
|
|
@@ -3488,6 +3663,9 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
3488
3663
|
// ==================== Cardinality & Guards ====================
|
|
3489
3664
|
cardinalityChecks;
|
|
3490
3665
|
hasGuards;
|
|
3666
|
+
// ν-net join flag (NU-020): gates the match-binding check off the hot
|
|
3667
|
+
// enablement path for non-ν transitions, mirroring `hasGuards`.
|
|
3668
|
+
hasMatch;
|
|
3491
3669
|
// ==================== Global Flags ====================
|
|
3492
3670
|
allImmediate;
|
|
3493
3671
|
allSamePriority;
|
|
@@ -3582,12 +3760,15 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
3582
3760
|
this.consumptionPlaceIds = consumptionPlaceIds;
|
|
3583
3761
|
const cardinalityChecks = new Array(tc);
|
|
3584
3762
|
const hasGuards = new Array(tc);
|
|
3763
|
+
const hasMatch = new Array(tc);
|
|
3585
3764
|
for (let tid = 0; tid < tc; tid++) {
|
|
3586
3765
|
cardinalityChecks[tid] = compiled.cardinalityCheck(tid);
|
|
3587
3766
|
hasGuards[tid] = compiled.hasGuards(tid);
|
|
3767
|
+
hasMatch[tid] = compiled.hasMatch(tid);
|
|
3588
3768
|
}
|
|
3589
3769
|
this.cardinalityChecks = cardinalityChecks;
|
|
3590
3770
|
this.hasGuards = hasGuards;
|
|
3771
|
+
this.hasMatch = hasMatch;
|
|
3591
3772
|
this.earliestMs = new Float64Array(tc);
|
|
3592
3773
|
this.latestMs = new Float64Array(tc);
|
|
3593
3774
|
this.hasDeadline = new Uint8Array(tc);
|
|
@@ -3783,6 +3964,17 @@ var PrecompiledNetExecutor = class {
|
|
|
3783
3964
|
// ==================== Token Storage ====================
|
|
3784
3965
|
/** Per-place token arrays, indexed by pid. */
|
|
3785
3966
|
tokenQueues;
|
|
3967
|
+
/** Monotonic source for ν-name minting (ctx.freshName(), NU-010). */
|
|
3968
|
+
freshNameCounter = 0;
|
|
3969
|
+
/**
|
|
3970
|
+
* Per-transition ν-name minter cache, indexed by tid (built lazily). Each
|
|
3971
|
+
* supplier is created once and reused across firings, so installing it on a
|
|
3972
|
+
* per-fire context is a field assignment rather than a per-fire closure
|
|
3973
|
+
* allocation — keeping the fire path lean for non-ν transitions. Every
|
|
3974
|
+
* transition gets one (forks mint but carry no MatchSpec, so gating on
|
|
3975
|
+
* `hasMatch` would wrongly starve them).
|
|
3976
|
+
*/
|
|
3977
|
+
freshNameSuppliers = [];
|
|
3786
3978
|
// ==================== Marking Bitmap ====================
|
|
3787
3979
|
markingBitmap;
|
|
3788
3980
|
// ==================== Transition State ====================
|
|
@@ -4085,6 +4277,12 @@ var PrecompiledNetExecutor = class {
|
|
|
4085
4277
|
if (this.countMatching(prog.compiled.placeId(spec.place), spec.guard) < required) return false;
|
|
4086
4278
|
}
|
|
4087
4279
|
}
|
|
4280
|
+
if (prog.hasMatch[tid]) {
|
|
4281
|
+
const tMatch = prog.compiled.transition(tid);
|
|
4282
|
+
if (findBinding(tMatch, (p) => this.tokenQueues[prog.compiled.placeId(p)]) === null) {
|
|
4283
|
+
return false;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4088
4286
|
return true;
|
|
4089
4287
|
}
|
|
4090
4288
|
countMatching(pid, guard) {
|
|
@@ -4174,7 +4372,9 @@ var PrecompiledNetExecutor = class {
|
|
|
4174
4372
|
const t = prog.compiled.transition(tid);
|
|
4175
4373
|
const consumed = [];
|
|
4176
4374
|
const inputs = new TokenInput();
|
|
4177
|
-
if (
|
|
4375
|
+
if (t.matchSpec) {
|
|
4376
|
+
this.fireTransitionMatched(tid, t, inputs, consumed);
|
|
4377
|
+
} else if (prog.hasGuards[tid]) {
|
|
4178
4378
|
this.fireTransitionGuarded(tid, t, inputs, consumed);
|
|
4179
4379
|
} else {
|
|
4180
4380
|
const ops = prog.consumeOps[tid];
|
|
@@ -4308,6 +4508,13 @@ var PrecompiledNetExecutor = class {
|
|
|
4308
4508
|
logFn,
|
|
4309
4509
|
t.placeAlias
|
|
4310
4510
|
);
|
|
4511
|
+
let freshNameSupplier = this.freshNameSuppliers[tid];
|
|
4512
|
+
if (freshNameSupplier === void 0) {
|
|
4513
|
+
const base = t.name;
|
|
4514
|
+
freshNameSupplier = () => nameId(`${base}#${this.freshNameCounter++}`);
|
|
4515
|
+
this.freshNameSuppliers[tid] = freshNameSupplier;
|
|
4516
|
+
}
|
|
4517
|
+
context.setFreshNameSupplier(freshNameSupplier);
|
|
4311
4518
|
let actionPromise = t.action(context);
|
|
4312
4519
|
if (t.hasActionTimeout()) {
|
|
4313
4520
|
const timeoutSpec = t.actionTimeout;
|
|
@@ -4361,6 +4568,63 @@ var PrecompiledNetExecutor = class {
|
|
|
4361
4568
|
this.enabledTransitionCount--;
|
|
4362
4569
|
this.enabledAtMs[tid] = -Infinity;
|
|
4363
4570
|
}
|
|
4571
|
+
/**
|
|
4572
|
+
* Consumes the name-matched tokens for a ν-net join (NU-020): correlated
|
|
4573
|
+
* inputs take tokens whose projected name equals the chosen binding (guard
|
|
4574
|
+
* first, then name equality — NU-021); other inputs consume FIFO. Reset arcs
|
|
4575
|
+
* are honoured as on the opcode path. Mirrors {@link fireTransitionGuarded}.
|
|
4576
|
+
*/
|
|
4577
|
+
fireTransitionMatched(_tid, t, inputs, consumed) {
|
|
4578
|
+
const prog = this.program;
|
|
4579
|
+
const ms = t.matchSpec;
|
|
4580
|
+
const chosen = findBinding(t, (p) => this.tokenQueues[prog.compiled.placeId(p)]);
|
|
4581
|
+
for (const inSpec of t.inputSpecs) {
|
|
4582
|
+
const pid = prog.compiled.placeId(inSpec.place);
|
|
4583
|
+
const keyFn = keyForPlace(ms, inSpec.place.name);
|
|
4584
|
+
const baseGuard = inSpec.guard;
|
|
4585
|
+
const pred = keyFn && chosen !== null ? (v) => (baseGuard ? baseGuard(v) : true) && keyFn(v) === chosen : baseGuard;
|
|
4586
|
+
let toConsume;
|
|
4587
|
+
switch (inSpec.type) {
|
|
4588
|
+
case "one":
|
|
4589
|
+
toConsume = 1;
|
|
4590
|
+
break;
|
|
4591
|
+
case "exactly":
|
|
4592
|
+
toConsume = inSpec.count;
|
|
4593
|
+
break;
|
|
4594
|
+
case "all":
|
|
4595
|
+
case "at-least":
|
|
4596
|
+
toConsume = pred ? this.countMatching(pid, pred) : this.tokenQueues[pid].length;
|
|
4597
|
+
break;
|
|
4598
|
+
}
|
|
4599
|
+
for (let i = 0; i < toConsume; i++) {
|
|
4600
|
+
const token = pred ? this.removeFirstMatching(pid, pred) : this.tokenQueues[pid].shift() ?? null;
|
|
4601
|
+
if (token === null) break;
|
|
4602
|
+
consumed.push(token);
|
|
4603
|
+
inputs.add(inSpec.place, token);
|
|
4604
|
+
this.emitEvent({
|
|
4605
|
+
type: "token-removed",
|
|
4606
|
+
timestamp: Date.now(),
|
|
4607
|
+
placeName: inSpec.place.name,
|
|
4608
|
+
token
|
|
4609
|
+
});
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
for (const arc of t.resets) {
|
|
4613
|
+
const pid = prog.compiled.placeId(arc.place);
|
|
4614
|
+
const tokens = this.tokenQueues[pid].splice(0);
|
|
4615
|
+
this.pendingResetWords[pid >>> WORD_SHIFT] |= 1 << (pid & BIT_MASK);
|
|
4616
|
+
this.hasPendingResets = true;
|
|
4617
|
+
for (const token of tokens) {
|
|
4618
|
+
consumed.push(token);
|
|
4619
|
+
this.emitEvent({
|
|
4620
|
+
type: "token-removed",
|
|
4621
|
+
timestamp: Date.now(),
|
|
4622
|
+
placeName: arc.place.name,
|
|
4623
|
+
token
|
|
4624
|
+
});
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4364
4628
|
fireTransitionGuarded(_tid, t, inputs, consumed) {
|
|
4365
4629
|
const prog = this.program;
|
|
4366
4630
|
for (const inSpec of t.inputSpecs) {
|
|
@@ -4719,8 +4983,13 @@ export {
|
|
|
4719
4983
|
intersects,
|
|
4720
4984
|
isFailureEvent,
|
|
4721
4985
|
isUnit,
|
|
4986
|
+
keyForPlace,
|
|
4722
4987
|
latest,
|
|
4988
|
+
matchCorrelates,
|
|
4989
|
+
matchKey,
|
|
4990
|
+
matchSpec,
|
|
4723
4991
|
matchesGuard,
|
|
4992
|
+
nameId,
|
|
4724
4993
|
noopEventStore,
|
|
4725
4994
|
one,
|
|
4726
4995
|
openSubnet,
|