gnps 0.0.0__tar.gz
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.
- gnps-0.0.0/PKG-INFO +78 -0
- gnps-0.0.0/README.md +63 -0
- gnps-0.0.0/pyproject.toml +104 -0
- gnps-0.0.0/src/gnps/__init__.py +3 -0
- gnps-0.0.0/src/gnps/__main__.py +4 -0
- gnps-0.0.0/src/gnps/_version.py +1 -0
- gnps-0.0.0/src/gnps/cell.py +26 -0
- gnps-0.0.0/src/gnps/cli.py +89 -0
- gnps-0.0.0/src/gnps/cli_transform.py +134 -0
- gnps-0.0.0/src/gnps/parser/__init__.py +6 -0
- gnps-0.0.0/src/gnps/parser/ast/__init__.py +31 -0
- gnps-0.0.0/src/gnps/parser/ast/boolean_expression.py +151 -0
- gnps-0.0.0/src/gnps/parser/ast/expression.py +243 -0
- gnps-0.0.0/src/gnps/parser/ast/value/__init__.py +4 -0
- gnps-0.0.0/src/gnps/parser/ast/value/array_value.py +62 -0
- gnps-0.0.0/src/gnps/parser/ast/value/base_value.py +33 -0
- gnps-0.0.0/src/gnps/parser/ast/value/float_value.py +43 -0
- gnps-0.0.0/src/gnps/parser/ast/value/math_functions.py +126 -0
- gnps-0.0.0/src/gnps/parser/ast/value/numerical_value.py +313 -0
- gnps-0.0.0/src/gnps/parser/ast/variable.py +16 -0
- gnps-0.0.0/src/gnps/parser/parser.py +215 -0
- gnps-0.0.0/src/gnps/rule.py +24 -0
- gnps-0.0.0/src/gnps/system.py +146 -0
- gnps-0.0.0/src/gnps/transformers/__init__.py +6 -0
- gnps-0.0.0/src/gnps/transformers/base_transformer.py +51 -0
- gnps-0.0.0/src/gnps/transformers/python_transformer.py +475 -0
- gnps-0.0.0/tests/__init__.py +0 -0
- gnps-0.0.0/tests/functional_tests/sequence_test.py +39 -0
- gnps-0.0.0/tests/functional_tests/test_example1.py +46 -0
- gnps-0.0.0/tests/functional_tests/test_example1.yaml +14 -0
- gnps-0.0.0/tests/functional_tests/test_example1a.py +28 -0
- gnps-0.0.0/tests/functional_tests/test_example2.yaml +27 -0
- gnps-0.0.0/tests/functional_tests/test_example2_all_operators.py +23 -0
- gnps-0.0.0/tests/functional_tests/test_math_functions.py +141 -0
- gnps-0.0.0/tests/functional_tests/test_math_functions.yaml +31 -0
- gnps-0.0.0/tests/unit_tests/__init__.py +0 -0
- gnps-0.0.0/tests/unit_tests/test_array_value.py +111 -0
- gnps-0.0.0/tests/unit_tests/test_boolean_expression.py +132 -0
- gnps-0.0.0/tests/unit_tests/test_expression.py +171 -0
- gnps-0.0.0/tests/unit_tests/test_function_call.py +334 -0
- gnps-0.0.0/tests/unit_tests/test_system.py +36 -0
- gnps-0.0.0/tests/unit_tests/test_value.py +114 -0
- gnps-0.0.0/tests/unit_tests/transformers/__init__.py +2 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_base_transformer.py +135 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_cli_transform.py +221 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_final_coverage.py +154 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_python_transformer.py +597 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_remaining_coverage.py +253 -0
- gnps-0.0.0/tests/unit_tests/transformers/test_transformers_init.py +25 -0
gnps-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gnps
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Generalized Numerical P Systems simulator package
|
|
5
|
+
Author-Email: Sergey Verlan <dont-spam-me@no.spam>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: lark~=1.1
|
|
13
|
+
Requires-Dist: PyYAML~=6.0
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# GNPS project
|
|
17
|
+
|
|
18
|
+
The *GNPS* project (Generalized Numerical P Systems) aims to create a
|
|
19
|
+
library for simulation of different variants of Numerical P Systems (NPS).
|
|
20
|
+
|
|
21
|
+
It contains:
|
|
22
|
+
|
|
23
|
+
* GNPS core (the core classes for NPS simulation)
|
|
24
|
+
* GNPS parser (parsers for different inputs)
|
|
25
|
+
* GNPS generator (generates outputs, i.e. Verilog, Amaranth and Lustre) *work in progress.*
|
|
26
|
+
* Utils (like the main runner)
|
|
27
|
+
|
|
28
|
+
## Input Files
|
|
29
|
+
|
|
30
|
+
- **System description**: YAML file describing the GNPS system (required as the first argument).
|
|
31
|
+
- **Input file**: Optional CSV file, each row representing input variable values for a simulation step. The header must match the input variable names defined in the YAML.
|
|
32
|
+
- **Output file**: Optional CSV file, each row representing output variable values for each step.
|
|
33
|
+
|
|
34
|
+
## Command Line Options
|
|
35
|
+
|
|
36
|
+
Run the simulator with:
|
|
37
|
+
|
|
38
|
+
python -m gnps <gnps_file.yaml> [input.csv] [output.csv] [options]
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
- `-c`, `--compute_mode` Run in continuous compute mode (no input variables allowed)
|
|
42
|
+
- `-s`, `--steps N` Number of steps to run (default: 1)
|
|
43
|
+
- `--csv` Output in CSV format in continuous compute mode
|
|
44
|
+
|
|
45
|
+
- `gnps_file` (YAML): Required. GNPS system description.
|
|
46
|
+
- `input` (CSV): Optional. Input values per step (default: stdin).
|
|
47
|
+
- `output` (CSV): Optional. Output values per step (default: stdout).
|
|
48
|
+
|
|
49
|
+
## Modes
|
|
50
|
+
|
|
51
|
+
- **IO mode (default)**: Reads input variables from CSV for each step, writes output variables for each step.
|
|
52
|
+
- **Continuous compute mode**: Runs for a set number of steps without input variables, outputs results at each step (CSV or text).
|
|
53
|
+
|
|
54
|
+
## Example Input YAML
|
|
55
|
+
|
|
56
|
+
Below is an example of a GNPS system description in YAML format (from `test_math_functions.yaml`):
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
description: "A test system"
|
|
60
|
+
|
|
61
|
+
cells:
|
|
62
|
+
- id: 1
|
|
63
|
+
contents:
|
|
64
|
+
- x = 0, y = 0, E = 0, z = 0, u = 1
|
|
65
|
+
# Comments can be added like this
|
|
66
|
+
# input variables should be empty for continuous compute mode
|
|
67
|
+
input: [u]
|
|
68
|
+
output: [x,y,z]
|
|
69
|
+
|
|
70
|
+
rules:
|
|
71
|
+
- E > x | x + 3 -> x # guarded rule
|
|
72
|
+
- E + 1 -> E # unguarded rule
|
|
73
|
+
- 0*z + x + y -> z
|
|
74
|
+
- (E > u || E > x || E > y) | u + x + y -> y # step will be reset to 0
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
For more details, see the CHANGELOG.md and documentation in the docs/ folder.
|
gnps-0.0.0/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# GNPS project
|
|
2
|
+
|
|
3
|
+
The *GNPS* project (Generalized Numerical P Systems) aims to create a
|
|
4
|
+
library for simulation of different variants of Numerical P Systems (NPS).
|
|
5
|
+
|
|
6
|
+
It contains:
|
|
7
|
+
|
|
8
|
+
* GNPS core (the core classes for NPS simulation)
|
|
9
|
+
* GNPS parser (parsers for different inputs)
|
|
10
|
+
* GNPS generator (generates outputs, i.e. Verilog, Amaranth and Lustre) *work in progress.*
|
|
11
|
+
* Utils (like the main runner)
|
|
12
|
+
|
|
13
|
+
## Input Files
|
|
14
|
+
|
|
15
|
+
- **System description**: YAML file describing the GNPS system (required as the first argument).
|
|
16
|
+
- **Input file**: Optional CSV file, each row representing input variable values for a simulation step. The header must match the input variable names defined in the YAML.
|
|
17
|
+
- **Output file**: Optional CSV file, each row representing output variable values for each step.
|
|
18
|
+
|
|
19
|
+
## Command Line Options
|
|
20
|
+
|
|
21
|
+
Run the simulator with:
|
|
22
|
+
|
|
23
|
+
python -m gnps <gnps_file.yaml> [input.csv] [output.csv] [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
- `-c`, `--compute_mode` Run in continuous compute mode (no input variables allowed)
|
|
27
|
+
- `-s`, `--steps N` Number of steps to run (default: 1)
|
|
28
|
+
- `--csv` Output in CSV format in continuous compute mode
|
|
29
|
+
|
|
30
|
+
- `gnps_file` (YAML): Required. GNPS system description.
|
|
31
|
+
- `input` (CSV): Optional. Input values per step (default: stdin).
|
|
32
|
+
- `output` (CSV): Optional. Output values per step (default: stdout).
|
|
33
|
+
|
|
34
|
+
## Modes
|
|
35
|
+
|
|
36
|
+
- **IO mode (default)**: Reads input variables from CSV for each step, writes output variables for each step.
|
|
37
|
+
- **Continuous compute mode**: Runs for a set number of steps without input variables, outputs results at each step (CSV or text).
|
|
38
|
+
|
|
39
|
+
## Example Input YAML
|
|
40
|
+
|
|
41
|
+
Below is an example of a GNPS system description in YAML format (from `test_math_functions.yaml`):
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
description: "A test system"
|
|
45
|
+
|
|
46
|
+
cells:
|
|
47
|
+
- id: 1
|
|
48
|
+
contents:
|
|
49
|
+
- x = 0, y = 0, E = 0, z = 0, u = 1
|
|
50
|
+
# Comments can be added like this
|
|
51
|
+
# input variables should be empty for continuous compute mode
|
|
52
|
+
input: [u]
|
|
53
|
+
output: [x,y,z]
|
|
54
|
+
|
|
55
|
+
rules:
|
|
56
|
+
- E > x | x + 3 -> x # guarded rule
|
|
57
|
+
- E + 1 -> E # unguarded rule
|
|
58
|
+
- 0*z + x + y -> z
|
|
59
|
+
- (E > u || E > x || E > y) | u + x + y -> y # step will be reset to 0
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
For more details, see the CHANGELOG.md and documentation in the docs/ folder.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"pdm-backend",
|
|
4
|
+
]
|
|
5
|
+
build-backend = "pdm.backend"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "gnps"
|
|
9
|
+
dynamic = []
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Sergey Verlan", email = "dont-spam-me@no.spam" },
|
|
12
|
+
]
|
|
13
|
+
description = "Generalized Numerical P Systems simulator package"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"lark~=1.1",
|
|
17
|
+
"PyYAML~=6.0",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.10"
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
|
+
]
|
|
26
|
+
version = "0.0.0"
|
|
27
|
+
|
|
28
|
+
[project.license]
|
|
29
|
+
text = "MIT"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
gnps = "gnps.__main__:main"
|
|
33
|
+
gnps-transformer = "gnps.cli_transform:main"
|
|
34
|
+
|
|
35
|
+
[tool.pdm]
|
|
36
|
+
autoexport = [
|
|
37
|
+
{ filename = "requirements.txt", without-hashes = true },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[tool.pdm.version]
|
|
41
|
+
source = "scm"
|
|
42
|
+
fallback_version = "0.0.0"
|
|
43
|
+
tag_filter = "v*"
|
|
44
|
+
tag_regex = "^v(.*)$"
|
|
45
|
+
write_to = "gnps/_version.py"
|
|
46
|
+
write_template = "__version__ = '{}'"
|
|
47
|
+
|
|
48
|
+
[tool.pdm.scripts]
|
|
49
|
+
test = "pytest --cov=gnps --cov-report=xml --cov-report=term-missing"
|
|
50
|
+
|
|
51
|
+
[tool.coverage.run]
|
|
52
|
+
branch = true
|
|
53
|
+
source = [
|
|
54
|
+
"src/gnps/",
|
|
55
|
+
"tests/",
|
|
56
|
+
]
|
|
57
|
+
omit = [
|
|
58
|
+
"*/__main__.py",
|
|
59
|
+
"*/_version.py",
|
|
60
|
+
"venv/*",
|
|
61
|
+
"cli.py",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.coverage.report]
|
|
65
|
+
show_missing = true
|
|
66
|
+
skip_covered = true
|
|
67
|
+
omit = [
|
|
68
|
+
"*/__main__.py",
|
|
69
|
+
"*/_version.py",
|
|
70
|
+
"cli.py",
|
|
71
|
+
"venv/*",
|
|
72
|
+
]
|
|
73
|
+
exclude_also = [
|
|
74
|
+
"if __name__ == .__main__.:",
|
|
75
|
+
]
|
|
76
|
+
exclude_lines = [
|
|
77
|
+
"pragma: no cover",
|
|
78
|
+
"if __name__ == .__main__.:",
|
|
79
|
+
"def __repr__",
|
|
80
|
+
"def __str__",
|
|
81
|
+
"raise NotImplementedError",
|
|
82
|
+
]
|
|
83
|
+
ignore_errors = true
|
|
84
|
+
|
|
85
|
+
[tool.coverage.html]
|
|
86
|
+
directory = "coverage_html_report"
|
|
87
|
+
|
|
88
|
+
[tool.pytest.ini_options]
|
|
89
|
+
minversion = "6.0"
|
|
90
|
+
pythonpath = [
|
|
91
|
+
"src/",
|
|
92
|
+
]
|
|
93
|
+
testpaths = [
|
|
94
|
+
"tests/unit_tests/",
|
|
95
|
+
"tests/functional_tests/",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
[dependency-groups]
|
|
99
|
+
dev = [
|
|
100
|
+
"pytest>=7.4.3",
|
|
101
|
+
"sphinx>=7.2.6",
|
|
102
|
+
"pytest-cov>=7.0.0",
|
|
103
|
+
"sphinx-rtd-theme>=3.0.2",
|
|
104
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.0'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .parser.ast.variable import Variable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Cell:
|
|
5
|
+
"""
|
|
6
|
+
The class representing a cell.
|
|
7
|
+
|
|
8
|
+
:param id: The id of the cell.
|
|
9
|
+
:type id: int
|
|
10
|
+
|
|
11
|
+
:param contents: The contents of the cell. It is a dictionary of variables.
|
|
12
|
+
:type contents: dict[str, Variable]
|
|
13
|
+
"""
|
|
14
|
+
id: int
|
|
15
|
+
contents: dict[str, Variable]
|
|
16
|
+
|
|
17
|
+
def __init__(self, cell_id: int, contents: dict[str, Variable]):
|
|
18
|
+
"""
|
|
19
|
+
Initialize a cell.
|
|
20
|
+
:param cell_id: the cell id
|
|
21
|
+
:type cell_id: int
|
|
22
|
+
:param contents: the contents of the cell (a dictionary of variables)
|
|
23
|
+
:type contents: dict[str, Variable]
|
|
24
|
+
"""
|
|
25
|
+
self.id = cell_id
|
|
26
|
+
self.contents = contents
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import csv
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
from lark import Tree
|
|
7
|
+
|
|
8
|
+
from gnps import Cell
|
|
9
|
+
from gnps.parser.ast import Variable
|
|
10
|
+
from gnps.parser.ast.value import FloatValue
|
|
11
|
+
from gnps.system import GnpsSystem
|
|
12
|
+
from gnps.parser import parse_condition, parse_expression, parse_rule, parse_variable_assignment
|
|
13
|
+
|
|
14
|
+
from gnps._version import __version__
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
parser = argparse.ArgumentParser(description=f"GNPS simulator v{__version__}", prog='gnps')
|
|
18
|
+
parser.add_argument('gnps_file', type=argparse.FileType('r'),
|
|
19
|
+
help='The file containing the description of the GNPS system')
|
|
20
|
+
parser.add_argument('input', type=argparse.FileType('r'), nargs='?', default=sys.stdin,
|
|
21
|
+
help='Input (CSV) file giving stimulus for each step (default: stdin)')
|
|
22
|
+
parser.add_argument('output', type=argparse.FileType('w'), nargs='?', default=sys.stdout,
|
|
23
|
+
help='Output (CSV) file giving the results of each step (default: stdout)')
|
|
24
|
+
parser.add_argument('-c', '--compute_mode', action='store_true',
|
|
25
|
+
help='Run in continuous compute mode (default: False)')
|
|
26
|
+
parser.add_argument('-s', '--steps', type=int, default=1,
|
|
27
|
+
help='Number of steps to run (default: 1)')
|
|
28
|
+
parser.add_argument('--csv', action='store_true',
|
|
29
|
+
help='Output in CSV format when in continuous compute mode')
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
|
|
32
|
+
gnps = GnpsSystem.from_yaml(args.gnps_file.name)
|
|
33
|
+
|
|
34
|
+
if args.compute_mode:
|
|
35
|
+
if len(gnps.input_variables) > 0:
|
|
36
|
+
raise ValueError("Cannot run in continuous compute mode with input variables")
|
|
37
|
+
if args.csv:
|
|
38
|
+
# CSV output mode
|
|
39
|
+
args.output.reconfigure(newline='')
|
|
40
|
+
result = {name: str(variable.value) for name, variable in gnps.output_variables.items()}
|
|
41
|
+
fieldnames = ['step'] + list(result.keys())
|
|
42
|
+
writer = csv.DictWriter(args.output, fieldnames=fieldnames)
|
|
43
|
+
writer.writeheader()
|
|
44
|
+
# Write initial state (step 0) with only output variables
|
|
45
|
+
row = {'step': 0}
|
|
46
|
+
row.update(result)
|
|
47
|
+
writer.writerow(row)
|
|
48
|
+
for i in range(args.steps):
|
|
49
|
+
gnps.step()
|
|
50
|
+
result = {name: str(variable.value) for name, variable in gnps.output_variables.items()}
|
|
51
|
+
row = {'step': i+1}
|
|
52
|
+
row.update(result)
|
|
53
|
+
writer.writerow(row)
|
|
54
|
+
else:
|
|
55
|
+
print(f"Running in continuous compute mode for {args.steps} steps")
|
|
56
|
+
result = {name: str(variable.value) for name, variable in gnps.output_variables.items()}
|
|
57
|
+
print(f"Step: 0: {result}")
|
|
58
|
+
for i in range(args.steps):
|
|
59
|
+
gnps.step()
|
|
60
|
+
result = {name: str(variable.value) for name, variable in gnps.output_variables.items()}
|
|
61
|
+
print(f"Step: {i+1}: {result}")
|
|
62
|
+
else: # IO mode
|
|
63
|
+
|
|
64
|
+
args.output.reconfigure(newline='')
|
|
65
|
+
args.input.reconfigure(newline='')
|
|
66
|
+
|
|
67
|
+
reader = csv.DictReader(args.input)
|
|
68
|
+
writer = None
|
|
69
|
+
|
|
70
|
+
for row in reader:
|
|
71
|
+
|
|
72
|
+
for input_var_name, input_var_value in row.items():
|
|
73
|
+
var = gnps.get_variable(input_var_name)
|
|
74
|
+
if var is None or input_var_name not in gnps.input_variables.keys():
|
|
75
|
+
raise ValueError(f"Unknown input variable {input_var_name}")
|
|
76
|
+
var.value = FloatValue(input_var_value)
|
|
77
|
+
|
|
78
|
+
gnps.step()
|
|
79
|
+
|
|
80
|
+
result = {name: variable.value for name, variable in gnps.output_variables.items()}
|
|
81
|
+
|
|
82
|
+
if writer is None:
|
|
83
|
+
writer = csv.DictWriter(args.output, fieldnames=result.keys())
|
|
84
|
+
writer.writeheader()
|
|
85
|
+
writer.writerow(result)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if __name__ == '__main__':
|
|
89
|
+
main()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""CLI tool for transforming GNPS systems to various output formats."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Type
|
|
7
|
+
|
|
8
|
+
from gnps.system import GnpsSystem
|
|
9
|
+
from gnps.transformers import BaseTransformer, PythonTransformer
|
|
10
|
+
from gnps._version import __version__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Registry of available transformers
|
|
14
|
+
TRANSFORMERS: Dict[str, Type[BaseTransformer]] = {
|
|
15
|
+
'python': PythonTransformer,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_transformer(transform_type: str) -> BaseTransformer:
|
|
20
|
+
"""Get a transformer instance by type name.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
transform_type: The type of transformer to create
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Transformer instance
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If transform_type is not supported
|
|
30
|
+
"""
|
|
31
|
+
if transform_type not in TRANSFORMERS:
|
|
32
|
+
available = ', '.join(TRANSFORMERS.keys())
|
|
33
|
+
raise ValueError(f"Unsupported transformation type '{transform_type}'. Available types: {available}")
|
|
34
|
+
|
|
35
|
+
return TRANSFORMERS[transform_type]()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main():
|
|
39
|
+
"""Main entry point for the gnps-transformer CLI."""
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description=f"GNPS Transformer v{__version__} - Convert GNPS systems to various output formats",
|
|
42
|
+
prog='gnps-transformer'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
'gnps_files',
|
|
47
|
+
nargs='+',
|
|
48
|
+
help='GNPS system files (.yaml) to transform'
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
'-t', '--type',
|
|
53
|
+
dest='transform_type',
|
|
54
|
+
required=True,
|
|
55
|
+
choices=list(TRANSFORMERS.keys()),
|
|
56
|
+
help='Transformation type'
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
'-o', '--output-dir',
|
|
61
|
+
type=Path,
|
|
62
|
+
default=Path('.'),
|
|
63
|
+
help='Output directory for transformed files (default: current directory)'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
'--output-suffix',
|
|
68
|
+
default='',
|
|
69
|
+
help='Suffix to add to output filenames (before extension)'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
'-v', '--verbose',
|
|
74
|
+
action='store_true',
|
|
75
|
+
help='Enable verbose output'
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
args = parser.parse_args()
|
|
79
|
+
|
|
80
|
+
# Ensure output directory exists
|
|
81
|
+
args.output_dir.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
# Get transformer
|
|
84
|
+
try:
|
|
85
|
+
transformer = get_transformer(args.transform_type)
|
|
86
|
+
except ValueError as e:
|
|
87
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
88
|
+
sys.exit(1)
|
|
89
|
+
|
|
90
|
+
# Process each file
|
|
91
|
+
for gnps_file_path in args.gnps_files:
|
|
92
|
+
gnps_file = Path(gnps_file_path)
|
|
93
|
+
|
|
94
|
+
if not gnps_file.exists():
|
|
95
|
+
print(f"Error: File '{gnps_file}' not found", file=sys.stderr)
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
if args.verbose:
|
|
99
|
+
print(f"Processing: {gnps_file}")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
# Load GNPS system
|
|
103
|
+
gnps_system = GnpsSystem.from_yaml(str(gnps_file))
|
|
104
|
+
|
|
105
|
+
# Transform to target format
|
|
106
|
+
transformed_code = transformer.transform(gnps_system)
|
|
107
|
+
|
|
108
|
+
# Generate output filename
|
|
109
|
+
base_name = gnps_file.stem
|
|
110
|
+
if args.output_suffix:
|
|
111
|
+
base_name += args.output_suffix
|
|
112
|
+
|
|
113
|
+
output_file = args.output_dir / (base_name + transformer.get_file_extension())
|
|
114
|
+
|
|
115
|
+
# Write output
|
|
116
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
117
|
+
f.write(transformed_code)
|
|
118
|
+
|
|
119
|
+
if args.verbose:
|
|
120
|
+
print(f" -> {output_file}")
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
print(f"Error processing '{gnps_file}': {e}", file=sys.stderr)
|
|
124
|
+
if args.verbose: # pragma: no cover
|
|
125
|
+
import traceback
|
|
126
|
+
traceback.print_exc()
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
if args.verbose:
|
|
130
|
+
print("Transformation complete.")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == '__main__':
|
|
134
|
+
main()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .variable import Variable
|
|
2
|
+
|
|
3
|
+
from .expression import (
|
|
4
|
+
Expression,
|
|
5
|
+
ConstantExpression,
|
|
6
|
+
VariableExpression,
|
|
7
|
+
BinaryExpression,
|
|
8
|
+
SumExpression,
|
|
9
|
+
DifferenceExpression,
|
|
10
|
+
MultiplicationExpression,
|
|
11
|
+
DivisionExpression,
|
|
12
|
+
UnaryMinusExpression,
|
|
13
|
+
IntOperationExpression,
|
|
14
|
+
IntMultiplicationExpression,
|
|
15
|
+
IntDivisionExpression,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from .boolean_expression import (
|
|
19
|
+
BooleanExpression,
|
|
20
|
+
BooleanConstantExpression,
|
|
21
|
+
BooleanBinaryExpression,
|
|
22
|
+
BooleanAndExpression,
|
|
23
|
+
BooleanOrExpression,
|
|
24
|
+
BooleanNotExpression,
|
|
25
|
+
BooleanLessTestExpression,
|
|
26
|
+
BooleanLessEqualTestExpression,
|
|
27
|
+
BooleanGreaterTestExpression,
|
|
28
|
+
BooleanGreaterEqualTestExpression,
|
|
29
|
+
BooleanEqualTestExpression,
|
|
30
|
+
BooleanNotEqualTestExpression,
|
|
31
|
+
)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
|
|
3
|
+
from .expression import Expression
|
|
4
|
+
from .variable import Variable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BooleanExpression(Expression):
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def evaluate(self) -> bool:
|
|
10
|
+
pass # pragma: no cover
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BooleanConstantExpression(BooleanExpression):
|
|
14
|
+
value: bool
|
|
15
|
+
|
|
16
|
+
def __init__(self, value: bool):
|
|
17
|
+
self.value = value
|
|
18
|
+
|
|
19
|
+
def evaluate(self):
|
|
20
|
+
return self.value
|
|
21
|
+
|
|
22
|
+
def get_variables(self) -> dict[str, Variable]:
|
|
23
|
+
return {}
|
|
24
|
+
|
|
25
|
+
def __str__(self):
|
|
26
|
+
return self.value.__str__()
|
|
27
|
+
|
|
28
|
+
def __repr__(self):
|
|
29
|
+
return f"BooleanConstantExpression({self.value})"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BooleanTestExpression(BooleanExpression, ABC):
|
|
33
|
+
def __init__(self, left: Expression, right: Expression, op: str = ''):
|
|
34
|
+
self.left = left
|
|
35
|
+
self.right = right
|
|
36
|
+
self.op = op
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
ll = self.left.__str__()
|
|
40
|
+
r = self.right.__str__()
|
|
41
|
+
return f"({ll} {self.op} {r})"
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return f"BooleanTestExpression('{self.op}',{self.left.__repr__()},{self.right.__repr__()})"
|
|
45
|
+
|
|
46
|
+
def get_variables(self) -> dict[str, Variable]:
|
|
47
|
+
variables = self.left.get_variables()
|
|
48
|
+
variables.update(self.right.get_variables())
|
|
49
|
+
return variables
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BooleanGreaterTestExpression(BooleanTestExpression):
|
|
53
|
+
def __init__(self, left: Expression, right: Expression):
|
|
54
|
+
super().__init__(left, right, ">")
|
|
55
|
+
|
|
56
|
+
def evaluate(self):
|
|
57
|
+
return self.left.evaluate() > self.right.evaluate()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BooleanGreaterEqualTestExpression(BooleanTestExpression):
|
|
61
|
+
def __init__(self, left: Expression, right: Expression):
|
|
62
|
+
super().__init__(left, right, ">=")
|
|
63
|
+
|
|
64
|
+
def evaluate(self):
|
|
65
|
+
return self.left.evaluate() >= self.right.evaluate()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class BooleanEqualTestExpression(BooleanTestExpression):
|
|
69
|
+
def __init__(self, left: Expression, right: Expression):
|
|
70
|
+
super().__init__(left, right, "==")
|
|
71
|
+
|
|
72
|
+
def evaluate(self):
|
|
73
|
+
return self.left.evaluate() == self.right.evaluate()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class BooleanNotEqualTestExpression(BooleanTestExpression):
|
|
77
|
+
def __init__(self, left: Expression, right: Expression):
|
|
78
|
+
super().__init__(left, right, "!=")
|
|
79
|
+
|
|
80
|
+
def evaluate(self):
|
|
81
|
+
return self.left.evaluate() != self.right.evaluate()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class BooleanLessTestExpression(BooleanTestExpression):
|
|
85
|
+
def __init__(self, left: Expression, right: Expression):
|
|
86
|
+
super().__init__(left, right, "<")
|
|
87
|
+
|
|
88
|
+
def evaluate(self):
|
|
89
|
+
return self.left.evaluate() < self.right.evaluate()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class BooleanLessEqualTestExpression(BooleanTestExpression):
|
|
93
|
+
def __init__(self, left: Expression, right: Expression):
|
|
94
|
+
super().__init__(left, right, "<=")
|
|
95
|
+
|
|
96
|
+
def evaluate(self):
|
|
97
|
+
return self.left.evaluate() <= self.right.evaluate()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class BooleanBinaryExpression(BooleanExpression, ABC):
|
|
101
|
+
def __init__(self, left: BooleanExpression, right: BooleanExpression, op: str = ''):
|
|
102
|
+
self.left = left
|
|
103
|
+
self.right = right
|
|
104
|
+
self.op = op
|
|
105
|
+
|
|
106
|
+
def __str__(self):
|
|
107
|
+
ll = self.left.__str__()
|
|
108
|
+
r = self.right.__str__()
|
|
109
|
+
return f"({ll} {self.op} {r})"
|
|
110
|
+
|
|
111
|
+
def __repr__(self):
|
|
112
|
+
return f"BooleanBinaryExpression('{self.op}',{self.left.__repr__()},{self.right.__repr__()})"
|
|
113
|
+
|
|
114
|
+
def get_variables(self) -> dict[str, Variable]:
|
|
115
|
+
variables = self.left.get_variables()
|
|
116
|
+
variables.update(self.right.get_variables())
|
|
117
|
+
return variables
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class BooleanOrExpression(BooleanBinaryExpression):
|
|
121
|
+
def __init__(self, left: BooleanExpression, right: BooleanExpression):
|
|
122
|
+
super().__init__(left, right, '||')
|
|
123
|
+
|
|
124
|
+
def evaluate(self):
|
|
125
|
+
return self.left.evaluate() or self.right.evaluate()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class BooleanAndExpression(BooleanBinaryExpression):
|
|
129
|
+
def __init__(self, left: BooleanExpression, right: BooleanExpression):
|
|
130
|
+
super().__init__(left, right, '&&')
|
|
131
|
+
|
|
132
|
+
def evaluate(self):
|
|
133
|
+
return self.left.evaluate() and self.right.evaluate()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class BooleanNotExpression(BooleanExpression):
|
|
137
|
+
def __init__(self, expression: BooleanExpression):
|
|
138
|
+
self.expression = expression
|
|
139
|
+
|
|
140
|
+
def evaluate(self):
|
|
141
|
+
return not self.expression.evaluate()
|
|
142
|
+
|
|
143
|
+
def __str__(self):
|
|
144
|
+
exp = self.expression.__str__()
|
|
145
|
+
return f"!({exp})"
|
|
146
|
+
|
|
147
|
+
def __repr__(self):
|
|
148
|
+
return f"BooleanNotExpression({self.expression.__repr__()})"
|
|
149
|
+
|
|
150
|
+
def get_variables(self) -> dict[str, Variable]:
|
|
151
|
+
return self.expression.get_variables()
|