@greenbytehq/cpn.js 0.1.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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ # Licensing Termsm
2
+
3
+ This project is dual-licensed under the MIT License and the GNU General Public License v3.0 (GPLv3). You may choose to use this project under the terms of either license.
4
+
5
+ * For the MIT License terms, see the `LICENSE-MIT` file.
6
+ * For the GPLv3 License terms, see the `LICENSE-GPL` file.
7
+
8
+ Please note: If you choose the MIT License, but utilize this project alongside its optional peer dependency `@sosml/interpreter`, your combined work will become subject to the copyleft terms of the GPLv3.
9
+
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # @greenByteHQ/cpn-semantics
2
+
3
+ [![npm](https://img.shields.io/npm/v/@greenByteHQ/cpn-semantics)](https://www.npmjs.com/package/@greenByteHQ/cpn-semantics)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ Pure **Coloured Petri Net (CPN)** simulation engine. Computes enabled bindings, fires transitions, and manages multiset token flow — with no UI, no app-specific types, and no side effects.
7
+
8
+ ```bash
9
+ npm install @greenByteHQ/cpn-semantics
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Core Types
15
+
16
+ ```ts
17
+ import type { Place, Transition, Arc, NetLike, Marking, Binding, Multiset } from '@greenByteHQ/cpn-semantics';
18
+ ```
19
+
20
+ `NetLike` is intentionally structural and map-based:
21
+
22
+ ```ts
23
+ type NetLike = {
24
+ places: ReadonlyMap<string, Place>;
25
+ transitions: ReadonlyMap<string, Transition>;
26
+ arcs: ReadonlyMap<string, Arc>;
27
+ };
28
+
29
+ type Marking = ReadonlyMap<string, Multiset>; // placeId -> token multiset
30
+ type Binding = ReadonlyMap<string, string>; // variable -> serialized token key
31
+ type Multiset = Map<string, number>; // token key -> count
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Multiset Operations
37
+
38
+ ```ts
39
+ import { msAdd, msSubtract, msScale, msContains, msIsEmpty, msEquals, EMPTY_MULTISET } from '@greenByteHQ/cpn-semantics';
40
+
41
+ const a: Multiset = new Map([['red', 2], ['blue', 1]]);
42
+ const b: Multiset = new Map([['red', 1]]);
43
+
44
+ msAdd(a, b); // Map { red→3, blue→1 }
45
+ msSubtract(a, b); // Map { red→1, blue→1 }
46
+ msScale(a, 2); // Map { red→4, blue→2 }
47
+ msContains(a, b); // true
48
+ msIsEmpty(a); // false
49
+ msEquals(a, a); // true
50
+ EMPTY_MULTISET; // Map {}
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Simulation API
56
+
57
+ ### `findEnabledBindings`
58
+
59
+ ```ts
60
+ import { findEnabledBindings } from '@greenByteHQ/cpn-semantics';
61
+
62
+ const bindings: Binding[] = findEnabledBindings(
63
+ net, // NetLike
64
+ marking, // Marking
65
+ transitionId, // string
66
+ evalExpr, // optional EvalExprFn
67
+ evalGuard, // optional EvalGuardFn
68
+ );
69
+ ```
70
+
71
+ Returns all variable bindings under which the given transition is enabled.
72
+
73
+ ### `computeEnabledSet`
74
+
75
+ ```ts
76
+ import { computeEnabledSet } from '@greenByteHQ/cpn-semantics';
77
+
78
+ const enabled: ReadonlySet<string> =
79
+ computeEnabledSet(net, marking, evalExpr, evalGuard);
80
+ ```
81
+
82
+ Returns all transition IDs that are currently fireable.
83
+
84
+ ### `fire`
85
+
86
+ ```ts
87
+ import { fire } from '@greenByteHQ/cpn-semantics';
88
+
89
+ const newMarking: Marking = await fire(
90
+ net, // NetLike
91
+ marking, // Marking (immutable — returns new copy)
92
+ transitionId, // string
93
+ binding, // Binding
94
+ evalExpr, // optional EvalExprFn
95
+ callbacks, // optional FireCallbacks
96
+ );
97
+ ```
98
+
99
+ Fires the given transition and returns the updated marking.
100
+
101
+ ---
102
+
103
+ ## SML Evaluation
104
+
105
+ SML expression and guard evaluation defaults to `@sosml/interpreter`.
106
+ `evalExprWithSosml` supports CPN multiset inscriptions such as `1\`x ++ 2\`(x + 1)` by evaluating the SML sub-expressions with SOSML.
107
+
108
+ ```ts
109
+ import { evalExprWithSosml, evalGuardWithSosml } from '@greenByteHQ/cpn-semantics';
110
+
111
+ await evalExprWithSosml('1`x ++ 2`(x + 1)', 'sml', new Map([['x', '3']]));
112
+ await evalGuardWithSosml('x + 1 = 4', 'sml', new Map([['x', '3']]));
113
+ ```
114
+
115
+ Python or other non-SML languages are still supplied by the host app through `EvalExprFn` and `EvalGuardFn`.
116
+
117
+ ---
118
+
119
+ ## Fire Callbacks
120
+
121
+ Use callbacks to observe or customize firing. The semantics package does not
122
+ interpret application-specific transition classes or arc extensions; layer those
123
+ behaviors in callbacks.
124
+
125
+ ```ts
126
+ import type { FireCallbacks } from '@greenByteHQ/cpn-semantics';
127
+
128
+ const callbacks: FireCallbacks = {
129
+ beforeFire: ({ transition, binding, marking, net }) => {
130
+ // called before input tokens are consumed
131
+ },
132
+ midFire: async ({ transition, binding, marking, markingAfterConsume, net }) => {
133
+ if (transition.id !== 'special-transition') return null;
134
+
135
+ // Return Map<placeId, Multiset> to own output-side token placement.
136
+ // null/undefined falls through to standard TP inscription evaluation.
137
+ return new Map([['p_out', new Map([['done', 1]])]]);
138
+ },
139
+ afterFire: ({ transition, binding, markingAfterConsume, markingAfterFire, net }) => {
140
+ // called after output tokens are added
141
+ },
142
+ };
143
+
144
+ await fire(net, marking, transitionId, binding, evalExpr, callbacks);
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Classical Examples
150
+
151
+ The package includes executable examples under `examples/`. They are intentionally
152
+ not exported from the package API; they show how to build and run nets with the
153
+ public primitives.
154
+
155
+ Run all examples:
156
+
157
+ ```bash
158
+ pnpm --filter @greenByteHQ/cpn-semantics run examples
159
+ ```
160
+
161
+ Or build once and run an individual example:
162
+
163
+ ```bash
164
+ pnpm --filter @greenByteHQ/cpn-semantics run build
165
+ node packages/cpn-semantics/examples/traffic-light.mjs
166
+ node packages/cpn-semantics/examples/producer-consumer.mjs
167
+ node packages/cpn-semantics/examples/sml-integration.mjs
168
+ ```
169
+
170
+ ### Traffic Light
171
+
172
+ One coloured token represents the current light state. Firing `advance` cycles
173
+ `red -> green -> yellow -> red`.
174
+
175
+ ```bash
176
+ node packages/cpn-semantics/examples/traffic-light.mjs
177
+ ```
178
+
179
+ ### Bounded Producer-Consumer
180
+
181
+ This net models a bounded buffer with `slots`, `buffer`, and `consumed` places.
182
+ `produce` consumes one free slot and creates an `item`; `consume` consumes one
183
+ `item`, restores one slot, and records the consumed item.
184
+
185
+ ```bash
186
+ node packages/cpn-semantics/examples/producer-consumer.mjs
187
+ ```
188
+
189
+ ### SML Guard + Expression
190
+
191
+ This net starts with integer tokens `2`, `3`, and `8`. The transition has an
192
+ SML guard `n mod 2 = 0`, so only even tokens are enabled. Its output inscription
193
+ is `1\`(n div 2)`, so firing computes the half of each even token.
194
+
195
+ ```bash
196
+ node packages/cpn-semantics/examples/sml-integration.mjs
197
+ ```
198
+
199
+ Each script prints JSON step output containing the fired transition, binding,
200
+ and marking snapshot after each firing.
201
+
202
+ ---
203
+
204
+ ## License
205
+
206
+ MIT © kentis — see [LICENSE](LICENSE)
@@ -0,0 +1,483 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ EMPTY_MULTISET: () => EMPTY_MULTISET,
34
+ SmlEvaluationError: () => SmlEvaluationError,
35
+ computeEnabledSet: () => computeEnabledSet,
36
+ evalExprWithSosml: () => evalExprWithSosml,
37
+ evalGuardWithSosml: () => evalGuardWithSosml,
38
+ evalSmlExpression: () => evalSmlExpression,
39
+ evalSmlGuard: () => evalSmlGuard,
40
+ findEnabledBindings: () => findEnabledBindings,
41
+ fire: () => fire,
42
+ msAdd: () => msAdd,
43
+ msContains: () => msContains,
44
+ msEquals: () => msEquals,
45
+ msIsEmpty: () => msIsEmpty,
46
+ msScale: () => msScale,
47
+ msSubtract: () => msSubtract
48
+ });
49
+ module.exports = __toCommonJS(index_exports);
50
+
51
+ // src/multiset.ts
52
+ var EMPTY_MULTISET = /* @__PURE__ */ new Map();
53
+ function msAdd(a, b) {
54
+ const result = new Map(a);
55
+ for (const [k, v] of b) {
56
+ result.set(k, (result.get(k) ?? 0) + v);
57
+ }
58
+ return result;
59
+ }
60
+ function msSubtract(a, b) {
61
+ const result = new Map(a);
62
+ for (const [k, v] of b) {
63
+ const have = result.get(k) ?? 0;
64
+ const count = have - v;
65
+ if (count < 0) {
66
+ throw new Error(`Multiset underflow for token "${k}": need ${v}, have ${have}`);
67
+ }
68
+ if (count === 0) {
69
+ result.delete(k);
70
+ } else {
71
+ result.set(k, count);
72
+ }
73
+ }
74
+ return result;
75
+ }
76
+ function msScale(ms, n) {
77
+ if (!Number.isInteger(n) || n < 0) {
78
+ throw new RangeError(`msScale: n must be a non-negative integer, got ${n}`);
79
+ }
80
+ if (n === 0) return EMPTY_MULTISET;
81
+ const result = /* @__PURE__ */ new Map();
82
+ for (const [k, v] of ms) {
83
+ result.set(k, v * n);
84
+ }
85
+ return result;
86
+ }
87
+ function msContains(a, b) {
88
+ for (const [k, v] of b) {
89
+ if ((a.get(k) ?? 0) < v) return false;
90
+ }
91
+ return true;
92
+ }
93
+ function msIsEmpty(ms) {
94
+ return ms.size === 0;
95
+ }
96
+ function msEquals(a, b) {
97
+ if (a.size !== b.size) return false;
98
+ for (const [k, v] of a) {
99
+ if (b.get(k) !== v) return false;
100
+ }
101
+ return true;
102
+ }
103
+
104
+ // src/sml.ts
105
+ var import_interpreter = __toESM(require("@sosml/interpreter"), 1);
106
+ var { getFirstState, interpret } = import_interpreter.default;
107
+ var SmlEvaluationError = class extends Error {
108
+ constructor(message) {
109
+ super(message);
110
+ this.name = "SmlEvaluationError";
111
+ }
112
+ };
113
+ var RESULT_NAME = "cpnResult";
114
+ function escapeSmlString(value) {
115
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
116
+ }
117
+ function tokenKeyToSmlLiteral(value) {
118
+ if (value === "true" || value === "false") return value;
119
+ if (value === "()") return "()";
120
+ if (/^-?\d+$/.test(value)) return value.replace(/^-/, "~");
121
+ if (/^\(.*\)$/.test(value)) {
122
+ return value.replace(/(^|[(,])-?\d+/g, (match) => match.replace("-", "~"));
123
+ }
124
+ return escapeSmlString(value);
125
+ }
126
+ function bindingPrelude(binding) {
127
+ return [...binding].map(([name, value]) => `val ${name} = ${tokenKeyToSmlLiteral(value)};`).join("\n");
128
+ }
129
+ function evaluateValue(expr, binding) {
130
+ const source = `${bindingPrelude(binding)}
131
+ val ${RESULT_NAME} = ${expr};`;
132
+ const result = interpret(source, getFirstState(), { allowSuccessorML: true, allowVector: true });
133
+ if (result.evaluationErrored) {
134
+ throw new SmlEvaluationError(String(result.error ?? "SML evaluation failed"));
135
+ }
136
+ const bindingValue = result.state.getDynamicValue(RESULT_NAME)?.[0];
137
+ if (!bindingValue) {
138
+ throw new SmlEvaluationError("SML evaluation did not produce a result");
139
+ }
140
+ return { value: bindingValue, state: result.state };
141
+ }
142
+ function unquoteSmlString(value) {
143
+ return value.slice(1, -1).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
144
+ }
145
+ function valueToTokenKey(value, state) {
146
+ const typed = value;
147
+ switch (value.typeName()) {
148
+ case "Integer":
149
+ return String(typed.value);
150
+ case "BoolValue":
151
+ return String(typed.value);
152
+ case "StringValue":
153
+ return typeof typed.value === "string" ? typed.value : unquoteSmlString(value.toString(state));
154
+ case "RecordValue": {
155
+ const entries = typed.entries;
156
+ if (!entries || entries.size === 0) return "()";
157
+ const tupleItems = [];
158
+ for (let i = 1; i <= entries.size; i += 1) {
159
+ const item = entries.get(String(i));
160
+ if (!item) break;
161
+ tupleItems.push(valueToTokenKey(item, state));
162
+ }
163
+ if (tupleItems.length === entries.size && tupleItems.length > 0) {
164
+ return `(${tupleItems.join(",")})`;
165
+ }
166
+ break;
167
+ }
168
+ default:
169
+ break;
170
+ }
171
+ return value.toString(state);
172
+ }
173
+ function valueToBoolean(value) {
174
+ if (value.typeName() !== "BoolValue") {
175
+ throw new SmlEvaluationError(`Guard must evaluate to bool, got ${value.typeName()}`);
176
+ }
177
+ return Boolean(value.value);
178
+ }
179
+ function addToken(result, key, count) {
180
+ if (!Number.isInteger(count) || count < 0) {
181
+ throw new SmlEvaluationError(`Multiset count must be a non-negative integer, got ${count}`);
182
+ }
183
+ if (count === 0) return;
184
+ result.set(key, (result.get(key) ?? 0) + count);
185
+ }
186
+ function splitTopLevelUnion(expr) {
187
+ const parts = [];
188
+ let start = 0;
189
+ let depth = 0;
190
+ let inString = false;
191
+ let escaped = false;
192
+ for (let i = 0; i < expr.length; i += 1) {
193
+ const ch = expr[i];
194
+ if (inString) {
195
+ if (escaped) escaped = false;
196
+ else if (ch === "\\") escaped = true;
197
+ else if (ch === '"') inString = false;
198
+ continue;
199
+ }
200
+ if (ch === '"') {
201
+ inString = true;
202
+ continue;
203
+ }
204
+ if (ch === "(" || ch === "[" || ch === "{") depth += 1;
205
+ else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
206
+ else if (ch === "+" && expr[i + 1] === "+" && depth === 0) {
207
+ parts.push(expr.slice(start, i).trim());
208
+ start = i + 2;
209
+ i += 1;
210
+ }
211
+ }
212
+ parts.push(expr.slice(start).trim());
213
+ return parts.filter(Boolean);
214
+ }
215
+ function splitMultisetTerm(term) {
216
+ let depth = 0;
217
+ let inString = false;
218
+ let escaped = false;
219
+ for (let i = 0; i < term.length; i += 1) {
220
+ const ch = term[i];
221
+ if (inString) {
222
+ if (escaped) escaped = false;
223
+ else if (ch === "\\") escaped = true;
224
+ else if (ch === '"') inString = false;
225
+ continue;
226
+ }
227
+ if (ch === '"') {
228
+ inString = true;
229
+ continue;
230
+ }
231
+ if (ch === "(" || ch === "[" || ch === "{") depth += 1;
232
+ else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
233
+ else if (ch === "`" && depth === 0) {
234
+ return { countExpr: term.slice(0, i).trim(), valueExpr: term.slice(i + 1).trim() };
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+ async function evalSmlExpression(expr, binding) {
240
+ const trimmed = expr.trim();
241
+ if (!trimmed || trimmed === "empty") return /* @__PURE__ */ new Map();
242
+ const result = /* @__PURE__ */ new Map();
243
+ for (const part of splitTopLevelUnion(trimmed)) {
244
+ const term = splitMultisetTerm(part);
245
+ if (!term) {
246
+ const { value, state } = evaluateValue(part, binding);
247
+ addToken(result, valueToTokenKey(value, state), 1);
248
+ continue;
249
+ }
250
+ const countResult = evaluateValue(term.countExpr, binding);
251
+ if (countResult.value.typeName() !== "Integer") {
252
+ throw new SmlEvaluationError(`Multiset count must evaluate to int, got ${countResult.value.typeName()}`);
253
+ }
254
+ const valueResult = evaluateValue(term.valueExpr, binding);
255
+ addToken(
256
+ result,
257
+ valueToTokenKey(valueResult.value, valueResult.state),
258
+ Number(countResult.value.value)
259
+ );
260
+ }
261
+ return result;
262
+ }
263
+ async function evalSmlGuard(expr, binding) {
264
+ if (!expr.trim()) return true;
265
+ return valueToBoolean(evaluateValue(expr, binding).value);
266
+ }
267
+
268
+ // src/engine.ts
269
+ var evalExprWithSosml = async (expr, lang, binding) => {
270
+ if (lang !== "sml") {
271
+ throw new Error(`No ${lang} expression evaluator is configured in @greenByteHQ/cpn-semantics`);
272
+ }
273
+ return evalSmlExpression(expr, binding);
274
+ };
275
+ var evalGuardWithSosml = async (expr, lang, binding) => {
276
+ switch (lang) {
277
+ case "sml":
278
+ return evalSmlGuard(expr, binding);
279
+ case "python":
280
+ throw new Error(`No ${lang} guard evaluator is configured in @greenByteHQ/cpn-semantics`);
281
+ case "js":
282
+ throw new Error(`No ${lang} guard evaluator is configured in @greenByteHQ/cpn-semantics`);
283
+ default:
284
+ throw new Error(`Unknown language ${lang} in guard expression`);
285
+ }
286
+ };
287
+ function extractVariables(expr) {
288
+ const KEYWORDS = /* @__PURE__ */ new Set([
289
+ "let",
290
+ "val",
291
+ "in",
292
+ "end",
293
+ "if",
294
+ "then",
295
+ "else",
296
+ "andalso",
297
+ "orelse",
298
+ "not",
299
+ "div",
300
+ "mod",
301
+ "true",
302
+ "false",
303
+ "empty"
304
+ ]);
305
+ const matches = expr.match(/[a-zA-Z_][a-zA-Z0-9_']*/g) ?? [];
306
+ return [...new Set(matches)].filter((m) => !KEYWORDS.has(m));
307
+ }
308
+ function buildArcCandidates(arc, marking) {
309
+ const sourceMarking = marking.get(arc.sourceId) ?? EMPTY_MULTISET;
310
+ const tokenKeys = [...sourceMarking.keys()];
311
+ if (arc.inscription.trim() === "") {
312
+ return [/* @__PURE__ */ new Map()];
313
+ }
314
+ const vars = extractVariables(arc.inscription);
315
+ if (vars.length === 0) {
316
+ return [/* @__PURE__ */ new Map()];
317
+ }
318
+ if (vars.length === 1) {
319
+ return tokenKeys.map((key) => /* @__PURE__ */ new Map([[vars[0], key]]));
320
+ }
321
+ let candidates = [/* @__PURE__ */ new Map()];
322
+ for (const varName of vars) {
323
+ const next = [];
324
+ for (const candidate of candidates) {
325
+ for (const key of tokenKeys) {
326
+ if (candidate.has(varName) && candidate.get(varName) !== key) continue;
327
+ const extended = new Map(candidate);
328
+ extended.set(varName, key);
329
+ next.push(extended);
330
+ }
331
+ }
332
+ candidates = next;
333
+ }
334
+ return candidates;
335
+ }
336
+ function mergeBindings(perArcCandidates) {
337
+ let combined = [/* @__PURE__ */ new Map()];
338
+ for (const arcCandidates of perArcCandidates) {
339
+ const next = [];
340
+ for (const existing of combined) {
341
+ for (const arcCandidate of arcCandidates) {
342
+ let consistent = true;
343
+ for (const [k, v] of arcCandidate) {
344
+ if (existing.has(k) && existing.get(k) !== v) {
345
+ consistent = false;
346
+ break;
347
+ }
348
+ }
349
+ if (!consistent) continue;
350
+ next.push(new Map([...existing, ...arcCandidate]));
351
+ }
352
+ }
353
+ combined = next;
354
+ }
355
+ return combined;
356
+ }
357
+ async function findEnabledBindings(net, marking, transitionId, evalExpr = evalExprWithSosml, evalGuard = evalGuardWithSosml) {
358
+ const transition = net.transitions.get(transitionId);
359
+ if (!transition) return [];
360
+ const ptArcs = [...net.arcs.values()].filter(
361
+ (a) => a.kind === "PT" && a.targetId === transitionId
362
+ );
363
+ if (ptArcs.length === 0) {
364
+ const b = /* @__PURE__ */ new Map();
365
+ if (transition.guard.trim()) {
366
+ const lang = transition.guardLang ?? "sml" /* SML */;
367
+ const guardOk = await evalGuard(transition.guard, lang, b);
368
+ return guardOk ? [b] : [];
369
+ }
370
+ return [b];
371
+ }
372
+ const perArcCandidates = ptArcs.map((arc) => buildArcCandidates(arc, marking));
373
+ const candidateBindings = mergeBindings(perArcCandidates);
374
+ const enabled = [];
375
+ await Promise.all(
376
+ candidateBindings.map(async (b) => {
377
+ const arcChecks = ptArcs.map(async (arc) => {
378
+ const lang = arc.inscriptionLang ?? "sml" /* SML */;
379
+ const inscription = arc.inscription.trim();
380
+ if (!inscription) return true;
381
+ const weight = await evalExpr(inscription, lang, b);
382
+ const available = marking.get(arc.sourceId) ?? EMPTY_MULTISET;
383
+ return msContains(available, weight);
384
+ });
385
+ const arcResults = await Promise.all(arcChecks);
386
+ if (arcResults.some((ok) => !ok)) return;
387
+ if (transition.guard.trim()) {
388
+ const lang = transition.guardLang ?? "sml" /* SML */;
389
+ const guardOk = await evalGuard(transition.guard, lang, b);
390
+ if (!guardOk) return;
391
+ }
392
+ enabled.push(b);
393
+ })
394
+ );
395
+ return enabled;
396
+ }
397
+ async function fire(net, marking, transitionId, binding, evalExpr = evalExprWithSosml, callbacks = {}) {
398
+ const newMarking = new Map(marking);
399
+ const transition = net.transitions.get(transitionId);
400
+ if (!transition) return newMarking;
401
+ await callbacks.beforeFire?.({ transition, binding, marking, net });
402
+ for (const arc of net.arcs.values()) {
403
+ if (arc.kind !== "PT" || arc.targetId !== transitionId) continue;
404
+ if (arc.readArcGroupId) continue;
405
+ const inscription = arc.inscription.trim();
406
+ if (!inscription) continue;
407
+ const lang = arc.inscriptionLang ?? "sml" /* SML */;
408
+ const weight = await evalExpr(inscription, lang, binding);
409
+ const current = newMarking.get(arc.sourceId) ?? EMPTY_MULTISET;
410
+ newMarking.set(arc.sourceId, msSubtract(current, weight));
411
+ }
412
+ const markingAfterConsume = new Map(newMarking);
413
+ const customResult = await callbacks.midFire?.({
414
+ transition,
415
+ binding,
416
+ marking,
417
+ markingAfterConsume,
418
+ net
419
+ });
420
+ if (customResult !== null && customResult !== void 0) {
421
+ for (const [placeId, tokens] of customResult) {
422
+ const current = newMarking.get(placeId) ?? EMPTY_MULTISET;
423
+ newMarking.set(placeId, msAdd(current, tokens));
424
+ }
425
+ await callbacks.afterFire?.({
426
+ transition,
427
+ binding,
428
+ marking,
429
+ markingAfterConsume,
430
+ markingAfterFire: newMarking,
431
+ net
432
+ });
433
+ return newMarking;
434
+ }
435
+ for (const arc of net.arcs.values()) {
436
+ if (arc.kind !== "TP" || arc.sourceId !== transitionId) continue;
437
+ if (arc.readArcGroupId) continue;
438
+ const inscription = arc.inscription.trim();
439
+ if (!inscription) continue;
440
+ const lang = arc.inscriptionLang ?? "sml" /* SML */;
441
+ const weight = await evalExpr(inscription, lang, binding);
442
+ const current = newMarking.get(arc.targetId) ?? EMPTY_MULTISET;
443
+ newMarking.set(arc.targetId, msAdd(current, weight));
444
+ }
445
+ await callbacks.afterFire?.({
446
+ transition,
447
+ binding,
448
+ marking,
449
+ markingAfterConsume,
450
+ markingAfterFire: newMarking,
451
+ net
452
+ });
453
+ return newMarking;
454
+ }
455
+ async function computeEnabledSet(net, marking, evalExpr = evalExprWithSosml, evalGuard = evalGuardWithSosml) {
456
+ const enabled = /* @__PURE__ */ new Set();
457
+ await Promise.all(
458
+ [...net.transitions.keys()].map(async (tId) => {
459
+ const bindings = await findEnabledBindings(net, marking, tId, evalExpr, evalGuard);
460
+ if (bindings.length > 0) enabled.add(tId);
461
+ })
462
+ );
463
+ return enabled;
464
+ }
465
+ // Annotate the CommonJS export names for ESM import in node:
466
+ 0 && (module.exports = {
467
+ EMPTY_MULTISET,
468
+ SmlEvaluationError,
469
+ computeEnabledSet,
470
+ evalExprWithSosml,
471
+ evalGuardWithSosml,
472
+ evalSmlExpression,
473
+ evalSmlGuard,
474
+ findEnabledBindings,
475
+ fire,
476
+ msAdd,
477
+ msContains,
478
+ msEquals,
479
+ msIsEmpty,
480
+ msScale,
481
+ msSubtract
482
+ });
483
+ //# sourceMappingURL=index.cjs.map