libpetri 2.7.0 → 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 +280 -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) {
|
|
@@ -2210,6 +2280,9 @@ var PetriNetBuilder = class _PetriNetBuilder {
|
|
|
2210
2280
|
};
|
|
2211
2281
|
function rebuildWithAction(t, action) {
|
|
2212
2282
|
const builder = Transition.builder(t.name).timing(t.timing).priority(t.priority).action(action);
|
|
2283
|
+
if (t.placeAlias.size > 0) {
|
|
2284
|
+
builder.placeAlias(t.placeAlias);
|
|
2285
|
+
}
|
|
2213
2286
|
if (t.inputSpecs.length > 0) {
|
|
2214
2287
|
builder.inputs(...t.inputSpecs);
|
|
2215
2288
|
}
|
|
@@ -2384,6 +2457,10 @@ var CompiledNet = class _CompiledNet {
|
|
|
2384
2457
|
// Cardinality and guard flags
|
|
2385
2458
|
_cardinalityChecks;
|
|
2386
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;
|
|
2387
2464
|
constructor(net) {
|
|
2388
2465
|
this.net = net;
|
|
2389
2466
|
const allPlacesSet = /* @__PURE__ */ new Map();
|
|
@@ -2415,12 +2492,14 @@ var CompiledNet = class _CompiledNet {
|
|
|
2415
2492
|
this._consumptionPlaceIds = new Array(this.transitionCount);
|
|
2416
2493
|
this._cardinalityChecks = new Array(this.transitionCount).fill(null);
|
|
2417
2494
|
this._hasGuards = new Array(this.transitionCount).fill(false);
|
|
2495
|
+
this._hasMatch = new Array(this.transitionCount).fill(false);
|
|
2418
2496
|
const placeToTransitionsList = new Array(this.placeCount);
|
|
2419
2497
|
for (let i = 0; i < this.placeCount; i++) {
|
|
2420
2498
|
placeToTransitionsList[i] = [];
|
|
2421
2499
|
}
|
|
2422
2500
|
for (let tid = 0; tid < this.transitionCount; tid++) {
|
|
2423
2501
|
const t = this._transitionsById[tid];
|
|
2502
|
+
this._hasMatch[tid] = t.matchSpec !== null;
|
|
2424
2503
|
const needs = new Uint32Array(this.wordCount);
|
|
2425
2504
|
const inhibitors = new Uint32Array(this.wordCount);
|
|
2426
2505
|
let needsCardinality = false;
|
|
@@ -2502,6 +2581,9 @@ var CompiledNet = class _CompiledNet {
|
|
|
2502
2581
|
hasGuards(tid) {
|
|
2503
2582
|
return this._hasGuards[tid];
|
|
2504
2583
|
}
|
|
2584
|
+
hasMatch(tid) {
|
|
2585
|
+
return this._hasMatch[tid];
|
|
2586
|
+
}
|
|
2505
2587
|
// ==================== Enablement Check ====================
|
|
2506
2588
|
/**
|
|
2507
2589
|
* Two-phase bitmap enablement check for a transition:
|
|
@@ -2606,6 +2688,81 @@ function inMemoryEventStore() {
|
|
|
2606
2688
|
return new InMemoryEventStore();
|
|
2607
2689
|
}
|
|
2608
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
|
+
|
|
2609
2766
|
// src/runtime/out-violation-error.ts
|
|
2610
2767
|
var OutViolationError = class extends Error {
|
|
2611
2768
|
constructor(message) {
|
|
@@ -2679,6 +2836,8 @@ function produceTimeoutOutput(context, timeoutChild) {
|
|
|
2679
2836
|
var BitmapNetExecutor = class {
|
|
2680
2837
|
compiled;
|
|
2681
2838
|
marking;
|
|
2839
|
+
/** Monotonic source for ν-name minting (ctx.freshName(), NU-010). */
|
|
2840
|
+
freshNameCounter = 0;
|
|
2682
2841
|
eventStore;
|
|
2683
2842
|
environmentPlaces;
|
|
2684
2843
|
hasEnvironmentPlaces;
|
|
@@ -2977,6 +3136,12 @@ var BitmapNetExecutor = class {
|
|
|
2977
3136
|
if (this.marking.countMatching(spec) < requiredCount2) return false;
|
|
2978
3137
|
}
|
|
2979
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
|
+
}
|
|
2980
3145
|
return true;
|
|
2981
3146
|
}
|
|
2982
3147
|
hasInputFromResetPlace(t) {
|
|
@@ -3054,7 +3219,20 @@ var BitmapNetExecutor = class {
|
|
|
3054
3219
|
const t = this.compiled.transition(tid);
|
|
3055
3220
|
const inputs = new TokenInput();
|
|
3056
3221
|
const consumed = [];
|
|
3222
|
+
const ms = t.matchSpec;
|
|
3223
|
+
const chosen = ms ? findBinding(t, (p) => this.marking.peekTokens(p)) : null;
|
|
3057
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
|
+
}
|
|
3058
3236
|
let toConsume;
|
|
3059
3237
|
switch (inSpec.type) {
|
|
3060
3238
|
case "one":
|
|
@@ -3064,14 +3242,12 @@ var BitmapNetExecutor = class {
|
|
|
3064
3242
|
toConsume = inSpec.count;
|
|
3065
3243
|
break;
|
|
3066
3244
|
case "all":
|
|
3067
|
-
toConsume = inSpec.guard ? this.marking.countMatching(inSpec) : this.marking.tokenCount(inSpec.place);
|
|
3068
|
-
break;
|
|
3069
3245
|
case "at-least":
|
|
3070
|
-
toConsume =
|
|
3246
|
+
toConsume = spec.guard ? this.marking.countMatching(spec) : this.marking.tokenCount(inSpec.place);
|
|
3071
3247
|
break;
|
|
3072
3248
|
}
|
|
3073
3249
|
for (let i = 0; i < toConsume; i++) {
|
|
3074
|
-
const token =
|
|
3250
|
+
const token = spec.guard ? this.marking.removeFirstMatching(spec) : this.marking.removeFirst(inSpec.place);
|
|
3075
3251
|
if (token === null) break;
|
|
3076
3252
|
consumed.push(token);
|
|
3077
3253
|
inputs.add(inSpec.place, token);
|
|
@@ -3133,6 +3309,8 @@ var BitmapNetExecutor = class {
|
|
|
3133
3309
|
logFn,
|
|
3134
3310
|
t.placeAlias
|
|
3135
3311
|
);
|
|
3312
|
+
const freshNameBase = t.name;
|
|
3313
|
+
context.setFreshNameSupplier(() => nameId(`${freshNameBase}#${this.freshNameCounter++}`));
|
|
3136
3314
|
let actionPromise = t.action(context);
|
|
3137
3315
|
if (t.hasActionTimeout()) {
|
|
3138
3316
|
const timeoutSpec = t.actionTimeout;
|
|
@@ -3485,6 +3663,9 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
3485
3663
|
// ==================== Cardinality & Guards ====================
|
|
3486
3664
|
cardinalityChecks;
|
|
3487
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;
|
|
3488
3669
|
// ==================== Global Flags ====================
|
|
3489
3670
|
allImmediate;
|
|
3490
3671
|
allSamePriority;
|
|
@@ -3579,12 +3760,15 @@ var PrecompiledNet = class _PrecompiledNet {
|
|
|
3579
3760
|
this.consumptionPlaceIds = consumptionPlaceIds;
|
|
3580
3761
|
const cardinalityChecks = new Array(tc);
|
|
3581
3762
|
const hasGuards = new Array(tc);
|
|
3763
|
+
const hasMatch = new Array(tc);
|
|
3582
3764
|
for (let tid = 0; tid < tc; tid++) {
|
|
3583
3765
|
cardinalityChecks[tid] = compiled.cardinalityCheck(tid);
|
|
3584
3766
|
hasGuards[tid] = compiled.hasGuards(tid);
|
|
3767
|
+
hasMatch[tid] = compiled.hasMatch(tid);
|
|
3585
3768
|
}
|
|
3586
3769
|
this.cardinalityChecks = cardinalityChecks;
|
|
3587
3770
|
this.hasGuards = hasGuards;
|
|
3771
|
+
this.hasMatch = hasMatch;
|
|
3588
3772
|
this.earliestMs = new Float64Array(tc);
|
|
3589
3773
|
this.latestMs = new Float64Array(tc);
|
|
3590
3774
|
this.hasDeadline = new Uint8Array(tc);
|
|
@@ -3780,6 +3964,17 @@ var PrecompiledNetExecutor = class {
|
|
|
3780
3964
|
// ==================== Token Storage ====================
|
|
3781
3965
|
/** Per-place token arrays, indexed by pid. */
|
|
3782
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 = [];
|
|
3783
3978
|
// ==================== Marking Bitmap ====================
|
|
3784
3979
|
markingBitmap;
|
|
3785
3980
|
// ==================== Transition State ====================
|
|
@@ -4082,6 +4277,12 @@ var PrecompiledNetExecutor = class {
|
|
|
4082
4277
|
if (this.countMatching(prog.compiled.placeId(spec.place), spec.guard) < required) return false;
|
|
4083
4278
|
}
|
|
4084
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
|
+
}
|
|
4085
4286
|
return true;
|
|
4086
4287
|
}
|
|
4087
4288
|
countMatching(pid, guard) {
|
|
@@ -4171,7 +4372,9 @@ var PrecompiledNetExecutor = class {
|
|
|
4171
4372
|
const t = prog.compiled.transition(tid);
|
|
4172
4373
|
const consumed = [];
|
|
4173
4374
|
const inputs = new TokenInput();
|
|
4174
|
-
if (
|
|
4375
|
+
if (t.matchSpec) {
|
|
4376
|
+
this.fireTransitionMatched(tid, t, inputs, consumed);
|
|
4377
|
+
} else if (prog.hasGuards[tid]) {
|
|
4175
4378
|
this.fireTransitionGuarded(tid, t, inputs, consumed);
|
|
4176
4379
|
} else {
|
|
4177
4380
|
const ops = prog.consumeOps[tid];
|
|
@@ -4305,6 +4508,13 @@ var PrecompiledNetExecutor = class {
|
|
|
4305
4508
|
logFn,
|
|
4306
4509
|
t.placeAlias
|
|
4307
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);
|
|
4308
4518
|
let actionPromise = t.action(context);
|
|
4309
4519
|
if (t.hasActionTimeout()) {
|
|
4310
4520
|
const timeoutSpec = t.actionTimeout;
|
|
@@ -4358,6 +4568,63 @@ var PrecompiledNetExecutor = class {
|
|
|
4358
4568
|
this.enabledTransitionCount--;
|
|
4359
4569
|
this.enabledAtMs[tid] = -Infinity;
|
|
4360
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
|
+
}
|
|
4361
4628
|
fireTransitionGuarded(_tid, t, inputs, consumed) {
|
|
4362
4629
|
const prog = this.program;
|
|
4363
4630
|
for (const inSpec of t.inputSpecs) {
|
|
@@ -4716,8 +4983,13 @@ export {
|
|
|
4716
4983
|
intersects,
|
|
4717
4984
|
isFailureEvent,
|
|
4718
4985
|
isUnit,
|
|
4986
|
+
keyForPlace,
|
|
4719
4987
|
latest,
|
|
4988
|
+
matchCorrelates,
|
|
4989
|
+
matchKey,
|
|
4990
|
+
matchSpec,
|
|
4720
4991
|
matchesGuard,
|
|
4992
|
+
nameId,
|
|
4721
4993
|
noopEventStore,
|
|
4722
4994
|
one,
|
|
4723
4995
|
openSubnet,
|