@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 +9 -0
- package/README.md +206 -0
- package/dist/cjs/index.cjs +483 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/index.d.ts +159 -0
- package/dist/esm/index.js +432 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/types/engine.d.ts +62 -0
- package/dist/types/engine.d.ts.map +1 -0
- package/dist/types/examples.test.d.ts +2 -0
- package/dist/types/examples.test.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/multiset.d.ts +47 -0
- package/dist/types/multiset.d.ts.map +1 -0
- package/dist/types/net-types.d.ts +33 -0
- package/dist/types/net-types.d.ts.map +1 -0
- package/dist/types/semantics.test.d.ts +7 -0
- package/dist/types/semantics.test.d.ts.map +1 -0
- package/dist/types/sml.d.ts +7 -0
- package/dist/types/sml.d.ts.map +1 -0
- package/dist/types/types.d.ts +13 -0
- package/dist/types/types.d.ts.map +1 -0
- package/examples/lib.mjs +40 -0
- package/examples/producer-consumer.mjs +82 -0
- package/examples/sml-integration.mjs +72 -0
- package/examples/traffic-light.mjs +53 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/multiset.ts","../../src/sml.ts","../../src/engine.ts"],"sourcesContent":["/**\n * Multiset — a Map from serialized token value (string) to count (positive integer).\n * Used for CPN token markings and arc annotations.\n * Keys are arbitrary strings (JSON-serialized token values).\n *\n * Security note (T-01-03-01): Map is used (not plain object) so that keys like\n * `__proto__` are safe — Map key identity is value-based and does not interact\n * with the prototype chain.\n */\nexport type Multiset = ReadonlyMap<string, number>;\n\n/** The empty multiset. Reuse this constant — don't create `new Map()` everywhere. */\nexport const EMPTY_MULTISET: Multiset = new Map();\n\n// ─── Multiset arithmetic ──────────────────────────────────────────────────────\n\n/**\n * Union with summed counts: a + b.\n * Result contains every token from both; counts are summed for shared tokens.\n */\nexport function msAdd(a: Multiset, b: Multiset): Multiset {\n const result = new Map(a);\n for (const [k, v] of b) {\n result.set(k, (result.get(k) ?? 0) + v);\n }\n return result;\n}\n\n/**\n * Multiset difference: a - b.\n * Tokens with count reaching 0 are removed from the result.\n *\n * THROWS if any token in b has a higher count than in a (underflow).\n * This enforces the CPN enabling condition (T-01-03-04): a transition can only\n * fire if the arc weight is a submultiset of the current place marking.\n * Callers must verify `msContains(marking, arcWeight)` before calling this.\n */\nexport function msSubtract(a: Multiset, b: Multiset): Multiset {\n const result = new Map(a);\n for (const [k, v] of b) {\n const have = result.get(k) ?? 0;\n const count = have - v;\n if (count < 0) {\n throw new Error(`Multiset underflow for token \"${k}\": need ${v}, have ${have}`);\n }\n if (count === 0) {\n result.delete(k);\n } else {\n result.set(k, count);\n }\n }\n return result;\n}\n\n/**\n * Scalar multiplication: ms * n.\n * n must be a non-negative integer. n=0 returns EMPTY_MULTISET.\n * Used for arc weight expressions (e.g. 2`token means count 2 of token).\n */\nexport function msScale(ms: Multiset, n: number): Multiset {\n if (!Number.isInteger(n) || n < 0) {\n throw new RangeError(`msScale: n must be a non-negative integer, got ${n}`);\n }\n if (n === 0) return EMPTY_MULTISET;\n const result = new Map<string, number>();\n for (const [k, v] of ms) {\n result.set(k, v * n);\n }\n return result;\n}\n\n/**\n * Submultiset check: b ⊆ a.\n * Returns true iff every token in b appears in a with at least the same count.\n * Used for the CPN enabling check (arc weight ⊆ current marking).\n */\nexport function msContains(a: Multiset, b: Multiset): boolean {\n for (const [k, v] of b) {\n if ((a.get(k) ?? 0) < v) return false;\n }\n return true;\n}\n\n/** Returns true iff the multiset has no tokens. */\nexport function msIsEmpty(ms: Multiset): boolean {\n return ms.size === 0;\n}\n\n/**\n * Structural equality: a == b.\n * Two multisets are equal iff they have identical keys and counts.\n */\nexport function msEquals(a: Multiset, b: Multiset): boolean {\n if (a.size !== b.size) return false;\n for (const [k, v] of a) {\n if (b.get(k) !== v) return false;\n }\n return true;\n}\n","import interpreter from '@sosml/interpreter';\nimport type { State, Values } from '@sosml/interpreter';\n\nimport type { Binding } from './types.js';\n\nconst { getFirstState, interpret } = interpreter;\ntype Value = Values.Value;\n\nexport class SmlEvaluationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SmlEvaluationError';\n }\n}\n\nconst RESULT_NAME = 'cpnResult';\n\nfunction escapeSmlString(value: string): string {\n return `\"${value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t')}\"`;\n}\n\nfunction tokenKeyToSmlLiteral(value: string): string {\n if (value === 'true' || value === 'false') return value;\n if (value === '()') return '()';\n if (/^-?\\d+$/.test(value)) return value.replace(/^-/, '~');\n if (/^\\(.*\\)$/.test(value)) {\n return value.replace(/(^|[(,])-?\\d+/g, (match) => match.replace('-', '~'));\n }\n return escapeSmlString(value);\n}\n\nfunction bindingPrelude(binding: Binding): string {\n return [...binding]\n .map(([name, value]) => `val ${name} = ${tokenKeyToSmlLiteral(value)};`)\n .join('\\n');\n}\n\nfunction evaluateValue(expr: string, binding: Binding): { value: Value; state: State } {\n const source = `${bindingPrelude(binding)}\\nval ${RESULT_NAME} = ${expr};`;\n const result = interpret(source, getFirstState(), { allowSuccessorML: true, allowVector: true });\n if (result.evaluationErrored) {\n throw new SmlEvaluationError(String(result.error ?? 'SML evaluation failed'));\n }\n\n const bindingValue = result.state.getDynamicValue(RESULT_NAME)?.[0];\n if (!bindingValue) {\n throw new SmlEvaluationError('SML evaluation did not produce a result');\n }\n return { value: bindingValue, state: result.state };\n}\n\ntype SosmlValue = Value & {\n readonly value?: unknown;\n readonly entries?: Map<string, Value>;\n};\n\nfunction unquoteSmlString(value: string): string {\n return value\n .slice(1, -1)\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\\\\\/g, '\\\\');\n}\n\nfunction valueToTokenKey(value: Value, state: State): string {\n const typed = value as SosmlValue;\n switch (value.typeName()) {\n case 'Integer':\n return String(typed.value);\n case 'BoolValue':\n return String(typed.value);\n case 'StringValue':\n return typeof typed.value === 'string' ? typed.value : unquoteSmlString(value.toString(state));\n case 'RecordValue': {\n const entries = typed.entries;\n if (!entries || entries.size === 0) return '()';\n const tupleItems: string[] = [];\n for (let i = 1; i <= entries.size; i += 1) {\n const item = entries.get(String(i));\n if (!item) break;\n tupleItems.push(valueToTokenKey(item, state));\n }\n if (tupleItems.length === entries.size && tupleItems.length > 0) {\n return `(${tupleItems.join(',')})`;\n }\n break;\n }\n default:\n break;\n }\n return value.toString(state);\n}\n\nfunction valueToBoolean(value: Value): boolean {\n if (value.typeName() !== 'BoolValue') {\n throw new SmlEvaluationError(`Guard must evaluate to bool, got ${value.typeName()}`);\n }\n return Boolean((value as SosmlValue).value);\n}\n\nfunction addToken(result: Map<string, number>, key: string, count: number): void {\n if (!Number.isInteger(count) || count < 0) {\n throw new SmlEvaluationError(`Multiset count must be a non-negative integer, got ${count}`);\n }\n if (count === 0) return;\n result.set(key, (result.get(key) ?? 0) + count);\n}\n\nfunction splitTopLevelUnion(expr: string): string[] {\n const parts: string[] = [];\n let start = 0;\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = 0; i < expr.length; i += 1) {\n const ch = expr[i]!;\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') {\n inString = true;\n continue;\n }\n if (ch === '(' || ch === '[' || ch === '{') depth += 1;\n else if (ch === ')' || ch === ']' || ch === '}') depth -= 1;\n else if (ch === '+' && expr[i + 1] === '+' && depth === 0) {\n parts.push(expr.slice(start, i).trim());\n start = i + 2;\n i += 1;\n }\n }\n\n parts.push(expr.slice(start).trim());\n return parts.filter(Boolean);\n}\n\nfunction splitMultisetTerm(term: string): { countExpr: string; valueExpr: string } | null {\n let depth = 0;\n let inString = false;\n let escaped = false;\n for (let i = 0; i < term.length; i += 1) {\n const ch = term[i]!;\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') {\n inString = true;\n continue;\n }\n if (ch === '(' || ch === '[' || ch === '{') depth += 1;\n else if (ch === ')' || ch === ']' || ch === '}') depth -= 1;\n else if (ch === '`' && depth === 0) {\n return { countExpr: term.slice(0, i).trim(), valueExpr: term.slice(i + 1).trim() };\n }\n }\n return null;\n}\n\nexport async function evalSmlExpression(\n expr: string,\n binding: Binding,\n): Promise<Map<string, number>> {\n const trimmed = expr.trim();\n if (!trimmed || trimmed === 'empty') return new Map();\n\n const result = new Map<string, number>();\n for (const part of splitTopLevelUnion(trimmed)) {\n const term = splitMultisetTerm(part);\n if (!term) {\n const { value, state } = evaluateValue(part, binding);\n addToken(result, valueToTokenKey(value, state), 1);\n continue;\n }\n\n const countResult = evaluateValue(term.countExpr, binding);\n if (countResult.value.typeName() !== 'Integer') {\n throw new SmlEvaluationError(`Multiset count must evaluate to int, got ${countResult.value.typeName()}`);\n }\n const valueResult = evaluateValue(term.valueExpr, binding);\n addToken(\n result,\n valueToTokenKey(valueResult.value, valueResult.state),\n Number((countResult.value as SosmlValue).value),\n );\n }\n return result;\n}\n\nexport async function evalSmlGuard(\n expr: string,\n binding: Binding,\n): Promise<boolean> {\n if (!expr.trim()) return true;\n return valueToBoolean(evaluateValue(expr, binding).value);\n}\n","/**\n * CPN Simulation Engine — pure async functions implementing Jensen Vol.1 enabling/firing semantics.\n *\n * SECURITY (LOCKED-05): No eval(), new Function(), or Function() in this file.\n */\n\nimport type { Arc, Place, Transition, NetLike } from './net-types.js';\nimport { msContains, msSubtract, msAdd, EMPTY_MULTISET } from './multiset.js';\nimport type { Marking, Binding } from './types.js';\nimport { evalSmlExpression, evalSmlGuard } from './sml.js';\n\nimport { InterpreterLanguage } from './net-types.js';\n\n/**\n * The evaluation function injected by simulationCommands.ts.\n * For arc expressions: returns a Multiset (Map<string, number>).\n */\nexport type EvalExprFn = (\n expr: string,\n lang: InterpreterLanguage,\n binding: Binding,\n) => Promise<Map<string, number>>;\n\nexport type EvalGuardFn = (\n expr: string,\n lang: InterpreterLanguage,\n binding: Binding,\n) => Promise<boolean>;\n\n/**\n * Lifecycle callbacks for simulation firing.\n * `midFire` runs after input tokens are removed and before output tokens are added.\n * Returning a Map from `midFire` lets an application provide custom output tokens\n * for a transition; returning null/undefined keeps standard TP inscription semantics.\n */\nexport type FireCallbackContext = {\n readonly transition: Transition;\n readonly binding: Binding;\n readonly marking: Marking;\n readonly net: NetLike;\n};\n\nexport type MidFireCallbackContext = FireCallbackContext & {\n readonly markingAfterConsume: Marking;\n};\n\nexport type AfterFireCallbackContext = FireCallbackContext & {\n readonly markingAfterConsume: Marking;\n readonly markingAfterFire: Marking;\n};\n\nexport type CustomTransitionOutput = Map<string, Map<string, number>>;\n\nexport type FireCallbacks = {\n readonly beforeFire?: (ctx: FireCallbackContext) => Promise<void> | void;\n readonly midFire?: (\n ctx: MidFireCallbackContext,\n ) => Promise<CustomTransitionOutput | null | undefined> | CustomTransitionOutput | null | undefined;\n readonly afterFire?: (ctx: AfterFireCallbackContext) => Promise<void> | void;\n};\n\nexport const evalExprWithSosml: EvalExprFn = async (expr, lang, binding) => {\n if (lang !== 'sml') {\n throw new Error(`No ${lang} expression evaluator is configured in @greenByteHQ/cpn-semantics`);\n }\n return evalSmlExpression(expr, binding);\n};\n\nexport const evalGuardWithSosml: EvalGuardFn = async (expr, lang, binding) => {\n switch (lang) {\n case 'sml':\n return evalSmlGuard(expr, binding);\n case 'python':\n throw new Error(`No ${lang} guard evaluator is configured in @greenByteHQ/cpn-semantics`);\n case 'js':\n throw new Error(`No ${lang} guard evaluator is configured in @greenByteHQ/cpn-semantics`);\n default:\n throw new Error(`Unknown language ${lang} in guard expression`);\n }\n};\n\n// ── Internal helpers ────────────────────────────────────────────────────────\n\n/**\n * Extract variable names from an inscription expression.\n * Phase 3 simple heuristic: scan for IDENT tokens (sequences of [a-zA-Z_][a-zA-Z0-9_']*)\n * that are not CPN ML keywords. Returns deduplicated variable names.\n */\nfunction extractVariables(expr: string): string[] {\n const KEYWORDS = new Set([\n 'let', 'val', 'in', 'end', 'if', 'then', 'else',\n 'andalso', 'orelse', 'not', 'div', 'mod', 'true', 'false', 'empty',\n ]);\n const matches = expr.match(/[a-zA-Z_][a-zA-Z0-9_']*/g) ?? [];\n return [...new Set(matches)].filter((m) => !KEYWORDS.has(m));\n}\n\n/**\n * Build candidate bindings for a single PT arc.\n * Strategy (per LOCKED-05 and RESEARCH.md §Binding Enumeration):\n * - Constant inscription (no free variables): one candidate = empty partial binding\n * - Single variable x: one candidate per distinct token key in source place marking\n * - Multiple variables: Cartesian product of all token keys in source marking\n *\n * Returns array of partial Binding maps for this arc's variables.\n */\nfunction buildArcCandidates(\n arc: Arc,\n marking: Marking,\n): Map<string, string>[] {\n const sourceMarking = marking.get(arc.sourceId) ?? EMPTY_MULTISET;\n const tokenKeys = [...sourceMarking.keys()];\n\n if (arc.inscription.trim() === '') {\n // No inscription — arc has weight 1 (implicit), treat as constant\n return [new Map()];\n }\n\n const vars = extractVariables(arc.inscription);\n if (vars.length === 0) {\n // Constant inscription — no variables to bind\n return [new Map()];\n }\n\n if (vars.length === 1) {\n // Single variable — one candidate per token in source place\n return tokenKeys.map((key) => new Map([[vars[0]!, key]]));\n }\n\n // Multiple variables — Cartesian product of tokenKeys for each variable\n // Complexity: O(k^n) where k=distinct tokens, n=variables — bounded per RESEARCH.md\n let candidates: Map<string, string>[] = [new Map()];\n for (const varName of vars) {\n const next: Map<string, string>[] = [];\n for (const candidate of candidates) {\n for (const key of tokenKeys) {\n // Skip if variable already bound to a different value (consistency)\n if (candidate.has(varName) && candidate.get(varName) !== key) continue;\n const extended = new Map(candidate);\n extended.set(varName, key);\n next.push(extended);\n }\n }\n candidates = next;\n }\n return candidates;\n}\n\n/**\n * Merge partial binding maps from multiple arcs.\n * Returns only consistent combinations (same variable → same value).\n * Produces Cartesian product of per-arc candidates filtered for consistency.\n */\nfunction mergeBindings(\n perArcCandidates: Map<string, string>[][],\n): Binding[] {\n let combined: Map<string, string>[] = [new Map()];\n for (const arcCandidates of perArcCandidates) {\n const next: Map<string, string>[] = [];\n for (const existing of combined) {\n for (const arcCandidate of arcCandidates) {\n // Check consistency: if both maps have the same key, values must match\n let consistent = true;\n for (const [k, v] of arcCandidate) {\n if (existing.has(k) && existing.get(k) !== v) { consistent = false; break; }\n }\n if (!consistent) continue;\n next.push(new Map([...existing, ...arcCandidate]));\n }\n }\n combined = next;\n }\n return combined;\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Find all bindings under which transition `transitionId` is enabled.\n *\n * A binding b enables t iff:\n * 1. ∀ PT-arc a to t: E(a)(b) ⊆ M(source(a)) (arc inscription submultiset)\n * 2. G(t)(b) = true (guard evaluates to true)\n *\n * (Jensen Vol.1, Chapter 2, Definition 2.6)\n */\nexport async function findEnabledBindings(\n net: NetLike,\n marking: Marking,\n transitionId: string,\n evalExpr: EvalExprFn = evalExprWithSosml,\n evalGuard: EvalGuardFn = evalGuardWithSosml,\n): Promise<Binding[]> {\n const transition = net.transitions.get(transitionId);\n if (!transition) return [];\n\n const ptArcs = [...net.arcs.values()].filter(\n (a) => a.kind === 'PT' && a.targetId === transitionId,\n );\n\n if (ptArcs.length === 0) {\n // No input arcs — check guard only; binding is empty\n const b: Binding = new Map();\n if (transition.guard.trim()) {\n const lang = transition.guardLang ?? InterpreterLanguage.SML;\n const guardOk = await evalGuard(transition.guard, lang, b);\n return guardOk ? [b] : [];\n }\n return [b];\n }\n\n // Build per-arc candidates and merge\n const perArcCandidates = ptArcs.map((arc) => buildArcCandidates(arc, marking));\n const candidateBindings = mergeBindings(perArcCandidates);\n\n // Filter: check arc expressions satisfied AND guard passes\n const enabled: Binding[] = [];\n await Promise.all(\n candidateBindings.map(async (b) => {\n // Check all PT arc expression weights are covered by the marking\n const arcChecks = ptArcs.map(async (arc) => {\n const lang = arc.inscriptionLang ?? InterpreterLanguage.SML;\n const inscription = arc.inscription.trim();\n if (!inscription) return true; // empty inscription = no token consumed\n const weight = await evalExpr(inscription, lang, b);\n const available = marking.get(arc.sourceId) ?? EMPTY_MULTISET;\n return msContains(available, weight);\n });\n const arcResults = await Promise.all(arcChecks);\n if (arcResults.some((ok) => !ok)) return;\n\n // Check guard\n if (transition.guard.trim()) {\n const lang = transition.guardLang ?? InterpreterLanguage.SML;\n const guardOk = await evalGuard(transition.guard, lang, b);\n if (!guardOk) return;\n }\n enabled.push(b);\n }),\n );\n return enabled;\n}\n\n/**\n * Fire transition `transitionId` under binding `b`, returning the updated marking.\n * Assumes (transitionId, b) is enabled — throws if a place would underflow.\n */\nexport async function fire(\n net: NetLike,\n marking: Marking,\n transitionId: string,\n binding: Binding,\n evalExpr: EvalExprFn = evalExprWithSosml,\n callbacks: FireCallbacks = {},\n): Promise<Marking> {\n // Start from a mutable copy; replace reference for immutability\n const newMarking = new Map(marking);\n\n const transition = net.transitions.get(transitionId);\n if (!transition) return newMarking;\n\n await callbacks.beforeFire?.({ transition, binding, marking, net });\n\n // Remove tokens consumed by PT arcs.\n for (const arc of net.arcs.values()) {\n if (arc.kind !== 'PT' || arc.targetId !== transitionId) continue;\n if (arc.readArcGroupId) continue;\n const inscription = arc.inscription.trim();\n if (!inscription) continue;\n const lang = arc.inscriptionLang ?? InterpreterLanguage.SML;\n const weight = await evalExpr(inscription, lang, binding);\n const current = newMarking.get(arc.sourceId) ?? EMPTY_MULTISET;\n newMarking.set(arc.sourceId, msSubtract(current, weight));\n }\n\n // Add tokens produced by TP arcs, unless midFire supplies custom output tokens.\n const markingAfterConsume = new Map(newMarking);\n const customResult = await callbacks.midFire?.({\n transition,\n binding,\n marking,\n markingAfterConsume,\n net,\n });\n if (customResult !== null && customResult !== undefined) {\n for (const [placeId, tokens] of customResult) {\n const current = newMarking.get(placeId) ?? EMPTY_MULTISET;\n newMarking.set(placeId, msAdd(current, tokens));\n }\n await callbacks.afterFire?.({\n transition,\n binding,\n marking,\n markingAfterConsume,\n markingAfterFire: newMarking,\n net,\n });\n return newMarking;\n }\n\n // Standard CPN path: evaluate TP arc inscriptions via evalExpr\n for (const arc of net.arcs.values()) {\n if (arc.kind !== 'TP' || arc.sourceId !== transitionId) continue;\n if (arc.readArcGroupId) continue;\n const inscription = arc.inscription.trim();\n if (!inscription) continue;\n const lang = arc.inscriptionLang ?? InterpreterLanguage.SML;\n const weight = await evalExpr(inscription, lang, binding);\n const current = newMarking.get(arc.targetId) ?? EMPTY_MULTISET;\n newMarking.set(arc.targetId, msAdd(current, weight));\n }\n\n await callbacks.afterFire?.({\n transition,\n binding,\n marking,\n markingAfterConsume,\n markingAfterFire: newMarking,\n net,\n });\n return newMarking;\n}\n\n/**\n * Compute the set of all currently enabled transition IDs.\n * Used after every step to update simulationStore.enabledTransitions.\n */\nexport async function computeEnabledSet(\n net: NetLike,\n marking: Marking,\n evalExpr: EvalExprFn = evalExprWithSosml,\n evalGuard: EvalGuardFn = evalGuardWithSosml,\n): Promise<ReadonlySet<string>> {\n const enabled = new Set<string>();\n // Parallel evaluation — all transitions checked concurrently\n await Promise.all(\n [...net.transitions.keys()].map(async (tId) => {\n const bindings = await findEnabledBindings(net, marking, tId, evalExpr, evalGuard);\n if (bindings.length > 0) enabled.add(tId);\n }),\n );\n return enabled;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CPN Simulation Engine — pure async functions implementing Jensen Vol.1 enabling/firing semantics.
|
|
3
|
+
*
|
|
4
|
+
* SECURITY (LOCKED-05): No eval(), new Function(), or Function() in this file.
|
|
5
|
+
*/
|
|
6
|
+
import type { Transition, NetLike } from './net-types.js';
|
|
7
|
+
import type { Marking, Binding } from './types.js';
|
|
8
|
+
import { InterpreterLanguage } from './net-types.js';
|
|
9
|
+
/**
|
|
10
|
+
* The evaluation function injected by simulationCommands.ts.
|
|
11
|
+
* For arc expressions: returns a Multiset (Map<string, number>).
|
|
12
|
+
*/
|
|
13
|
+
export type EvalExprFn = (expr: string, lang: InterpreterLanguage, binding: Binding) => Promise<Map<string, number>>;
|
|
14
|
+
export type EvalGuardFn = (expr: string, lang: InterpreterLanguage, binding: Binding) => Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Lifecycle callbacks for simulation firing.
|
|
17
|
+
* `midFire` runs after input tokens are removed and before output tokens are added.
|
|
18
|
+
* Returning a Map from `midFire` lets an application provide custom output tokens
|
|
19
|
+
* for a transition; returning null/undefined keeps standard TP inscription semantics.
|
|
20
|
+
*/
|
|
21
|
+
export type FireCallbackContext = {
|
|
22
|
+
readonly transition: Transition;
|
|
23
|
+
readonly binding: Binding;
|
|
24
|
+
readonly marking: Marking;
|
|
25
|
+
readonly net: NetLike;
|
|
26
|
+
};
|
|
27
|
+
export type MidFireCallbackContext = FireCallbackContext & {
|
|
28
|
+
readonly markingAfterConsume: Marking;
|
|
29
|
+
};
|
|
30
|
+
export type AfterFireCallbackContext = FireCallbackContext & {
|
|
31
|
+
readonly markingAfterConsume: Marking;
|
|
32
|
+
readonly markingAfterFire: Marking;
|
|
33
|
+
};
|
|
34
|
+
export type CustomTransitionOutput = Map<string, Map<string, number>>;
|
|
35
|
+
export type FireCallbacks = {
|
|
36
|
+
readonly beforeFire?: (ctx: FireCallbackContext) => Promise<void> | void;
|
|
37
|
+
readonly midFire?: (ctx: MidFireCallbackContext) => Promise<CustomTransitionOutput | null | undefined> | CustomTransitionOutput | null | undefined;
|
|
38
|
+
readonly afterFire?: (ctx: AfterFireCallbackContext) => Promise<void> | void;
|
|
39
|
+
};
|
|
40
|
+
export declare const evalExprWithSosml: EvalExprFn;
|
|
41
|
+
export declare const evalGuardWithSosml: EvalGuardFn;
|
|
42
|
+
/**
|
|
43
|
+
* Find all bindings under which transition `transitionId` is enabled.
|
|
44
|
+
*
|
|
45
|
+
* A binding b enables t iff:
|
|
46
|
+
* 1. ∀ PT-arc a to t: E(a)(b) ⊆ M(source(a)) (arc inscription submultiset)
|
|
47
|
+
* 2. G(t)(b) = true (guard evaluates to true)
|
|
48
|
+
*
|
|
49
|
+
* (Jensen Vol.1, Chapter 2, Definition 2.6)
|
|
50
|
+
*/
|
|
51
|
+
export declare function findEnabledBindings(net: NetLike, marking: Marking, transitionId: string, evalExpr?: EvalExprFn, evalGuard?: EvalGuardFn): Promise<Binding[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Fire transition `transitionId` under binding `b`, returning the updated marking.
|
|
54
|
+
* Assumes (transitionId, b) is enabled — throws if a place would underflow.
|
|
55
|
+
*/
|
|
56
|
+
export declare function fire(net: NetLike, marking: Marking, transitionId: string, binding: Binding, evalExpr?: EvalExprFn, callbacks?: FireCallbacks): Promise<Marking>;
|
|
57
|
+
/**
|
|
58
|
+
* Compute the set of all currently enabled transition IDs.
|
|
59
|
+
* Used after every step to update simulationStore.enabledTransitions.
|
|
60
|
+
*/
|
|
61
|
+
export declare function computeEnabledSet(net: NetLike, marking: Marking, evalExpr?: EvalExprFn, evalGuard?: EvalGuardFn): Promise<ReadonlySet<string>>;
|
|
62
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAc,UAAU,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IACzD,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,mBAAmB,GAAG;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,CACjB,GAAG,EAAE,sBAAsB,KACxB,OAAO,CAAC,sBAAsB,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,sBAAsB,GAAG,IAAI,GAAG,SAAS,CAAC;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,wBAAwB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9E,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,UAK/B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,WAWhC,CAAC;AAkGF;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,UAA8B,EACxC,SAAS,GAAE,WAAgC,GAC1C,OAAO,CAAC,OAAO,EAAE,CAAC,CAiDpB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACxB,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,UAA8B,EACxC,SAAS,GAAE,aAAkB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAmElB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,UAA8B,EACxC,SAAS,GAAE,WAAgC,GAC1C,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAU9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"examples.test.d.ts","sourceRoot":"","sources":["../../src/examples.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { Multiset } from './multiset.js';
|
|
2
|
+
export { EMPTY_MULTISET, msAdd, msSubtract, msScale, msContains, msIsEmpty, msEquals, } from './multiset.js';
|
|
3
|
+
export type { Marking, Binding } from './types.js';
|
|
4
|
+
export type { Place, Transition, Arc, NetLike } from './net-types.js';
|
|
5
|
+
export type { EvalExprFn, EvalGuardFn, FireCallbackContext, MidFireCallbackContext, AfterFireCallbackContext, CustomTransitionOutput, FireCallbacks, } from './engine.js';
|
|
6
|
+
export { findEnabledBindings, fire, computeEnabledSet, evalExprWithSosml, evalGuardWithSosml, } from './engine.js';
|
|
7
|
+
export { evalSmlExpression, evalSmlGuard, SmlEvaluationError } from './sml.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,cAAc,EACd,KAAK,EAAE,UAAU,EAAE,OAAO,EAC1B,UAAU,EAAE,SAAS,EAAE,QAAQ,GAChC,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EACV,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,IAAI,EACJ,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiset — a Map from serialized token value (string) to count (positive integer).
|
|
3
|
+
* Used for CPN token markings and arc annotations.
|
|
4
|
+
* Keys are arbitrary strings (JSON-serialized token values).
|
|
5
|
+
*
|
|
6
|
+
* Security note (T-01-03-01): Map is used (not plain object) so that keys like
|
|
7
|
+
* `__proto__` are safe — Map key identity is value-based and does not interact
|
|
8
|
+
* with the prototype chain.
|
|
9
|
+
*/
|
|
10
|
+
export type Multiset = ReadonlyMap<string, number>;
|
|
11
|
+
/** The empty multiset. Reuse this constant — don't create `new Map()` everywhere. */
|
|
12
|
+
export declare const EMPTY_MULTISET: Multiset;
|
|
13
|
+
/**
|
|
14
|
+
* Union with summed counts: a + b.
|
|
15
|
+
* Result contains every token from both; counts are summed for shared tokens.
|
|
16
|
+
*/
|
|
17
|
+
export declare function msAdd(a: Multiset, b: Multiset): Multiset;
|
|
18
|
+
/**
|
|
19
|
+
* Multiset difference: a - b.
|
|
20
|
+
* Tokens with count reaching 0 are removed from the result.
|
|
21
|
+
*
|
|
22
|
+
* THROWS if any token in b has a higher count than in a (underflow).
|
|
23
|
+
* This enforces the CPN enabling condition (T-01-03-04): a transition can only
|
|
24
|
+
* fire if the arc weight is a submultiset of the current place marking.
|
|
25
|
+
* Callers must verify `msContains(marking, arcWeight)` before calling this.
|
|
26
|
+
*/
|
|
27
|
+
export declare function msSubtract(a: Multiset, b: Multiset): Multiset;
|
|
28
|
+
/**
|
|
29
|
+
* Scalar multiplication: ms * n.
|
|
30
|
+
* n must be a non-negative integer. n=0 returns EMPTY_MULTISET.
|
|
31
|
+
* Used for arc weight expressions (e.g. 2`token means count 2 of token).
|
|
32
|
+
*/
|
|
33
|
+
export declare function msScale(ms: Multiset, n: number): Multiset;
|
|
34
|
+
/**
|
|
35
|
+
* Submultiset check: b ⊆ a.
|
|
36
|
+
* Returns true iff every token in b appears in a with at least the same count.
|
|
37
|
+
* Used for the CPN enabling check (arc weight ⊆ current marking).
|
|
38
|
+
*/
|
|
39
|
+
export declare function msContains(a: Multiset, b: Multiset): boolean;
|
|
40
|
+
/** Returns true iff the multiset has no tokens. */
|
|
41
|
+
export declare function msIsEmpty(ms: Multiset): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Structural equality: a == b.
|
|
44
|
+
* Two multisets are equal iff they have identical keys and counts.
|
|
45
|
+
*/
|
|
46
|
+
export declare function msEquals(a: Multiset, b: Multiset): boolean;
|
|
47
|
+
//# sourceMappingURL=multiset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiset.d.ts","sourceRoot":"","sources":["../../src/multiset.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD,qFAAqF;AACrF,eAAO,MAAM,cAAc,EAAE,QAAoB,CAAC;AAIlD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAMxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAe7D;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,CAUzD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAK5D;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAE/C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAM1D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural interfaces for standard CPN net elements used by the engine.
|
|
3
|
+
*/
|
|
4
|
+
/** Minimal place shape. The engine only needs stable place ids. */
|
|
5
|
+
export interface Place {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
}
|
|
8
|
+
export declare enum InterpreterLanguage {
|
|
9
|
+
SML = "sml",
|
|
10
|
+
Python = "python",
|
|
11
|
+
JS = "js"
|
|
12
|
+
}
|
|
13
|
+
export interface Transition {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly guard: string;
|
|
16
|
+
readonly guardLang?: InterpreterLanguage;
|
|
17
|
+
}
|
|
18
|
+
/** Arc shape for standard PT/TP CPN arcs. */
|
|
19
|
+
export interface Arc {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly kind: 'PT' | 'TP';
|
|
22
|
+
readonly sourceId: string;
|
|
23
|
+
readonly targetId: string;
|
|
24
|
+
readonly inscription: string;
|
|
25
|
+
readonly inscriptionLang?: InterpreterLanguage;
|
|
26
|
+
readonly readArcGroupId?: string;
|
|
27
|
+
}
|
|
28
|
+
export type NetLike = {
|
|
29
|
+
readonly places: ReadonlyMap<string, Place>;
|
|
30
|
+
readonly transitions: ReadonlyMap<string, Transition>;
|
|
31
|
+
readonly arcs: ReadonlyMap<string, Arc>;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=net-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net-types.d.ts","sourceRoot":"","sources":["../../src/net-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,mEAAmE;AACnE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,oBAAY,mBAAmB;IAC7B,GAAG,QAAQ;IACX,MAAM,WAAW;IACjB,EAAE,OAAO;CACV;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;CAC1C;AAED,6CAA6C;AAC7C,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,mBAAmB,CAAC;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACzC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-level unit tests for @greenByteHQ/cpn-semantics.
|
|
3
|
+
* All imports use relative package paths (NOT @greenByteHQ/cpn-semantics self-ref).
|
|
4
|
+
* All evalExpr / evalGuard are injected mocks — no Web Worker, no eval().
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=semantics.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantics.test.d.ts","sourceRoot":"","sources":["../../src/semantics.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Binding } from './types.js';
|
|
2
|
+
export declare class SmlEvaluationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function evalSmlExpression(expr: string, binding: Binding): Promise<Map<string, number>>;
|
|
6
|
+
export declare function evalSmlGuard(expr: string, binding: Binding): Promise<boolean>;
|
|
7
|
+
//# sourceMappingURL=sml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sml.d.ts","sourceRoot":"","sources":["../../src/sml.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AA+JD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAyB9B;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,OAAO,CAAC,CAGlB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Multiset } from './multiset.js';
|
|
2
|
+
/**
|
|
3
|
+
* Marking — current token distribution across all places.
|
|
4
|
+
* Immutable: a new Map is created on every step (not mutated in place).
|
|
5
|
+
*/
|
|
6
|
+
export type Marking = ReadonlyMap<string, Multiset>;
|
|
7
|
+
/**
|
|
8
|
+
* Binding — assignment of colour values (serialised as token-key strings)
|
|
9
|
+
* to transition variables found in arc inscriptions and guards.
|
|
10
|
+
* Key = variable name, Value = token key string (e.g. '3', 'red', '(1,2)').
|
|
11
|
+
*/
|
|
12
|
+
export type Binding = ReadonlyMap<string, string>;
|
|
13
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEpD;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC"}
|
package/examples/lib.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { findEnabledBindings, fire } from '../dist/esm/index.js';
|
|
2
|
+
|
|
3
|
+
export function multiset(entries) {
|
|
4
|
+
return new Map(entries);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function snapshotMarking(marking) {
|
|
8
|
+
return Object.fromEntries(
|
|
9
|
+
[...marking].map(([placeId, tokens]) => [placeId, Object.fromEntries(tokens)]),
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function fireFirstEnabled(net, marking, transitionId) {
|
|
14
|
+
const bindings = await findEnabledBindings(net, marking, transitionId);
|
|
15
|
+
const binding = bindings[0];
|
|
16
|
+
if (!binding) {
|
|
17
|
+
throw new Error(`Transition "${transitionId}" is not enabled`);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
binding,
|
|
21
|
+
marking: await fire(net, marking, transitionId, binding),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function runTransitionSequence(net, initialMarking, transitionIds) {
|
|
26
|
+
let marking = initialMarking;
|
|
27
|
+
const steps = [];
|
|
28
|
+
|
|
29
|
+
for (const transitionId of transitionIds) {
|
|
30
|
+
const result = await fireFirstEnabled(net, marking, transitionId);
|
|
31
|
+
marking = result.marking;
|
|
32
|
+
steps.push({
|
|
33
|
+
transitionId,
|
|
34
|
+
binding: Object.fromEntries(result.binding),
|
|
35
|
+
snapshot: snapshotMarking(marking),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return steps;
|
|
40
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { multiset, runTransitionSequence } from './lib.mjs';
|
|
5
|
+
|
|
6
|
+
export function createProducerConsumerExample(bufferCapacity = 2) {
|
|
7
|
+
if (!Number.isInteger(bufferCapacity) || bufferCapacity < 1) {
|
|
8
|
+
throw new RangeError(`bufferCapacity must be a positive integer, got ${bufferCapacity}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const net = {
|
|
12
|
+
places: new Map([
|
|
13
|
+
['slots', { id: 'slots' }],
|
|
14
|
+
['buffer', { id: 'buffer' }],
|
|
15
|
+
['consumed', { id: 'consumed' }],
|
|
16
|
+
]),
|
|
17
|
+
transitions: new Map([
|
|
18
|
+
['produce', { id: 'produce', guard: '' }],
|
|
19
|
+
['consume', { id: 'consume', guard: '' }],
|
|
20
|
+
]),
|
|
21
|
+
arcs: new Map([
|
|
22
|
+
['slot-to-produce', {
|
|
23
|
+
id: 'slot-to-produce',
|
|
24
|
+
kind: 'PT',
|
|
25
|
+
sourceId: 'slots',
|
|
26
|
+
targetId: 'produce',
|
|
27
|
+
inscription: 'slot',
|
|
28
|
+
}],
|
|
29
|
+
['produce-to-buffer', {
|
|
30
|
+
id: 'produce-to-buffer',
|
|
31
|
+
kind: 'TP',
|
|
32
|
+
sourceId: 'produce',
|
|
33
|
+
targetId: 'buffer',
|
|
34
|
+
inscription: '1`"item"',
|
|
35
|
+
}],
|
|
36
|
+
['buffer-to-consume', {
|
|
37
|
+
id: 'buffer-to-consume',
|
|
38
|
+
kind: 'PT',
|
|
39
|
+
sourceId: 'buffer',
|
|
40
|
+
targetId: 'consume',
|
|
41
|
+
inscription: 'item',
|
|
42
|
+
}],
|
|
43
|
+
['consume-to-slots', {
|
|
44
|
+
id: 'consume-to-slots',
|
|
45
|
+
kind: 'TP',
|
|
46
|
+
sourceId: 'consume',
|
|
47
|
+
targetId: 'slots',
|
|
48
|
+
inscription: '1`()',
|
|
49
|
+
}],
|
|
50
|
+
['consume-to-consumed', {
|
|
51
|
+
id: 'consume-to-consumed',
|
|
52
|
+
kind: 'TP',
|
|
53
|
+
sourceId: 'consume',
|
|
54
|
+
targetId: 'consumed',
|
|
55
|
+
inscription: 'item',
|
|
56
|
+
}],
|
|
57
|
+
]),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
net,
|
|
62
|
+
initialMarking: new Map([
|
|
63
|
+
['slots', multiset([['()', bufferCapacity]])],
|
|
64
|
+
['buffer', new Map()],
|
|
65
|
+
['consumed', new Map()],
|
|
66
|
+
]),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function main() {
|
|
71
|
+
const example = createProducerConsumerExample(2);
|
|
72
|
+
const steps = await runTransitionSequence(
|
|
73
|
+
example.net,
|
|
74
|
+
example.initialMarking,
|
|
75
|
+
['produce', 'produce', 'consume', 'produce', 'consume'],
|
|
76
|
+
);
|
|
77
|
+
console.log(JSON.stringify(steps, null, 2));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
81
|
+
await main();
|
|
82
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { findEnabledBindings, fire } from '../dist/esm/index.js';
|
|
5
|
+
import { multiset, snapshotMarking } from './lib.mjs';
|
|
6
|
+
|
|
7
|
+
export function createSmlIntegrationExample() {
|
|
8
|
+
const net = {
|
|
9
|
+
places: new Map([
|
|
10
|
+
['numbers', { id: 'numbers' }],
|
|
11
|
+
['halves', { id: 'halves' }],
|
|
12
|
+
]),
|
|
13
|
+
transitions: new Map([
|
|
14
|
+
['halveEven', {
|
|
15
|
+
id: 'halveEven',
|
|
16
|
+
guard: 'n mod 2 = 0',
|
|
17
|
+
guardLang: 'sml',
|
|
18
|
+
}],
|
|
19
|
+
]),
|
|
20
|
+
arcs: new Map([
|
|
21
|
+
['number-to-halve-even', {
|
|
22
|
+
id: 'number-to-halve-even',
|
|
23
|
+
kind: 'PT',
|
|
24
|
+
sourceId: 'numbers',
|
|
25
|
+
targetId: 'halveEven',
|
|
26
|
+
inscription: 'n',
|
|
27
|
+
inscriptionLang: 'sml',
|
|
28
|
+
}],
|
|
29
|
+
['halve-even-to-halves', {
|
|
30
|
+
id: 'halve-even-to-halves',
|
|
31
|
+
kind: 'TP',
|
|
32
|
+
sourceId: 'halveEven',
|
|
33
|
+
targetId: 'halves',
|
|
34
|
+
inscription: '1`(n div 2)',
|
|
35
|
+
inscriptionLang: 'sml',
|
|
36
|
+
}],
|
|
37
|
+
]),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
net,
|
|
42
|
+
initialMarking: new Map([
|
|
43
|
+
['numbers', multiset([['2', 1], ['3', 1], ['8', 1]])],
|
|
44
|
+
['halves', new Map()],
|
|
45
|
+
]),
|
|
46
|
+
transitionId: 'halveEven',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function main() {
|
|
51
|
+
const example = createSmlIntegrationExample();
|
|
52
|
+
let marking = example.initialMarking;
|
|
53
|
+
const steps = [];
|
|
54
|
+
|
|
55
|
+
while (true) {
|
|
56
|
+
const bindings = await findEnabledBindings(example.net, marking, example.transitionId);
|
|
57
|
+
const binding = bindings[0];
|
|
58
|
+
if (!binding) break;
|
|
59
|
+
marking = await fire(example.net, marking, example.transitionId, binding);
|
|
60
|
+
steps.push({
|
|
61
|
+
transitionId: example.transitionId,
|
|
62
|
+
binding: Object.fromEntries(binding),
|
|
63
|
+
snapshot: snapshotMarking(marking),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(JSON.stringify(steps, null, 2));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
71
|
+
await main();
|
|
72
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { multiset, runTransitionSequence } from './lib.mjs';
|
|
5
|
+
|
|
6
|
+
export function createTrafficLightExample() {
|
|
7
|
+
const net = {
|
|
8
|
+
places: new Map([
|
|
9
|
+
['state', { id: 'state' }],
|
|
10
|
+
]),
|
|
11
|
+
transitions: new Map([
|
|
12
|
+
['advance', { id: 'advance', guard: '' }],
|
|
13
|
+
]),
|
|
14
|
+
arcs: new Map([
|
|
15
|
+
['state-to-advance', {
|
|
16
|
+
id: 'state-to-advance',
|
|
17
|
+
kind: 'PT',
|
|
18
|
+
sourceId: 'state',
|
|
19
|
+
targetId: 'advance',
|
|
20
|
+
inscription: 's',
|
|
21
|
+
}],
|
|
22
|
+
['advance-to-state', {
|
|
23
|
+
id: 'advance-to-state',
|
|
24
|
+
kind: 'TP',
|
|
25
|
+
sourceId: 'advance',
|
|
26
|
+
targetId: 'state',
|
|
27
|
+
inscription: 'if s = "red" then "green" else if s = "green" then "yellow" else "red"',
|
|
28
|
+
}],
|
|
29
|
+
]),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
net,
|
|
34
|
+
initialMarking: new Map([
|
|
35
|
+
['state', multiset([['red', 1]])],
|
|
36
|
+
]),
|
|
37
|
+
transitionId: 'advance',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function main() {
|
|
42
|
+
const example = createTrafficLightExample();
|
|
43
|
+
const steps = await runTransitionSequence(
|
|
44
|
+
example.net,
|
|
45
|
+
example.initialMarking,
|
|
46
|
+
Array.from({ length: 3 }, () => example.transitionId),
|
|
47
|
+
);
|
|
48
|
+
console.log(JSON.stringify(steps, null, 2));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
52
|
+
await main();
|
|
53
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@greenbytehq/cpn.js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pure JS/TS CPN impementation and simulation engine",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cpn",
|
|
7
|
+
"coloured-petri-nets",
|
|
8
|
+
"petri-nets",
|
|
9
|
+
"simulation",
|
|
10
|
+
"formal-methods"
|
|
11
|
+
],
|
|
12
|
+
"author": "kentis",
|
|
13
|
+
"license": "(MIT OR GPL-3.0-only",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/kentis/CPNEditor"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/kentis/CPNEditor/tree/main/packages/cpn-semantics",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/cjs/index.cjs",
|
|
21
|
+
"module": "./dist/esm/index.js",
|
|
22
|
+
"types": "./dist/types/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/types/index.d.ts",
|
|
26
|
+
"import": "./dist/esm/index.js",
|
|
27
|
+
"require": "./dist/cjs/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"examples",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "rm -rf dist tsconfig.tsbuildinfo && tsup && tsc -p tsconfig.json --emitDeclarationOnly",
|
|
41
|
+
"examples": "pnpm run build && node examples/traffic-light.mjs && node examples/producer-consumer.mjs && node examples/sml-integration.mjs",
|
|
42
|
+
"test": "pnpm run build && vitest run",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^6.0.2",
|
|
48
|
+
"vitest": "^4.1.4"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@sosml/interpreter": "^1.6.11"
|
|
52
|
+
}
|
|
53
|
+
}
|