openprocess 0.7.0__py3-none-any.whl
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.
- cpnpy/__init__.py +46 -0
- openprocess/__init__.py +57 -0
- openprocess/analysis/__init__.py +0 -0
- openprocess/analysis/state_space.py +521 -0
- openprocess/analysis/state_space_process.py +251 -0
- openprocess/cli.py +742 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +31 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +38 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +27 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +29 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +65 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +1 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +67 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +75 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +5 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +27 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +65 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +1 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +62 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +1 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +28 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +30 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +69 -0
- openprocess/exercises/pack.md +14 -0
- openprocess/flow/__init__.py +50 -0
- openprocess/flow/box.py +466 -0
- openprocess/flow/boxes/__init__.py +7 -0
- openprocess/flow/boxes/check.py +119 -0
- openprocess/flow/boxes/compare.py +16 -0
- openprocess/flow/boxes/cpn.py +53 -0
- openprocess/flow/boxes/discover.py +124 -0
- openprocess/flow/boxes/filter.py +80 -0
- openprocess/flow/boxes/input.py +124 -0
- openprocess/flow/boxes/output.py +52 -0
- openprocess/flow/boxes/predict.py +186 -0
- openprocess/flow/boxes/science.py +159 -0
- openprocess/flow/boxes/sweeps.py +18 -0
- openprocess/flow/convert.py +187 -0
- openprocess/flow/datasets.py +198 -0
- openprocess/flow/explain.py +115 -0
- openprocess/flow/library.py +222 -0
- openprocess/flow/record.py +385 -0
- openprocess/flow/runner.py +357 -0
- openprocess/flow/sweep.py +92 -0
- openprocess/flow/types.py +290 -0
- openprocess/flow/workflow.py +628 -0
- openprocess/gui/__init__.py +0 -0
- openprocess/gui/app.py +90 -0
- openprocess/gui/arc_editing.py +295 -0
- openprocess/gui/canvas.py +1414 -0
- openprocess/gui/flow/__init__.py +8 -0
- openprocess/gui/flow/canvas.py +854 -0
- openprocess/gui/flow/page.py +972 -0
- openprocess/gui/flow/templates.py +131 -0
- openprocess/gui/flow/viewers.py +665 -0
- openprocess/gui/items.py +1275 -0
- openprocess/gui/learn/answer_boxes.py +978 -0
- openprocess/gui/learn/concealment.py +91 -0
- openprocess/gui/learn/mode.py +1181 -0
- openprocess/gui/panning.py +241 -0
- openprocess/gui/resources/openprocess-icon.png +0 -0
- openprocess/gui/studio/__init__.py +1 -0
- openprocess/gui/studio/__main__.py +3 -0
- openprocess/gui/studio/app.py +4031 -0
- openprocess/gui/studio/charts.py +115 -0
- openprocess/gui/studio/compare_page.py +487 -0
- openprocess/gui/studio/cpn_page.py +1858 -0
- openprocess/gui/studio/definition_view.py +284 -0
- openprocess/gui/studio/derivation_view.py +421 -0
- openprocess/gui/studio/documents.py +152 -0
- openprocess/gui/studio/dotted_chart.py +1401 -0
- openprocess/gui/studio/file_dialogs.py +143 -0
- openprocess/gui/studio/filter_dialog.py +247 -0
- openprocess/gui/studio/graph_builders.py +176 -0
- openprocess/gui/studio/graph_view.py +682 -0
- openprocess/gui/studio/instances.py +413 -0
- openprocess/gui/studio/log_editor.py +675 -0
- openprocess/gui/studio/log_page.py +800 -0
- openprocess/gui/studio/markdown_view.py +127 -0
- openprocess/gui/studio/mathtext.py +260 -0
- openprocess/gui/studio/ml_highlighter.py +75 -0
- openprocess/gui/studio/model_page.py +760 -0
- openprocess/gui/studio/net_comparison.py +124 -0
- openprocess/gui/studio/notes_overlay.py +275 -0
- openprocess/gui/studio/petri_page.py +844 -0
- openprocess/gui/studio/regions_view.py +502 -0
- openprocess/gui/studio/sidebar.py +149 -0
- openprocess/gui/studio/style.py +503 -0
- openprocess/gui/studio/tool_icons.py +134 -0
- openprocess/gui/studio/updates.py +439 -0
- openprocess/gui/studio/widgets.py +899 -0
- openprocess/gui/studio/workers.py +60 -0
- openprocess/gui/studio/workspace.py +447 -0
- openprocess/gui/theme.py +394 -0
- openprocess/gui/tidy.py +86 -0
- openprocess/io/__init__.py +0 -0
- openprocess/io/cpn_reader.py +389 -0
- openprocess/io/cpn_writer.py +357 -0
- openprocess/learn/__init__.py +23 -0
- openprocess/learn/answers.py +188 -0
- openprocess/learn/checks.py +953 -0
- openprocess/learn/computed.py +1180 -0
- openprocess/learn/context.py +145 -0
- openprocess/learn/exam.py +169 -0
- openprocess/learn/exercise-packs.md +325 -0
- openprocess/learn/importer.py +216 -0
- openprocess/learn/notation.py +474 -0
- openprocess/learn/pack.py +511 -0
- openprocess/learn/sheet.py +296 -0
- openprocess/mining/__init__.py +73 -0
- openprocess/mining/analysis.py +689 -0
- openprocess/mining/columns.py +282 -0
- openprocess/mining/compare_nets.py +246 -0
- openprocess/mining/conformance/__init__.py +0 -0
- openprocess/mining/conformance/alignments.py +263 -0
- openprocess/mining/conformance/quality.py +145 -0
- openprocess/mining/conformance/token_replay.py +252 -0
- openprocess/mining/csv_import.py +222 -0
- openprocess/mining/definitions.py +584 -0
- openprocess/mining/dfg.py +187 -0
- openprocess/mining/discovery/__init__.py +0 -0
- openprocess/mining/discovery/alpha.py +168 -0
- openprocess/mining/discovery/heuristics.py +332 -0
- openprocess/mining/discovery/inductive.py +477 -0
- openprocess/mining/discovery/state_regions.py +62 -0
- openprocess/mining/filtering.py +237 -0
- openprocess/mining/footprint.py +183 -0
- openprocess/mining/invariants.py +191 -0
- openprocess/mining/layout.py +279 -0
- openprocess/mining/log.py +364 -0
- openprocess/mining/petrinet.py +354 -0
- openprocess/mining/playout.py +75 -0
- openprocess/mining/pm4py_bridge.py +82 -0
- openprocess/mining/pnml.py +223 -0
- openprocess/mining/processtree.py +216 -0
- openprocess/mining/regions.py +476 -0
- openprocess/mining/stats.py +160 -0
- openprocess/mining/structure.py +374 -0
- openprocess/mining/transition_system.py +409 -0
- openprocess/mining/xes.py +399 -0
- openprocess/ml/__init__.py +0 -0
- openprocess/ml/ast_nodes.py +332 -0
- openprocess/ml/builtins.py +364 -0
- openprocess/ml/colorsets.py +522 -0
- openprocess/ml/errors.py +60 -0
- openprocess/ml/evaluator.py +754 -0
- openprocess/ml/lexer.py +277 -0
- openprocess/ml/multiset.py +417 -0
- openprocess/ml/parser.py +737 -0
- openprocess/ml/values.py +319 -0
- openprocess/model/__init__.py +0 -0
- openprocess/model/declarations.py +617 -0
- openprocess/model/examples.py +98 -0
- openprocess/model/net.py +701 -0
- openprocess/model/plain.py +192 -0
- openprocess/references.py +280 -0
- openprocess/sim/__init__.py +0 -0
- openprocess/sim/binding.py +620 -0
- openprocess/sim/export.py +66 -0
- openprocess/sim/simulator.py +315 -0
- openprocess/teaching/__init__.py +4 -0
- openprocess/teaching/answers.py +4 -0
- openprocess/teaching/checks.py +5 -0
- openprocess/teaching/pack.py +4 -0
- openprocess/teaching/sheet.py +4 -0
- openprocess-0.7.0.dist-info/METADATA +927 -0
- openprocess-0.7.0.dist-info/RECORD +173 -0
- openprocess-0.7.0.dist-info/WHEEL +5 -0
- openprocess-0.7.0.dist-info/entry_points.txt +6 -0
- openprocess-0.7.0.dist-info/licenses/LICENSE +21 -0
- openprocess-0.7.0.dist-info/top_level.txt +2 -0
openprocess/ml/values.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""Runtime values of the CPN ML subset.
|
|
2
|
+
|
|
3
|
+
Design decisions worth stating up front, because everything else depends on
|
|
4
|
+
them:
|
|
5
|
+
|
|
6
|
+
1. **Values must be hashable.** A marking is a multiset of values, and we
|
|
7
|
+
implement multisets with dictionaries. So every value we can put in a place
|
|
8
|
+
has to be usable as a dict key. That rules out Python ``list`` and ``dict``
|
|
9
|
+
as representations and is why :class:`MLList` and :class:`Record` exist.
|
|
10
|
+
|
|
11
|
+
2. **Values must be totally ordered.** State space analysis, canonical
|
|
12
|
+
printing of markings, and deterministic test output all need a stable order
|
|
13
|
+
over values of *different* shapes. Python will happily compare two ints but
|
|
14
|
+
refuses to compare an int with a string, so we define our own ordering via
|
|
15
|
+
:func:`sort_key`, which prefixes a type rank.
|
|
16
|
+
|
|
17
|
+
3. **We reuse Python primitives where the semantics coincide.** CPN ML's
|
|
18
|
+
``INT``, ``BOOL``, ``STRING`` and ``REAL`` map onto Python ``int``, ``bool``,
|
|
19
|
+
``str`` and ``float`` exactly. Only the constructs Python lacks (ML lists
|
|
20
|
+
as values, records, and union constructors) get wrapper classes.
|
|
21
|
+
|
|
22
|
+
Mapping from CPN ML to this module:
|
|
23
|
+
|
|
24
|
+
================== ==========================================
|
|
25
|
+
CPN ML Python representation
|
|
26
|
+
================== ==========================================
|
|
27
|
+
unit ``()`` :data:`UNIT` (the single ``Unit`` instance)
|
|
28
|
+
``true`` / ``false`` ``bool``
|
|
29
|
+
``3`` ``int``
|
|
30
|
+
``3.0`` ``float``
|
|
31
|
+
``"abc"`` ``str``
|
|
32
|
+
``(a, b)`` ``tuple`` (product colour set)
|
|
33
|
+
``[a, b]`` :class:`MLList`
|
|
34
|
+
``{f=a, g=b}`` :class:`Record`
|
|
35
|
+
``Cons(x)`` :class:`Constructor` (union colour set / enum)
|
|
36
|
+
================== ==========================================
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from typing import Any, Iterable, Iterator
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# unit
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
class Unit:
|
|
48
|
+
"""The single value ``()`` of the ``UNIT`` colour set.
|
|
49
|
+
|
|
50
|
+
Implemented as a singleton so that ``UNIT is UNIT`` and equality is cheap.
|
|
51
|
+
In CPN models the unit colour set is used for tokens that carry no data --
|
|
52
|
+
i.e. exactly the tokens of a classical place/transition Petri net.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_instance: "Unit | None" = None
|
|
56
|
+
|
|
57
|
+
def __new__(cls) -> "Unit":
|
|
58
|
+
if cls._instance is None:
|
|
59
|
+
cls._instance = super().__new__(cls)
|
|
60
|
+
return cls._instance
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return "()"
|
|
64
|
+
|
|
65
|
+
__str__ = __repr__
|
|
66
|
+
|
|
67
|
+
def __eq__(self, other: object) -> bool:
|
|
68
|
+
return isinstance(other, Unit)
|
|
69
|
+
|
|
70
|
+
def __hash__(self) -> int:
|
|
71
|
+
return hash("()")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
UNIT = Unit()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# lists
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
class MLList:
|
|
81
|
+
"""An immutable CPN ML list value, e.g. ``[1, 2, 3]``.
|
|
82
|
+
|
|
83
|
+
We cannot use a Python ``tuple`` because a tuple already represents a
|
|
84
|
+
*product* value, and ``(1, 2)`` and ``[1, 2]`` are different CPN values
|
|
85
|
+
belonging to different colour sets. Backed by a tuple internally so that
|
|
86
|
+
hashing and equality come for free.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
__slots__ = ("items",)
|
|
90
|
+
|
|
91
|
+
def __init__(self, items: Iterable[Any] = ()) -> None:
|
|
92
|
+
self.items: tuple[Any, ...] = tuple(items)
|
|
93
|
+
|
|
94
|
+
# -- sequence protocol ---------------------------------------------------
|
|
95
|
+
def __iter__(self) -> Iterator[Any]:
|
|
96
|
+
return iter(self.items)
|
|
97
|
+
|
|
98
|
+
def __len__(self) -> int:
|
|
99
|
+
return len(self.items)
|
|
100
|
+
|
|
101
|
+
def __getitem__(self, index: int) -> Any:
|
|
102
|
+
return self.items[index]
|
|
103
|
+
|
|
104
|
+
# -- value protocol ------------------------------------------------------
|
|
105
|
+
def __eq__(self, other: object) -> bool:
|
|
106
|
+
return isinstance(other, MLList) and self.items == other.items
|
|
107
|
+
|
|
108
|
+
def __hash__(self) -> int:
|
|
109
|
+
return hash(("MLList", self.items))
|
|
110
|
+
|
|
111
|
+
def __repr__(self) -> str:
|
|
112
|
+
return "[" + ",".join(format_value(x) for x in self.items) + "]"
|
|
113
|
+
|
|
114
|
+
# -- ML operations -------------------------------------------------------
|
|
115
|
+
def cons(self, head: Any) -> "MLList":
|
|
116
|
+
"""``head :: self`` -- prepend, returning a new list."""
|
|
117
|
+
return MLList((head,) + self.items)
|
|
118
|
+
|
|
119
|
+
def append(self, other: "MLList") -> "MLList":
|
|
120
|
+
"""``self ^^ other`` / ``self @ other`` -- concatenation."""
|
|
121
|
+
return MLList(self.items + other.items)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# records
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
class Record:
|
|
128
|
+
"""An immutable CPN ML record value, e.g. ``{name = "a", age = 3}``.
|
|
129
|
+
|
|
130
|
+
Field order is preserved as given by the colour set declaration, because
|
|
131
|
+
CPN Tools prints records in declaration order and we want byte-identical
|
|
132
|
+
round-trips. Equality and hashing ignore order only in the sense that two
|
|
133
|
+
records with the same field/value pairs in the same declared order are
|
|
134
|
+
equal -- CPN ML has no notion of two records with permuted fields, so this
|
|
135
|
+
is not a limitation.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
__slots__ = ("fields",)
|
|
139
|
+
|
|
140
|
+
def __init__(self, fields: Iterable[tuple[str, Any]] | dict[str, Any]) -> None:
|
|
141
|
+
if isinstance(fields, dict):
|
|
142
|
+
fields = tuple(fields.items())
|
|
143
|
+
self.fields: tuple[tuple[str, Any], ...] = tuple(fields)
|
|
144
|
+
|
|
145
|
+
def get(self, name: str) -> Any:
|
|
146
|
+
"""Field selection, i.e. ML's ``#name record``."""
|
|
147
|
+
for field_name, value in self.fields:
|
|
148
|
+
if field_name == name:
|
|
149
|
+
return value
|
|
150
|
+
raise KeyError(name)
|
|
151
|
+
|
|
152
|
+
def names(self) -> tuple[str, ...]:
|
|
153
|
+
return tuple(n for n, _ in self.fields)
|
|
154
|
+
|
|
155
|
+
def __eq__(self, other: object) -> bool:
|
|
156
|
+
return isinstance(other, Record) and self.fields == other.fields
|
|
157
|
+
|
|
158
|
+
def __hash__(self) -> int:
|
|
159
|
+
return hash(("Record", self.fields))
|
|
160
|
+
|
|
161
|
+
def __repr__(self) -> str:
|
|
162
|
+
inner = ",".join(f"{n}={format_value(v)}" for n, v in self.fields)
|
|
163
|
+
return "{" + inner + "}"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# union / enumeration constructors
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
class Constructor:
|
|
170
|
+
"""A tagged value from a union or enumeration colour set.
|
|
171
|
+
|
|
172
|
+
Two shapes exist and both are represented here:
|
|
173
|
+
|
|
174
|
+
* ``colset E = with red | green | blue;`` -- an *enumeration*. Its values
|
|
175
|
+
are constructors with ``argument is None``: ``Constructor("red")``.
|
|
176
|
+
* ``colset U = union Car:CARS + Bike:BIKES;`` -- a *union*. Its values
|
|
177
|
+
carry a payload: ``Constructor("Car", some_car_value)``.
|
|
178
|
+
|
|
179
|
+
Keeping them in one class means pattern matching, printing and ordering
|
|
180
|
+
only have to handle one case.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
__slots__ = ("name", "argument", "_has_arg")
|
|
184
|
+
|
|
185
|
+
# Sentinel distinguishing "no argument at all" (enumeration constant) from
|
|
186
|
+
# "argument that happens to be unit".
|
|
187
|
+
_NO_ARG = object()
|
|
188
|
+
|
|
189
|
+
def __init__(self, name: str, argument: Any = _NO_ARG) -> None:
|
|
190
|
+
self.name = name
|
|
191
|
+
self.argument = None if argument is Constructor._NO_ARG else argument
|
|
192
|
+
self._has_arg = argument is not Constructor._NO_ARG
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def has_argument(self) -> bool:
|
|
196
|
+
return self._has_arg
|
|
197
|
+
|
|
198
|
+
def __eq__(self, other: object) -> bool:
|
|
199
|
+
return (
|
|
200
|
+
isinstance(other, Constructor)
|
|
201
|
+
and self.name == other.name
|
|
202
|
+
and self._has_arg == other._has_arg
|
|
203
|
+
and self.argument == other.argument
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def __hash__(self) -> int:
|
|
207
|
+
return hash(("Constructor", self.name, self._has_arg, self.argument))
|
|
208
|
+
|
|
209
|
+
def __repr__(self) -> str:
|
|
210
|
+
if not self._has_arg:
|
|
211
|
+
return self.name
|
|
212
|
+
return f"{self.name}({format_value(self.argument)})"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
# ordering and printing
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
# Rank of each value shape. Values of different shapes are ordered by rank
|
|
219
|
+
# first, which gives us a total order over the whole value universe. The
|
|
220
|
+
# specific numbers are arbitrary but must never change, or saved state spaces
|
|
221
|
+
# would reorder between versions.
|
|
222
|
+
_TYPE_RANK: dict[type, int] = {
|
|
223
|
+
Unit: 0,
|
|
224
|
+
bool: 1,
|
|
225
|
+
int: 2,
|
|
226
|
+
float: 3,
|
|
227
|
+
str: 4,
|
|
228
|
+
Constructor: 5,
|
|
229
|
+
tuple: 6,
|
|
230
|
+
MLList: 7,
|
|
231
|
+
Record: 8,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
_SORT_KEYS: dict = {}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def sort_key(value: Any) -> tuple:
|
|
239
|
+
"""Memoised :func:`_sort_key` -- the simulator sorts the same token
|
|
240
|
+
values over and over, and building nested key tuples dominated its run
|
|
241
|
+
time. Values are immutable and hashable, so caching is safe; the cache
|
|
242
|
+
is bounded so a long run cannot grow it without limit."""
|
|
243
|
+
try:
|
|
244
|
+
return _SORT_KEYS[value]
|
|
245
|
+
except KeyError:
|
|
246
|
+
pass
|
|
247
|
+
except TypeError: # an unhashable value: compute directly
|
|
248
|
+
return _sort_key(value)
|
|
249
|
+
key = _sort_key(value)
|
|
250
|
+
if len(_SORT_KEYS) > 200_000:
|
|
251
|
+
_SORT_KEYS.clear()
|
|
252
|
+
_SORT_KEYS[value] = key
|
|
253
|
+
return key
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _sort_key(value: Any) -> tuple:
|
|
257
|
+
"""Return a tuple that sorts values of *any* shape into a total order.
|
|
258
|
+
|
|
259
|
+
Used for canonical marking output and for deterministic iteration during
|
|
260
|
+
state space exploration. The recursion mirrors the value structure, so
|
|
261
|
+
``sort_key`` on nested values is itself nested and compares element-wise,
|
|
262
|
+
exactly like ML's structural comparison.
|
|
263
|
+
"""
|
|
264
|
+
# ``bool`` must be tested before ``int`` because ``bool`` subclasses ``int``.
|
|
265
|
+
if isinstance(value, Unit):
|
|
266
|
+
return (_TYPE_RANK[Unit],)
|
|
267
|
+
if isinstance(value, bool):
|
|
268
|
+
return (_TYPE_RANK[bool], value)
|
|
269
|
+
if isinstance(value, int):
|
|
270
|
+
return (_TYPE_RANK[int], value)
|
|
271
|
+
if isinstance(value, float):
|
|
272
|
+
return (_TYPE_RANK[float], value)
|
|
273
|
+
if isinstance(value, str):
|
|
274
|
+
return (_TYPE_RANK[str], value)
|
|
275
|
+
if isinstance(value, Constructor):
|
|
276
|
+
arg = sort_key(value.argument) if value.has_argument else ()
|
|
277
|
+
return (_TYPE_RANK[Constructor], value.name, arg)
|
|
278
|
+
if isinstance(value, tuple):
|
|
279
|
+
return (_TYPE_RANK[tuple], tuple(sort_key(v) for v in value))
|
|
280
|
+
if isinstance(value, MLList):
|
|
281
|
+
return (_TYPE_RANK[MLList], tuple(sort_key(v) for v in value.items))
|
|
282
|
+
if isinstance(value, Record):
|
|
283
|
+
return (
|
|
284
|
+
_TYPE_RANK[Record],
|
|
285
|
+
tuple((n, sort_key(v)) for n, v in value.fields),
|
|
286
|
+
)
|
|
287
|
+
# Anything else is a bug in the evaluator, not a user error.
|
|
288
|
+
raise AssertionError(f"value of unsupported shape: {value!r}")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def format_value(value: Any) -> str:
|
|
292
|
+
"""Render a value the way CPN Tools would print it.
|
|
293
|
+
|
|
294
|
+
Notable conventions we follow deliberately:
|
|
295
|
+
|
|
296
|
+
* strings are double-quoted;
|
|
297
|
+
* reals always show a decimal point (``3.0``, not ``3``) so that they are
|
|
298
|
+
visually distinct from integers;
|
|
299
|
+
* negative numbers use ML's ``~`` prefix, not ``-`` (``~3``), because that
|
|
300
|
+
is what CPN Tools writes and what our lexer reads back.
|
|
301
|
+
"""
|
|
302
|
+
if isinstance(value, Unit):
|
|
303
|
+
return "()"
|
|
304
|
+
if isinstance(value, bool):
|
|
305
|
+
return "true" if value else "false"
|
|
306
|
+
if isinstance(value, int):
|
|
307
|
+
return f"~{-value}" if value < 0 else str(value)
|
|
308
|
+
if isinstance(value, float):
|
|
309
|
+
text = repr(value)
|
|
310
|
+
if value < 0:
|
|
311
|
+
text = "~" + repr(-value)
|
|
312
|
+
return text
|
|
313
|
+
if isinstance(value, str):
|
|
314
|
+
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
|
|
315
|
+
return f'"{escaped}"'
|
|
316
|
+
if isinstance(value, tuple):
|
|
317
|
+
return "(" + ",".join(format_value(v) for v in value) + ")"
|
|
318
|
+
# MLList, Record and Constructor already format themselves in __repr__.
|
|
319
|
+
return repr(value)
|
|
File without changes
|