model-checker 0.7.2__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.
- __init__.py +0 -0
- model_checker/__init__.py +78 -0
- model_checker/__main__.py +547 -0
- model_checker/builder.py +134 -0
- model_checker/champollion.py +941 -0
- model_checker/defined.py +296 -0
- model_checker/examples.py +525 -0
- model_checker/model.py +694 -0
- model_checker/primitive.py +946 -0
- model_checker/semantic.py +568 -0
- model_checker/syntactic.py +497 -0
- model_checker/template/__init__.py +0 -0
- model_checker/template/examples.py +432 -0
- model_checker/template/operators.py +960 -0
- model_checker/template/semantic.py +561 -0
- model_checker/utils.py +293 -0
- model_checker-0.7.2.dist-info/LICENCE +19 -0
- model_checker-0.7.2.dist-info/METADATA +204 -0
- model_checker-0.7.2.dist-info/RECORD +22 -0
- model_checker-0.7.2.dist-info/WHEEL +5 -0
- model_checker-0.7.2.dist-info/entry_points.txt +2 -0
- model_checker-0.7.2.dist-info/top_level.txt +2 -0
__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# __init__.py
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
try:
|
|
4
|
+
__version__ = version("model-checker")
|
|
5
|
+
except ImportError:
|
|
6
|
+
__version__ = "unknown"
|
|
7
|
+
|
|
8
|
+
# Import specific items from utils
|
|
9
|
+
from .utils import (
|
|
10
|
+
ForAll,
|
|
11
|
+
Exists,
|
|
12
|
+
bitvec_to_substates,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Import model as a whole
|
|
16
|
+
from . import model
|
|
17
|
+
|
|
18
|
+
# Import syntactic as a whole
|
|
19
|
+
from . import syntactic
|
|
20
|
+
|
|
21
|
+
# Import specific items from utils
|
|
22
|
+
from .builder import (
|
|
23
|
+
make_model_for,
|
|
24
|
+
find_max_N,
|
|
25
|
+
translate,
|
|
26
|
+
# run_comparison, # update
|
|
27
|
+
# save_comparisons, # update
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Import specific items from semantic
|
|
31
|
+
from .semantic import (
|
|
32
|
+
Semantics,
|
|
33
|
+
ImpositionSemantics,
|
|
34
|
+
Proposition,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Import specific items from primitive
|
|
38
|
+
from .primitive import (
|
|
39
|
+
AndOperator, NegationOperator, OrOperator, # extensional
|
|
40
|
+
TopOperator, BotOperator, # top and bottom
|
|
41
|
+
IdentityOperator, GroundOperator, EssenceOperator, # constitutive
|
|
42
|
+
RelevanceOperator, # relevance
|
|
43
|
+
NecessityOperator, PossibilityOperator, # modal
|
|
44
|
+
CounterfactualOperator, ImpositionOperator, # counterfactual
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from .defined import (
|
|
48
|
+
ConditionalOperator, BiconditionalOperator, # extensional
|
|
49
|
+
DefEssenceOperator, DefGroundOperator, # constitutive
|
|
50
|
+
MightCounterfactualOperator, MightImpositionOperator, # counterfactual
|
|
51
|
+
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Import specific items from __main__
|
|
56
|
+
from .__main__ import (
|
|
57
|
+
BuildModule,
|
|
58
|
+
BuildExample,
|
|
59
|
+
main,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Define the public API of the package
|
|
63
|
+
__all__ = [
|
|
64
|
+
"model", "syntactic",
|
|
65
|
+
"ForAll", "Exists", "bitvec_to_substates",
|
|
66
|
+
"make_model_for", "find_max_N", "translate", #"run_comparison", "save_comparisons",
|
|
67
|
+
"Semantics", "ImpositionSemantics", "Proposition",
|
|
68
|
+
"ConditionalOperator", "BiconditionalOperator",
|
|
69
|
+
"DefEssenceOperator", "DefGroundOperator",
|
|
70
|
+
"MightCounterfactualOperator", "MightImpositionOperator",
|
|
71
|
+
"AndOperator", "NegationOperator", "OrOperator",
|
|
72
|
+
"TopOperator", "BotOperator",
|
|
73
|
+
"IdentityOperator", "GroundOperator", "EssenceOperator",
|
|
74
|
+
"RelevanceOperator",
|
|
75
|
+
"NecessityOperator", "PossibilityOperator",
|
|
76
|
+
"CounterfactualOperator", "ImpositionOperator",
|
|
77
|
+
"BuildModule", "BuildExample", "main",
|
|
78
|
+
]
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
'''
|
|
2
|
+
file specifies premises, conclusions, and settings.
|
|
3
|
+
running the file finds a model and prints the result.
|
|
4
|
+
to test from the Code/ directory, run: python -m src.model_checker
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import argparse
|
|
12
|
+
import importlib.util
|
|
13
|
+
# Try local imports first (for development)
|
|
14
|
+
try:
|
|
15
|
+
from src.model_checker import __version__
|
|
16
|
+
from src.model_checker.builder import (
|
|
17
|
+
make_model_for,
|
|
18
|
+
translate,
|
|
19
|
+
)
|
|
20
|
+
from src.model_checker.model import (
|
|
21
|
+
SemanticDefaults,
|
|
22
|
+
PropositionDefaults
|
|
23
|
+
)
|
|
24
|
+
from src.model_checker.syntactic import OperatorCollection
|
|
25
|
+
except ImportError:
|
|
26
|
+
# Fall back to installed package imports
|
|
27
|
+
from model_checker import __version__
|
|
28
|
+
from model_checker.builder import (
|
|
29
|
+
make_model_for,
|
|
30
|
+
translate,
|
|
31
|
+
)
|
|
32
|
+
from model_checker.model import (
|
|
33
|
+
SemanticDefaults,
|
|
34
|
+
PropositionDefaults
|
|
35
|
+
)
|
|
36
|
+
from model_checker.syntactic import OperatorCollection
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BuildModule:
|
|
40
|
+
"""Load and store module values with settings and validation.
|
|
41
|
+
|
|
42
|
+
This class handles loading a Python module and extracting its model checking
|
|
43
|
+
configuration including premises, conclusions, and various settings.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
DEFAULT_GENERAL_SETTINGS = {
|
|
47
|
+
"print_impossible": False,
|
|
48
|
+
"print_constraints": False,
|
|
49
|
+
"save_output": False,
|
|
50
|
+
# "optimize": False,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def __init__(self, module_flags):
|
|
54
|
+
"""Initialize BuildModule with module name and path.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
module_name: Name of the module to load
|
|
58
|
+
module_path: File path to the module
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ImportError: If module cannot be loaded
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
self.module_flags = module_flags
|
|
65
|
+
self.module_path = module_flags.file_path
|
|
66
|
+
self.module_name = os.path.splitext(os.path.basename(self.module_path))[0]
|
|
67
|
+
self.module = self._load_module()
|
|
68
|
+
|
|
69
|
+
# Load core attributes
|
|
70
|
+
self.semantic_theories = self._load_attribute("semantic_theories")
|
|
71
|
+
self.example_range = self._load_attribute("example_range")
|
|
72
|
+
|
|
73
|
+
# Load general settings
|
|
74
|
+
self.general_settings = self._load_general_settings()
|
|
75
|
+
|
|
76
|
+
def _load_module(self):
|
|
77
|
+
"""Load the Python module from file.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
The loaded module object
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ImportError: If module cannot be loaded
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
spec = importlib.util.spec_from_file_location(self.module_name, self.module_path)
|
|
87
|
+
if spec is None or spec.loader is None:
|
|
88
|
+
raise ImportError("Module spec could not be loaded.")
|
|
89
|
+
module = importlib.util.module_from_spec(spec)
|
|
90
|
+
spec.loader.exec_module(module)
|
|
91
|
+
return module
|
|
92
|
+
except Exception as e:
|
|
93
|
+
raise ImportError(f"Failed to load the module '{self.module_name}': {e}") from e
|
|
94
|
+
|
|
95
|
+
def _load_attribute(self, attr_name):
|
|
96
|
+
"""Checks if an attribute exists in the module and store it.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
attr_name: Name of the attribute to check and store
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
AttributeError: If the attribute is missing from the module
|
|
103
|
+
"""
|
|
104
|
+
if not hasattr(self.module, attr_name):
|
|
105
|
+
raise AttributeError(
|
|
106
|
+
f"Module '{self.module_name}' is missing the attribute '{attr_name}'. "
|
|
107
|
+
)
|
|
108
|
+
return getattr(self.module, attr_name, {})
|
|
109
|
+
|
|
110
|
+
def _load_general_settings(self):
|
|
111
|
+
"""Initialize all settings from module with defaults."""
|
|
112
|
+
general_settings = getattr(self.module, "general_settings", {})
|
|
113
|
+
|
|
114
|
+
# Initialize each setting from module_settings with fallback to defaults
|
|
115
|
+
for key, default_value in self.DEFAULT_GENERAL_SETTINGS.items():
|
|
116
|
+
if key not in general_settings:
|
|
117
|
+
print(f"Warning: Module {self.module_name} settings missing '{key}', using default value: {default_value}")
|
|
118
|
+
setattr(self, key, general_settings.get(key, default_value))
|
|
119
|
+
|
|
120
|
+
# Store complete settings dict
|
|
121
|
+
general_settings = {
|
|
122
|
+
key: getattr(self, key)
|
|
123
|
+
for key in self.DEFAULT_GENERAL_SETTINGS
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return general_settings
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class BuildExample:
|
|
130
|
+
"""Class to create and store model structure with settings as attributes."""
|
|
131
|
+
|
|
132
|
+
DEFAULT_EXAMPLE = {
|
|
133
|
+
"premises": [],
|
|
134
|
+
"conclusions": [],
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
DEFAULT_EXAMPLE_SETTINGS = {
|
|
138
|
+
"N": 3,
|
|
139
|
+
"contingent": False,
|
|
140
|
+
"non_empty": False,
|
|
141
|
+
"non_null": False,
|
|
142
|
+
"disjoint": False,
|
|
143
|
+
"max_time": 1,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def __init__(self, module, semantic_theory, example_case):
|
|
147
|
+
|
|
148
|
+
"""Initialize model structure from module flags."""
|
|
149
|
+
self.module = module
|
|
150
|
+
self.semantics, self.proposition, self.operators, self.dictionary = self._validate_semantic_theory(semantic_theory)
|
|
151
|
+
self.premises, self.conclusions, example_settings = self._validate_example(example_case)
|
|
152
|
+
self.settings = self._validate_settings(example_settings)
|
|
153
|
+
|
|
154
|
+
# # TODO: warn if no premises or no conclusions
|
|
155
|
+
# # Check for missing example defaults
|
|
156
|
+
# for key, default_val in self.DEFAULT_EXAMPLE.items():
|
|
157
|
+
# module_val = getattr(self.module, key, None)
|
|
158
|
+
# if module_val is None:
|
|
159
|
+
# print(f"Warning: Module {self.module_name} is missing '{key}', using default value: {default_val}")
|
|
160
|
+
|
|
161
|
+
# # TODO: warn if missing setting
|
|
162
|
+
# # Check for missing settings defaults
|
|
163
|
+
# module_settings = getattr(self.module, "settings", {})
|
|
164
|
+
# for key, default_val in self.DEFAULT_GENERAL_SETTINGS.items():
|
|
165
|
+
# if key not in module_settings:
|
|
166
|
+
# print(f"Warning: Module {self.module_name} settings missing '{key}', using default value: {default_val}")
|
|
167
|
+
|
|
168
|
+
# # Set settings as instance attributes
|
|
169
|
+
# for key, value in self.settings.items():
|
|
170
|
+
# setattr(self, key, value)
|
|
171
|
+
|
|
172
|
+
# Create model structure
|
|
173
|
+
self.model_structure = make_model_for(
|
|
174
|
+
self.settings,
|
|
175
|
+
self.premises,
|
|
176
|
+
self.conclusions,
|
|
177
|
+
self.semantics,
|
|
178
|
+
self.proposition,
|
|
179
|
+
self.operators,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def _validate_semantic_theory(self, semantic_theory):
|
|
183
|
+
"""Validates that semantic_theory contains the required components in the correct order.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
semantic_theory: A tuple/list containing (Semantics, Proposition, OperatorCollection)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The validated semantic_theory
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
TypeError: If any component has an invalid type
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
if not isinstance(semantic_theory, dict) or len(semantic_theory) < 3:
|
|
196
|
+
raise TypeError(
|
|
197
|
+
"semantic_theory must be a tuple/list containing exactly 3 elements: "
|
|
198
|
+
"(Semantics, Proposition, OperatorCollection)"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
semantics = semantic_theory["semantics"]
|
|
202
|
+
proposition = semantic_theory["proposition"]
|
|
203
|
+
operators = semantic_theory["operators"]
|
|
204
|
+
dictionary = semantic_theory.get("operators", None)
|
|
205
|
+
|
|
206
|
+
# Validate semantics is subclass of SemanticDefaults
|
|
207
|
+
if not issubclass(semantic_theory["semantics"], SemanticDefaults):
|
|
208
|
+
raise TypeError(
|
|
209
|
+
f"First element must be a subclass of SemanticDefaults, got {type(semantics)}"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Validate proposition is subclass of PropositionDefaults
|
|
213
|
+
if not issubclass(proposition, PropositionDefaults):
|
|
214
|
+
raise TypeError(
|
|
215
|
+
f"Second element must be a subclass of PropositionDefaults, got {type(proposition)}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Validate operators is instance of OperatorCollection
|
|
219
|
+
if not isinstance(operators, OperatorCollection):
|
|
220
|
+
raise TypeError(
|
|
221
|
+
f"Third element must be an instance of OperatorCollection, got {type(operators)}"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return semantics, proposition, operators, dictionary
|
|
225
|
+
|
|
226
|
+
def _validate_example(self, example_case):
|
|
227
|
+
"""Validates that example_case contains lists of strings and a dictionary.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
example_case: A tuple/list containing (premises, conclusions, settings)
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
The validated example_case
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
TypeError: If any component has an invalid type
|
|
237
|
+
"""
|
|
238
|
+
if not isinstance(example_case, (tuple, list)) or len(example_case) != 3:
|
|
239
|
+
raise TypeError(
|
|
240
|
+
"example_case must be a tuple/list containing exactly 3 elements: "
|
|
241
|
+
"(premises, conclusions, settings)"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
premises, conclusions, example_settings = example_case
|
|
245
|
+
|
|
246
|
+
# Validate premises is a list of strings
|
|
247
|
+
if not isinstance(premises, list) or not all(isinstance(p, str) for p in premises):
|
|
248
|
+
raise TypeError(
|
|
249
|
+
f"First element must be a list of strings, got {type(premises)} "
|
|
250
|
+
f"containing types {[type(p) for p in premises]}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Validate conclusions is a list of strings
|
|
254
|
+
if not isinstance(conclusions, list) or not all(isinstance(c, str) for c in conclusions):
|
|
255
|
+
raise TypeError(
|
|
256
|
+
f"Second element must be a list of strings, got {type(conclusions)} "
|
|
257
|
+
f"containing types {[type(c) for c in conclusions]}"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Validate settings is a dictionary
|
|
261
|
+
if not isinstance(example_settings, dict):
|
|
262
|
+
raise TypeError(
|
|
263
|
+
f"Third element must be a dictionary, got {type(example_settings)}"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return example_case
|
|
267
|
+
|
|
268
|
+
def _validate_settings(self, example_settings):
|
|
269
|
+
"""Validates and merges example settings with general settings and module flags.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
example_settings: Dictionary containing settings specific to this example
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Dictionary containing the merged and validated settings with module flag overrides
|
|
276
|
+
|
|
277
|
+
The function performs three steps:
|
|
278
|
+
1. Merges example settings with general settings, warning about overlaps
|
|
279
|
+
2. Updates boolean settings based on module flags
|
|
280
|
+
3. Returns the final merged and validated settings dictionary
|
|
281
|
+
"""
|
|
282
|
+
def merge_settings(example_settings, general_settings):
|
|
283
|
+
"""Adds example_settings to general_settings, mentioning overlaps."""
|
|
284
|
+
# Track and merge settings
|
|
285
|
+
|
|
286
|
+
for key, default_value in example_settings.items():
|
|
287
|
+
# Get value from module settings or use default
|
|
288
|
+
new_value = general_settings.get(key, default_value)
|
|
289
|
+
|
|
290
|
+
# Check if we're replacing an existing general setting
|
|
291
|
+
if new_value != default_value:
|
|
292
|
+
print(f"Warning: Example setting '{key}' is replacing general setting "
|
|
293
|
+
f"'{default_value}' with '{new_value}'")
|
|
294
|
+
|
|
295
|
+
# Update the settings
|
|
296
|
+
general_settings[key] = new_value
|
|
297
|
+
|
|
298
|
+
return general_settings
|
|
299
|
+
|
|
300
|
+
def disjoin_flags(all_settings):
|
|
301
|
+
"""Update boolean settings if module flag is True."""
|
|
302
|
+
module_flags = self.module.module_flags
|
|
303
|
+
updated_settings = all_settings.copy()
|
|
304
|
+
|
|
305
|
+
for key, value in vars(module_flags).items():
|
|
306
|
+
if isinstance(value, bool) and value and key in updated_settings:
|
|
307
|
+
updated_settings[key] = value
|
|
308
|
+
|
|
309
|
+
return updated_settings
|
|
310
|
+
|
|
311
|
+
# Merge and update settings
|
|
312
|
+
all_settings = merge_settings(example_settings, self.module.general_settings)
|
|
313
|
+
updated_settings = disjoin_flags(all_settings)
|
|
314
|
+
|
|
315
|
+
return updated_settings
|
|
316
|
+
|
|
317
|
+
def print_result(self, example_name, theory_name):
|
|
318
|
+
"""Prints resulting model or no model if none is found."""
|
|
319
|
+
model_structure = self.model_structure
|
|
320
|
+
model_structure.print_to(example_name, theory_name)
|
|
321
|
+
if model_structure.settings["save_output"]:
|
|
322
|
+
file_name, save_constraints = self.ask_save()
|
|
323
|
+
self.save_or_append(model_structure, file_name, save_constraints, example_name, theory_name)
|
|
324
|
+
|
|
325
|
+
def ask_save(self):
|
|
326
|
+
"""print the model and prompt user to store the output"""
|
|
327
|
+
result = input("Would you like to save the output? (y/n):\n")
|
|
328
|
+
if not result in ['Yes', 'yes', 'Ye', 'ye', 'Y', 'y']:
|
|
329
|
+
return None, None
|
|
330
|
+
cons_input = input("\nWould you like to include the Z3 constraints? (y/n):\n")
|
|
331
|
+
print_cons = bool(cons_input in ['Yes', 'yes', 'Ye', 'ye', 'Y', 'y'])
|
|
332
|
+
file_name = input(
|
|
333
|
+
"\nEnter the file name in snake_case without an extension.\n"
|
|
334
|
+
"Leave the file name blank to append the output to the project file.\n"
|
|
335
|
+
"\nFile name: "
|
|
336
|
+
)
|
|
337
|
+
return file_name, print_cons
|
|
338
|
+
|
|
339
|
+
def save_or_append(self, model_structure, file_name, save_constraints, example_name, theory_name):
|
|
340
|
+
"""Option to save or append if a model is found."""
|
|
341
|
+
if file_name is None:
|
|
342
|
+
return
|
|
343
|
+
if len(file_name) == 0:
|
|
344
|
+
with open(f"{self.module.module_path}", 'a', encoding="utf-8") as f:
|
|
345
|
+
print('\n"""', file=f)
|
|
346
|
+
model_structure.print_to(example_name, theory_name, save_constraints, f)
|
|
347
|
+
print('"""', file=f)
|
|
348
|
+
print(f"\nAppended output to {self.module.module_path}")
|
|
349
|
+
return
|
|
350
|
+
|
|
351
|
+
# Default or fallback path
|
|
352
|
+
project_name = getattr(self.module, 'module_name', 'project')
|
|
353
|
+
destination_dir = os.path.join(os.getcwd(), project_name)
|
|
354
|
+
|
|
355
|
+
# Ensure the directory exists
|
|
356
|
+
os.makedirs(destination_dir, exist_ok=True)
|
|
357
|
+
|
|
358
|
+
with open(f"{destination_dir}/{file_name}.py", 'w', encoding="utf-8") as n:
|
|
359
|
+
print(f'# TITLE: {file_name}.py created in {destination_dir}\n"""', file=n)
|
|
360
|
+
model_structure.save_to(example_name, theory_name, save_constraints, n)
|
|
361
|
+
print(f'\n{file_name}.py created in {destination_dir}\n')
|
|
362
|
+
|
|
363
|
+
def generate_project(name):
|
|
364
|
+
"""
|
|
365
|
+
Copy the 'template/' directory to the current working directory,
|
|
366
|
+
rename it to the specified 'name', and rename its modules by prefixing 'name'.
|
|
367
|
+
"""
|
|
368
|
+
project_name = 'project_' + name
|
|
369
|
+
# Dynamically resolve the source directory relative to this script
|
|
370
|
+
source_dir = os.path.join(os.path.dirname(__file__), 'template')
|
|
371
|
+
destination_dir = os.path.join(os.getcwd(), project_name)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
# Check if the source directory exists
|
|
375
|
+
if not os.path.exists(source_dir):
|
|
376
|
+
raise FileNotFoundError(f"The source directory '{source_dir}' was not found.")
|
|
377
|
+
|
|
378
|
+
# Check if the destination directory already exists
|
|
379
|
+
if os.path.exists(destination_dir):
|
|
380
|
+
print(f"Error: Directory '{destination_dir}' already exists.")
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
# Copy the template directory
|
|
384
|
+
shutil.copytree(source_dir, destination_dir)
|
|
385
|
+
|
|
386
|
+
# Rename the files in the copied directory
|
|
387
|
+
files_to_rename = {
|
|
388
|
+
"examples.py": "examples.py",
|
|
389
|
+
"operators.py": "operators.py",
|
|
390
|
+
"semantics.py": "semantics.py",
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for old_name, new_name in files_to_rename.items():
|
|
394
|
+
old_path = os.path.join(destination_dir, old_name)
|
|
395
|
+
new_path = os.path.join(destination_dir, new_name)
|
|
396
|
+
if os.path.exists(old_path):
|
|
397
|
+
os.rename(old_path, new_path)
|
|
398
|
+
|
|
399
|
+
print(f"\nProject generated at: {destination_dir}\n")
|
|
400
|
+
print("The following modules were created:")
|
|
401
|
+
for old_name, new_name in files_to_rename.items():
|
|
402
|
+
print(f" {new_name}")
|
|
403
|
+
|
|
404
|
+
# Run the example script
|
|
405
|
+
example_script = os.path.join(destination_dir, "examples.py")
|
|
406
|
+
print(example_script)
|
|
407
|
+
if os.path.exists(example_script):
|
|
408
|
+
print("\nRunning the example script...")
|
|
409
|
+
subprocess.run(["model-checker", example_script])
|
|
410
|
+
else:
|
|
411
|
+
print(f"\nFail to run: model-checker {example_script}")
|
|
412
|
+
|
|
413
|
+
# Output the test command
|
|
414
|
+
print(f"\nRun the following command to test your project:\n\nmodel-checker {destination_dir}/examples.py\n")
|
|
415
|
+
|
|
416
|
+
except FileNotFoundError as e:
|
|
417
|
+
print(f"Error: {e}")
|
|
418
|
+
except Exception as e:
|
|
419
|
+
print(f"An unexpected error occurred: {e}")
|
|
420
|
+
|
|
421
|
+
def ask_generate_project():
|
|
422
|
+
"""prompt user to create a test file"""
|
|
423
|
+
result = input("Would you like to generate a new project? (y/n): ")
|
|
424
|
+
if result not in {'Yes', 'yes', 'Ye', 'ye', 'Y', 'y'}:
|
|
425
|
+
print("You can run an example.py file that already exists with the command:\n")
|
|
426
|
+
print("model-checker example.py\n")
|
|
427
|
+
test_name = input("Enter the name of your project using snake_case: ")
|
|
428
|
+
generate_project(test_name)
|
|
429
|
+
|
|
430
|
+
def parse_file_and_flags():
|
|
431
|
+
"""returns the name and path for the current script"""
|
|
432
|
+
# create an ArgumentParser object
|
|
433
|
+
parser = argparse.ArgumentParser(
|
|
434
|
+
prog='model-checker',
|
|
435
|
+
usage='%(prog)s [options] input',
|
|
436
|
+
description="""
|
|
437
|
+
Running '%(prog)s' without options or an input will prompt the user
|
|
438
|
+
to generate a project. To run a test on an existing file, include
|
|
439
|
+
the path to that file as the input.""",
|
|
440
|
+
epilog="""
|
|
441
|
+
More information can be found at:
|
|
442
|
+
https://github.com/benbrastmckie/ModelChecker/""",
|
|
443
|
+
)
|
|
444
|
+
parser.add_argument(
|
|
445
|
+
"file_path",
|
|
446
|
+
nargs='?',
|
|
447
|
+
default=None,
|
|
448
|
+
type=str,
|
|
449
|
+
help="Specifies the path to a Python.",
|
|
450
|
+
)
|
|
451
|
+
parser.add_argument(
|
|
452
|
+
'--contingent',
|
|
453
|
+
'-c',
|
|
454
|
+
action='store_true',
|
|
455
|
+
help='Overrides to make all propositions contingent.'
|
|
456
|
+
)
|
|
457
|
+
parser.add_argument(
|
|
458
|
+
'--non_empty',
|
|
459
|
+
'-e',
|
|
460
|
+
action='store_true',
|
|
461
|
+
help='Overrides to make all propositions non_empty.'
|
|
462
|
+
)
|
|
463
|
+
parser.add_argument(
|
|
464
|
+
'--non_null',
|
|
465
|
+
'-n',
|
|
466
|
+
action='store_true',
|
|
467
|
+
help='Overrides to make all propositions non_null.'
|
|
468
|
+
)
|
|
469
|
+
parser.add_argument(
|
|
470
|
+
'--disjoint',
|
|
471
|
+
'-d',
|
|
472
|
+
action='store_true',
|
|
473
|
+
help='Overrides to make all propositions have disjoint subject-matters.'
|
|
474
|
+
)
|
|
475
|
+
parser.add_argument(
|
|
476
|
+
'--print_constraints',
|
|
477
|
+
'-p',
|
|
478
|
+
action='store_true',
|
|
479
|
+
help='Overrides to print the Z3 constraints or else the unsat_core constraints if there is no model.'
|
|
480
|
+
)
|
|
481
|
+
parser.add_argument(
|
|
482
|
+
'--save_output',
|
|
483
|
+
'-s',
|
|
484
|
+
action='store_true',
|
|
485
|
+
help='Overrides to prompt user to save output.'
|
|
486
|
+
)
|
|
487
|
+
parser.add_argument(
|
|
488
|
+
'--print_impossible',
|
|
489
|
+
'-i',
|
|
490
|
+
action='store_true',
|
|
491
|
+
help='Overrides to print impossible states.'
|
|
492
|
+
)
|
|
493
|
+
parser.add_argument(
|
|
494
|
+
'--version',
|
|
495
|
+
'-v',
|
|
496
|
+
action='version',
|
|
497
|
+
version=f"%(prog)s: {__version__}",
|
|
498
|
+
help='Prints the version number.'
|
|
499
|
+
)
|
|
500
|
+
# parser.add_argument(
|
|
501
|
+
# '--optimize',
|
|
502
|
+
# '-o',
|
|
503
|
+
# action='store_true',
|
|
504
|
+
# help='finds the minimum value for N that is satisfiable before reaching max_time.'
|
|
505
|
+
# )
|
|
506
|
+
parser.add_argument(
|
|
507
|
+
'--upgrade',
|
|
508
|
+
'-u',
|
|
509
|
+
action='store_true',
|
|
510
|
+
help='Upgrade the package.'
|
|
511
|
+
)
|
|
512
|
+
# parse the command-line argument to get the module path
|
|
513
|
+
flags = parser.parse_args()
|
|
514
|
+
package_name = parser.prog # Get the package name from the parser
|
|
515
|
+
return flags, package_name
|
|
516
|
+
|
|
517
|
+
# TODO: make method for runtime and progress bar
|
|
518
|
+
# TODO: make method for turning on cProfile
|
|
519
|
+
def main():
|
|
520
|
+
if len(sys.argv) < 2:
|
|
521
|
+
ask_generate_project()
|
|
522
|
+
return
|
|
523
|
+
module_flags, package_name = parse_file_and_flags()
|
|
524
|
+
if module_flags.upgrade:
|
|
525
|
+
print("Upgrading package")
|
|
526
|
+
try:
|
|
527
|
+
subprocess.run(['pip', 'install', '--upgrade', package_name], check=True)
|
|
528
|
+
except subprocess.CalledProcessError as e:
|
|
529
|
+
print(f"Failed to upgrade {package_name}: {e}")
|
|
530
|
+
return
|
|
531
|
+
|
|
532
|
+
module = BuildModule(module_flags)
|
|
533
|
+
|
|
534
|
+
# TODO: check if compare = True and then:
|
|
535
|
+
# check if multiple semantic_theories and then:
|
|
536
|
+
# run comparison function
|
|
537
|
+
|
|
538
|
+
for example_name, example_case in module.example_range.items():
|
|
539
|
+
for theory_name, semantic_theory in module.semantic_theories.items():
|
|
540
|
+
dictionary = semantic_theory.get("dictionary", None)
|
|
541
|
+
if dictionary:
|
|
542
|
+
example_case = translate(example_case, dictionary)
|
|
543
|
+
example = BuildExample(module, semantic_theory, example_case)
|
|
544
|
+
example.print_result(example_name, theory_name)
|
|
545
|
+
|
|
546
|
+
if __name__ == "__main__":
|
|
547
|
+
main()
|