cyberark-tpc-plugin-parser 0.1.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.
- cyberark_tpc_plugin_parser-0.1.0/LICENSE +21 -0
- cyberark_tpc_plugin_parser-0.1.0/PKG-INFO +72 -0
- cyberark_tpc_plugin_parser-0.1.0/README.md +34 -0
- cyberark_tpc_plugin_parser-0.1.0/cyberark_tpc_plugin_parser.egg-info/PKG-INFO +72 -0
- cyberark_tpc_plugin_parser-0.1.0/cyberark_tpc_plugin_parser.egg-info/SOURCES.txt +25 -0
- cyberark_tpc_plugin_parser-0.1.0/cyberark_tpc_plugin_parser.egg-info/dependency_links.txt +1 -0
- cyberark_tpc_plugin_parser-0.1.0/cyberark_tpc_plugin_parser.egg-info/top_level.txt +1 -0
- cyberark_tpc_plugin_parser-0.1.0/pyproject.toml +34 -0
- cyberark_tpc_plugin_parser-0.1.0/setup.cfg +4 -0
- cyberark_tpc_plugin_parser-0.1.0/tests/test_lexer.py +263 -0
- cyberark_tpc_plugin_parser-0.1.0/tests/test_parser.py +44 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/__init__..py +1 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/__init__.py +1 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/lexer.py +238 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/__init__.py +1 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/assignment.py +16 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/comment.py +14 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/cpm_parameter_validation.py +17 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/fail_state.py +16 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/parse_error.py +14 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/section_header.py +14 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/tokens/transition.py +16 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/utilities/__init__.py +1 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/utilities/regex.py +10 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/utilities/token_name.py +15 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/lexer/utilities/types.py +23 -0
- cyberark_tpc_plugin_parser-0.1.0/tpc_plugin_parser/parser.py +89 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 (Peter McDonald)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cyberark-tpc-plugin-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package to help parse CyberArk TPC plugin prompts and process files.
|
|
5
|
+
Author-email: Peter McDonald <git@petermcdonald.co.uk>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 (Peter McDonald)
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: Programming Language :: Python
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
34
|
+
Requires-Python: >=3.11
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
License-File: LICENSE
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# CyberArk TPC Plugin Parser
|
|
40
|
+
|
|
41
|
+
CyberArk TPC Plugin Parser is a package intended to make it easy to work with CyberArk process
|
|
42
|
+
and platform ini files.
|
|
43
|
+
|
|
44
|
+
The lexer and parser breaks down the files to their constituent parts so that they can be
|
|
45
|
+
further processed by other applications.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
CyberArk TPC Plugin Parser can be installed using Pip:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install cyberark-tpc-plugin-parser
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
The main point of entry is the parser, this takes the content of the process and prompts
|
|
58
|
+
files as arguments:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from tpc_plugin_parser.parser import Parser
|
|
62
|
+
|
|
63
|
+
with open('/path/to/files/process.ini') as process_fh:
|
|
64
|
+
process_content: str = process_fh.read()
|
|
65
|
+
|
|
66
|
+
with open('/path/to/files/prompts.ini') as prompts_fh:
|
|
67
|
+
prompts_content: str = prompts_fh.read()
|
|
68
|
+
|
|
69
|
+
parser: Parser = Parser(process_file=process_content, prompts_file=prompts_content)
|
|
70
|
+
process_file_tokens = parser.process_file
|
|
71
|
+
prompt_file_tokens = parser.prompts_file
|
|
72
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CyberArk TPC Plugin Parser
|
|
2
|
+
|
|
3
|
+
CyberArk TPC Plugin Parser is a package intended to make it easy to work with CyberArk process
|
|
4
|
+
and platform ini files.
|
|
5
|
+
|
|
6
|
+
The lexer and parser breaks down the files to their constituent parts so that they can be
|
|
7
|
+
further processed by other applications.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
CyberArk TPC Plugin Parser can be installed using Pip:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install cyberark-tpc-plugin-parser
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
The main point of entry is the parser, this takes the content of the process and prompts
|
|
20
|
+
files as arguments:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from tpc_plugin_parser.parser import Parser
|
|
24
|
+
|
|
25
|
+
with open('/path/to/files/process.ini') as process_fh:
|
|
26
|
+
process_content: str = process_fh.read()
|
|
27
|
+
|
|
28
|
+
with open('/path/to/files/prompts.ini') as prompts_fh:
|
|
29
|
+
prompts_content: str = prompts_fh.read()
|
|
30
|
+
|
|
31
|
+
parser: Parser = Parser(process_file=process_content, prompts_file=prompts_content)
|
|
32
|
+
process_file_tokens = parser.process_file
|
|
33
|
+
prompt_file_tokens = parser.prompts_file
|
|
34
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cyberark-tpc-plugin-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package to help parse CyberArk TPC plugin prompts and process files.
|
|
5
|
+
Author-email: Peter McDonald <git@petermcdonald.co.uk>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 (Peter McDonald)
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: Programming Language :: Python
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
34
|
+
Requires-Python: >=3.11
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
License-File: LICENSE
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# CyberArk TPC Plugin Parser
|
|
40
|
+
|
|
41
|
+
CyberArk TPC Plugin Parser is a package intended to make it easy to work with CyberArk process
|
|
42
|
+
and platform ini files.
|
|
43
|
+
|
|
44
|
+
The lexer and parser breaks down the files to their constituent parts so that they can be
|
|
45
|
+
further processed by other applications.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
CyberArk TPC Plugin Parser can be installed using Pip:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install cyberark-tpc-plugin-parser
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
The main point of entry is the parser, this takes the content of the process and prompts
|
|
58
|
+
files as arguments:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from tpc_plugin_parser.parser import Parser
|
|
62
|
+
|
|
63
|
+
with open('/path/to/files/process.ini') as process_fh:
|
|
64
|
+
process_content: str = process_fh.read()
|
|
65
|
+
|
|
66
|
+
with open('/path/to/files/prompts.ini') as prompts_fh:
|
|
67
|
+
prompts_content: str = prompts_fh.read()
|
|
68
|
+
|
|
69
|
+
parser: Parser = Parser(process_file=process_content, prompts_file=prompts_content)
|
|
70
|
+
process_file_tokens = parser.process_file
|
|
71
|
+
prompt_file_tokens = parser.prompts_file
|
|
72
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
cyberark_tpc_plugin_parser.egg-info/PKG-INFO
|
|
5
|
+
cyberark_tpc_plugin_parser.egg-info/SOURCES.txt
|
|
6
|
+
cyberark_tpc_plugin_parser.egg-info/dependency_links.txt
|
|
7
|
+
cyberark_tpc_plugin_parser.egg-info/top_level.txt
|
|
8
|
+
tests/test_lexer.py
|
|
9
|
+
tests/test_parser.py
|
|
10
|
+
tpc_plugin_parser/__init__..py
|
|
11
|
+
tpc_plugin_parser/parser.py
|
|
12
|
+
tpc_plugin_parser/lexer/__init__.py
|
|
13
|
+
tpc_plugin_parser/lexer/lexer.py
|
|
14
|
+
tpc_plugin_parser/lexer/tokens/__init__.py
|
|
15
|
+
tpc_plugin_parser/lexer/tokens/assignment.py
|
|
16
|
+
tpc_plugin_parser/lexer/tokens/comment.py
|
|
17
|
+
tpc_plugin_parser/lexer/tokens/cpm_parameter_validation.py
|
|
18
|
+
tpc_plugin_parser/lexer/tokens/fail_state.py
|
|
19
|
+
tpc_plugin_parser/lexer/tokens/parse_error.py
|
|
20
|
+
tpc_plugin_parser/lexer/tokens/section_header.py
|
|
21
|
+
tpc_plugin_parser/lexer/tokens/transition.py
|
|
22
|
+
tpc_plugin_parser/lexer/utilities/__init__.py
|
|
23
|
+
tpc_plugin_parser/lexer/utilities/regex.py
|
|
24
|
+
tpc_plugin_parser/lexer/utilities/token_name.py
|
|
25
|
+
tpc_plugin_parser/lexer/utilities/types.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tpc_plugin_parser
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cyberark-tpc-plugin-parser"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A package to help parse CyberArk TPC plugin prompts and process files."
|
|
5
|
+
authors = [{ name = "Peter McDonald", email = "git@petermcdonald.co.uk"}]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = { file = "LICENSE" }
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Programming Language :: Python :: 3.14",
|
|
16
|
+
"Programming Language :: Python :: 3.15",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
dependencies = []
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=8.4.2",
|
|
24
|
+
"pytest-cov>=7.0.0",
|
|
25
|
+
"ruff>=0.13.0",
|
|
26
|
+
"troml>=0.4.1",
|
|
27
|
+
"ty>=0.0.1a20",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
addopts = "--cov=tpc_plugin_parser --cov-report term-missing --cov-report xml:coverage.xml"
|
|
32
|
+
|
|
33
|
+
[tool.ruff]
|
|
34
|
+
line-length = 120
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""Test the lexer."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from tpc_plugin_parser.lexer.lexer import Lexer
|
|
5
|
+
from tpc_plugin_parser.lexer.tokens.assignment import Assignment
|
|
6
|
+
from tpc_plugin_parser.lexer.tokens.comment import Comment
|
|
7
|
+
from tpc_plugin_parser.lexer.tokens.cpm_parameter_validation import CPMParameterValidation
|
|
8
|
+
from tpc_plugin_parser.lexer.tokens.fail_state import FailState
|
|
9
|
+
from tpc_plugin_parser.lexer.tokens.parse_error import ParseError
|
|
10
|
+
from tpc_plugin_parser.lexer.tokens.section_header import SectionHeader
|
|
11
|
+
from tpc_plugin_parser.lexer.tokens.transition import Transition
|
|
12
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestLexer(object):
|
|
16
|
+
"""Test the lexer."""
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize(
|
|
19
|
+
"line,expected_token_list",
|
|
20
|
+
[
|
|
21
|
+
(
|
|
22
|
+
'password, source=FILE, Mandatory=![string equal -nocase "<username>" ""], allowcharacters=abc',
|
|
23
|
+
[
|
|
24
|
+
(
|
|
25
|
+
TokenName.CPM_PARAMETER_VALIDATION,
|
|
26
|
+
CPMParameterValidation(
|
|
27
|
+
line_number=1,
|
|
28
|
+
name="password",
|
|
29
|
+
source="FILE",
|
|
30
|
+
mandatory='![string equal -nocase "<username>" ""]',
|
|
31
|
+
allow_characters="abc",
|
|
32
|
+
token_name=TokenName.CPM_PARAMETER_VALIDATION.value,
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
],
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
"TestVar",
|
|
39
|
+
[
|
|
40
|
+
(
|
|
41
|
+
TokenName.ASSIGNMENT,
|
|
42
|
+
Assignment(
|
|
43
|
+
line_number=1,
|
|
44
|
+
name="TestVar",
|
|
45
|
+
equals=None,
|
|
46
|
+
assigned=None,
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
],
|
|
50
|
+
),
|
|
51
|
+
(
|
|
52
|
+
"testvar = ",
|
|
53
|
+
[
|
|
54
|
+
(
|
|
55
|
+
TokenName.ASSIGNMENT,
|
|
56
|
+
Assignment(
|
|
57
|
+
line_number=1,
|
|
58
|
+
name="testvar",
|
|
59
|
+
equals="=",
|
|
60
|
+
assigned=None,
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
],
|
|
64
|
+
),
|
|
65
|
+
(
|
|
66
|
+
"test_var = 123",
|
|
67
|
+
[
|
|
68
|
+
(
|
|
69
|
+
TokenName.ASSIGNMENT,
|
|
70
|
+
Assignment(
|
|
71
|
+
line_number=1,
|
|
72
|
+
name="test_var",
|
|
73
|
+
equals="=",
|
|
74
|
+
assigned="123",
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
(
|
|
80
|
+
"# this is a standard comment ",
|
|
81
|
+
[
|
|
82
|
+
(
|
|
83
|
+
TokenName.COMMENT,
|
|
84
|
+
Comment(
|
|
85
|
+
line_number=1,
|
|
86
|
+
content="# this is a standard comment",
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
),
|
|
91
|
+
(
|
|
92
|
+
";this is a standard comment",
|
|
93
|
+
[
|
|
94
|
+
(
|
|
95
|
+
TokenName.COMMENT,
|
|
96
|
+
Comment(
|
|
97
|
+
line_number=1,
|
|
98
|
+
content=";this is a standard comment",
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
],
|
|
102
|
+
),
|
|
103
|
+
(
|
|
104
|
+
"standard=FAIL('This is a standard fail state', 1234)",
|
|
105
|
+
[
|
|
106
|
+
(
|
|
107
|
+
TokenName.FAIL_STATE,
|
|
108
|
+
FailState(
|
|
109
|
+
name="standard",
|
|
110
|
+
line_number=1,
|
|
111
|
+
message="'This is a standard fail state'",
|
|
112
|
+
code=1234,
|
|
113
|
+
),
|
|
114
|
+
),
|
|
115
|
+
],
|
|
116
|
+
),
|
|
117
|
+
(
|
|
118
|
+
"standard = FAIL ('This is a standard fail state, isn't it?', 2468)",
|
|
119
|
+
[
|
|
120
|
+
(
|
|
121
|
+
TokenName.FAIL_STATE,
|
|
122
|
+
FailState(
|
|
123
|
+
name="standard",
|
|
124
|
+
line_number=1,
|
|
125
|
+
message="'This is a standard fail state, isn't it?'",
|
|
126
|
+
code=2468,
|
|
127
|
+
),
|
|
128
|
+
),
|
|
129
|
+
],
|
|
130
|
+
),
|
|
131
|
+
(
|
|
132
|
+
"standard = FAIL (This is a standard fail state, isn't it?, 2468)",
|
|
133
|
+
[
|
|
134
|
+
(
|
|
135
|
+
TokenName.FAIL_STATE,
|
|
136
|
+
FailState(
|
|
137
|
+
name="standard",
|
|
138
|
+
line_number=1,
|
|
139
|
+
message="This is a standard fail state, isn't it?",
|
|
140
|
+
code=2468,
|
|
141
|
+
),
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
),
|
|
145
|
+
(
|
|
146
|
+
"[Some Section Header]",
|
|
147
|
+
[
|
|
148
|
+
(
|
|
149
|
+
TokenName.SECTION_HEADER,
|
|
150
|
+
SectionHeader(
|
|
151
|
+
line_number=1,
|
|
152
|
+
name="Some Section Header",
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
],
|
|
156
|
+
),
|
|
157
|
+
(
|
|
158
|
+
" [Some Section Header] ",
|
|
159
|
+
[
|
|
160
|
+
(
|
|
161
|
+
TokenName.SECTION_HEADER,
|
|
162
|
+
SectionHeader(
|
|
163
|
+
line_number=1,
|
|
164
|
+
name="Some Section Header",
|
|
165
|
+
),
|
|
166
|
+
),
|
|
167
|
+
],
|
|
168
|
+
),
|
|
169
|
+
(
|
|
170
|
+
"state1,condition,state2",
|
|
171
|
+
[
|
|
172
|
+
(
|
|
173
|
+
TokenName.TRANSITION,
|
|
174
|
+
Transition(
|
|
175
|
+
line_number=1,
|
|
176
|
+
current_state="state1",
|
|
177
|
+
condition="condition",
|
|
178
|
+
next_state="state2",
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
],
|
|
182
|
+
),
|
|
183
|
+
(
|
|
184
|
+
" state_1 , condition2 , STATE2 ",
|
|
185
|
+
[
|
|
186
|
+
(
|
|
187
|
+
TokenName.TRANSITION,
|
|
188
|
+
Transition(
|
|
189
|
+
line_number=1,
|
|
190
|
+
current_state="state_1",
|
|
191
|
+
condition="condition2",
|
|
192
|
+
next_state="STATE2",
|
|
193
|
+
),
|
|
194
|
+
),
|
|
195
|
+
],
|
|
196
|
+
),
|
|
197
|
+
(
|
|
198
|
+
"[Some Section Header]\r\rTestVar\r",
|
|
199
|
+
[
|
|
200
|
+
(
|
|
201
|
+
TokenName.SECTION_HEADER,
|
|
202
|
+
SectionHeader(
|
|
203
|
+
line_number=1,
|
|
204
|
+
name="Some Section Header",
|
|
205
|
+
),
|
|
206
|
+
),
|
|
207
|
+
(
|
|
208
|
+
TokenName.ASSIGNMENT,
|
|
209
|
+
Assignment(
|
|
210
|
+
line_number=3,
|
|
211
|
+
name="TestVar",
|
|
212
|
+
equals=None,
|
|
213
|
+
assigned=None,
|
|
214
|
+
),
|
|
215
|
+
),
|
|
216
|
+
],
|
|
217
|
+
),
|
|
218
|
+
],
|
|
219
|
+
)
|
|
220
|
+
def test_token(self, line: str, expected_token_list) -> None:
|
|
221
|
+
"""
|
|
222
|
+
Test to ensure that a token parses ok.
|
|
223
|
+
|
|
224
|
+
:param line: The line to be parsed.
|
|
225
|
+
:param expected_token_list: A copy of the object we expect to receive back.
|
|
226
|
+
"""
|
|
227
|
+
lexer = Lexer(source=line)
|
|
228
|
+
tokens = lexer.tokens
|
|
229
|
+
|
|
230
|
+
# The following 2 lines have been added to ensure we receive the same tokens after process running twice.
|
|
231
|
+
lexer.process()
|
|
232
|
+
tokens_post_process = lexer.tokens
|
|
233
|
+
|
|
234
|
+
assert tokens == expected_token_list
|
|
235
|
+
assert tokens_post_process == tokens
|
|
236
|
+
|
|
237
|
+
@pytest.mark.parametrize(
|
|
238
|
+
"line,expected_tokens",
|
|
239
|
+
[
|
|
240
|
+
(
|
|
241
|
+
"This line will not match.",
|
|
242
|
+
[
|
|
243
|
+
ParseError(
|
|
244
|
+
line_number=1,
|
|
245
|
+
content="This line will not match.",
|
|
246
|
+
)
|
|
247
|
+
],
|
|
248
|
+
),
|
|
249
|
+
],
|
|
250
|
+
)
|
|
251
|
+
def test_unmatched_lines(self, line: str, expected_tokens: list[ParseError]) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Test to ensure that the lexer returns a ParseError token.
|
|
254
|
+
|
|
255
|
+
:param line: The line to be parsed.
|
|
256
|
+
:param expected_tokens: Parse error tokens.
|
|
257
|
+
"""
|
|
258
|
+
lex: Lexer = Lexer(source=line)
|
|
259
|
+
lex.process()
|
|
260
|
+
found_tokens = lex.tokens
|
|
261
|
+
assert len(found_tokens) == len(expected_tokens)
|
|
262
|
+
assert found_tokens[0][1].content == expected_tokens[0].content
|
|
263
|
+
assert found_tokens[0][1].line_number == expected_tokens[0].line_number
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Test the parser."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from tpc_plugin_parser.parser import Parser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestParser(object):
|
|
8
|
+
"""Test the lexer."""
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
"process_file,prompts_file",
|
|
12
|
+
[
|
|
13
|
+
(
|
|
14
|
+
"tests/data/process.ini",
|
|
15
|
+
"tests/data/prompts.ini",
|
|
16
|
+
),
|
|
17
|
+
],
|
|
18
|
+
)
|
|
19
|
+
def test_parser(self, process_file: str, prompts_file: str) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Test to ensure that a token parses ok.
|
|
22
|
+
|
|
23
|
+
:param process_file: Path to the process file.
|
|
24
|
+
:param prompts_file: Path to the prompts file.
|
|
25
|
+
"""
|
|
26
|
+
with open(process_file, "r") as process_file:
|
|
27
|
+
process_content = process_file.read()
|
|
28
|
+
|
|
29
|
+
with open(prompts_file, "r") as prompts_file:
|
|
30
|
+
prompts_content = prompts_file.read()
|
|
31
|
+
|
|
32
|
+
parser = Parser(process_file=process_content, prompts_file=prompts_content)
|
|
33
|
+
|
|
34
|
+
assert len(parser.process_file) == 6
|
|
35
|
+
assert len(parser.process_file["default"]) == 6
|
|
36
|
+
assert len(parser.process_file["states"]) == 8
|
|
37
|
+
assert len(parser.process_file["transitions"]) == 7
|
|
38
|
+
assert len(parser.process_file["CPM Parameters Validation"]) == 5
|
|
39
|
+
assert len(parser.process_file["parameters"]) == 5
|
|
40
|
+
assert len(parser.process_file["Debug Information"]) == 7
|
|
41
|
+
|
|
42
|
+
assert len(parser.prompts_file) == 2
|
|
43
|
+
assert len(parser.prompts_file["default"]) == 6
|
|
44
|
+
assert len(parser.prompts_file["conditions"]) == 8
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Standard blank init file for the package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Standard blank init file for the package."""
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Lexer module for reading and processing configuration files."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.tokens.assignment import Assignment
|
|
6
|
+
from tpc_plugin_parser.lexer.tokens.comment import Comment
|
|
7
|
+
from tpc_plugin_parser.lexer.tokens.cpm_parameter_validation import (
|
|
8
|
+
CPMParameterValidation,
|
|
9
|
+
)
|
|
10
|
+
from tpc_plugin_parser.lexer.tokens.fail_state import FailState
|
|
11
|
+
from tpc_plugin_parser.lexer.tokens.parse_error import ParseError
|
|
12
|
+
from tpc_plugin_parser.lexer.tokens.section_header import SectionHeader
|
|
13
|
+
from tpc_plugin_parser.lexer.tokens.transition import Transition
|
|
14
|
+
from tpc_plugin_parser.lexer.utilities.regex import (
|
|
15
|
+
ASSIGNMENT,
|
|
16
|
+
COMMENT,
|
|
17
|
+
CPM_PARAMETER_VALIDATION,
|
|
18
|
+
FAIL_STATE,
|
|
19
|
+
SECTION_HEADER,
|
|
20
|
+
TRANSITION,
|
|
21
|
+
)
|
|
22
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
23
|
+
from tpc_plugin_parser.lexer.utilities.types import ALL_TOKEN_TYPES, TokenSpecs
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Lexer(object):
|
|
27
|
+
"""Object to handle processing the ini files."""
|
|
28
|
+
|
|
29
|
+
__slots__ = (
|
|
30
|
+
"_tokens",
|
|
31
|
+
"_source",
|
|
32
|
+
"_token_specs",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def __init__(self, source: str) -> None:
|
|
36
|
+
"""Standard init for the Lexer object."""
|
|
37
|
+
|
|
38
|
+
self._tokens: list[
|
|
39
|
+
tuple[
|
|
40
|
+
TokenName,
|
|
41
|
+
ALL_TOKEN_TYPES,
|
|
42
|
+
]
|
|
43
|
+
] = []
|
|
44
|
+
self._source: str = source
|
|
45
|
+
self._token_specs: list[TokenSpecs] = [
|
|
46
|
+
{
|
|
47
|
+
"pattern": re.compile(ASSIGNMENT, re.IGNORECASE),
|
|
48
|
+
"token_name": TokenName.ASSIGNMENT,
|
|
49
|
+
"processor_method": "_process_assignment",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"pattern": re.compile(COMMENT, re.IGNORECASE),
|
|
53
|
+
"token_name": TokenName.COMMENT,
|
|
54
|
+
"processor_method": "_process_comment",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"pattern": re.compile(CPM_PARAMETER_VALIDATION, re.IGNORECASE),
|
|
58
|
+
"token_name": TokenName.CPM_PARAMETER_VALIDATION,
|
|
59
|
+
"processor_method": "_process_cpm_parameter_validation",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"pattern": re.compile(FAIL_STATE, re.IGNORECASE),
|
|
63
|
+
"token_name": TokenName.FAIL_STATE,
|
|
64
|
+
"processor_method": "_process_fail_state",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"pattern": re.compile(SECTION_HEADER, re.IGNORECASE),
|
|
68
|
+
"token_name": TokenName.SECTION_HEADER,
|
|
69
|
+
"processor_method": "_process_section_header",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"pattern": re.compile(TRANSITION, re.IGNORECASE),
|
|
73
|
+
"token_name": TokenName.TRANSITION,
|
|
74
|
+
"processor_method": "_process_transitions",
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
def process(self) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Process the content of the file line by line.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
if self._tokens:
|
|
84
|
+
# Returning as we have parsed the data already.
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
for line_number, line in enumerate(self._source.splitlines(), start=1):
|
|
88
|
+
for token_spec in self._token_specs:
|
|
89
|
+
if match := token_spec["pattern"].match(line):
|
|
90
|
+
getattr(self, token_spec["processor_method"])(match=match, line_number=line_number)
|
|
91
|
+
break
|
|
92
|
+
else:
|
|
93
|
+
if line.strip():
|
|
94
|
+
self._process_parse_error(line=line, line_number=line_number)
|
|
95
|
+
|
|
96
|
+
def _process_assignment(self, match: re.Match, line_number: int) -> None:
|
|
97
|
+
"""
|
|
98
|
+
Process a variable assignment line
|
|
99
|
+
|
|
100
|
+
:param match: Regex match of the assignment.
|
|
101
|
+
"""
|
|
102
|
+
name: str = str(match.group("name")).strip()
|
|
103
|
+
equals = str(match.group("equals")).strip() if match.groupdict().get("equals", None) else None
|
|
104
|
+
assigned_stripped = str(match.group("value")).strip() if match.groupdict().get("value", None) else None
|
|
105
|
+
assigned = assigned_stripped or None
|
|
106
|
+
self._tokens.append(
|
|
107
|
+
(
|
|
108
|
+
TokenName.ASSIGNMENT,
|
|
109
|
+
Assignment(
|
|
110
|
+
name=name,
|
|
111
|
+
equals=equals,
|
|
112
|
+
assigned=assigned,
|
|
113
|
+
line_number=line_number,
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def _process_comment(self, match: re.Match, line_number: int) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Process the provided comment.
|
|
121
|
+
|
|
122
|
+
:param match: Regex match of the comment.
|
|
123
|
+
"""
|
|
124
|
+
self._tokens.append(
|
|
125
|
+
(
|
|
126
|
+
TokenName.COMMENT,
|
|
127
|
+
Comment(
|
|
128
|
+
content=str(match.group("comment")).strip(),
|
|
129
|
+
line_number=line_number,
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def _process_cpm_parameter_validation(self, match: re.Match, line_number: int) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Process the provided parameter validation.
|
|
137
|
+
|
|
138
|
+
:param match: Regex match of the parameter validation.
|
|
139
|
+
"""
|
|
140
|
+
allow_characters: str | None = None
|
|
141
|
+
if match.group("allowcharacters"):
|
|
142
|
+
allow_characters = str(match.group("allowcharacters")).strip()
|
|
143
|
+
|
|
144
|
+
self._tokens.append(
|
|
145
|
+
(
|
|
146
|
+
TokenName.CPM_PARAMETER_VALIDATION,
|
|
147
|
+
CPMParameterValidation(
|
|
148
|
+
name=str(match.group("name")),
|
|
149
|
+
source=str(match.group("source")),
|
|
150
|
+
mandatory=str(match.group("mandatory")),
|
|
151
|
+
allow_characters=allow_characters,
|
|
152
|
+
line_number=line_number,
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def _process_fail_state(self, match: re.Match, line_number: int) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Process the provided fail state .
|
|
160
|
+
|
|
161
|
+
:param match: Regex match of the fail state.
|
|
162
|
+
"""
|
|
163
|
+
self._tokens.append(
|
|
164
|
+
(
|
|
165
|
+
TokenName.FAIL_STATE,
|
|
166
|
+
FailState(
|
|
167
|
+
name=str(match.group("name")).strip(),
|
|
168
|
+
message=str(match.group("message")).strip(),
|
|
169
|
+
code=int(match.group("code")),
|
|
170
|
+
line_number=line_number,
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def _process_parse_error(self, line: str, line_number: int) -> None:
|
|
176
|
+
"""
|
|
177
|
+
Process a line that has a parse error.
|
|
178
|
+
|
|
179
|
+
:param line: The line that has the parse error.
|
|
180
|
+
:param line_number: The line number of the line that has the parse error.
|
|
181
|
+
"""
|
|
182
|
+
self._tokens.append(
|
|
183
|
+
(
|
|
184
|
+
TokenName.PARSE_ERROR,
|
|
185
|
+
ParseError(
|
|
186
|
+
content=line.strip(),
|
|
187
|
+
line_number=line_number,
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _process_section_header(self, match: re.Match, line_number: int) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Process the provided section header.
|
|
195
|
+
|
|
196
|
+
:param match: Regex match of the section header.
|
|
197
|
+
"""
|
|
198
|
+
self._tokens.append(
|
|
199
|
+
(
|
|
200
|
+
TokenName.SECTION_HEADER,
|
|
201
|
+
SectionHeader(
|
|
202
|
+
name=str(match.group("name").strip()),
|
|
203
|
+
line_number=line_number,
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def _process_transitions(self, match: re.Match, line_number: int) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Process the provided transitions.
|
|
211
|
+
|
|
212
|
+
:param match: Regex match of the transitions.
|
|
213
|
+
"""
|
|
214
|
+
self._tokens.append(
|
|
215
|
+
(
|
|
216
|
+
TokenName.TRANSITION,
|
|
217
|
+
Transition(
|
|
218
|
+
current_state=str(match.group("current")).strip(),
|
|
219
|
+
condition=str(match.group("condition")).strip(),
|
|
220
|
+
next_state=str(match.group("next")).strip(),
|
|
221
|
+
line_number=line_number,
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def tokens(
|
|
228
|
+
self,
|
|
229
|
+
) -> list[
|
|
230
|
+
tuple[
|
|
231
|
+
TokenName,
|
|
232
|
+
ALL_TOKEN_TYPES,
|
|
233
|
+
]
|
|
234
|
+
]:
|
|
235
|
+
"""A list of tokens found by the lexer."""
|
|
236
|
+
if not self._tokens:
|
|
237
|
+
self.process()
|
|
238
|
+
return self._tokens
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Standard blank init file for the package."""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Class to hold the assignment token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Assignment(object):
|
|
10
|
+
"""Dataclass to hold variable assignment details."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
name: str
|
|
14
|
+
equals: str | None = None
|
|
15
|
+
assigned: str | None = None
|
|
16
|
+
token_name: str = TokenName.ASSIGNMENT.value
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Class to hold the comment token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Comment(object):
|
|
10
|
+
"""Dataclass to hold a comment."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
content: str
|
|
14
|
+
token_name: str = TokenName.COMMENT.value
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Class to hold the parameter validation token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class CPMParameterValidation(object):
|
|
10
|
+
"""Dataclass to hold variable cpm parameter validation details."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
name: str
|
|
14
|
+
source: str
|
|
15
|
+
mandatory: str
|
|
16
|
+
allow_characters: str | None = None
|
|
17
|
+
token_name: str = TokenName.CPM_PARAMETER_VALIDATION.value
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Class to hold the fail state token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class FailState(object):
|
|
10
|
+
"""Dataclass to hold a fail state."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
name: str
|
|
14
|
+
message: str
|
|
15
|
+
code: int
|
|
16
|
+
token_name: str = TokenName.FAIL_STATE.value
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Class to hold a parse error token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class ParseError(object):
|
|
10
|
+
"""Dataclass to hold sparse errors."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
content: str
|
|
14
|
+
token_name: str = TokenName.PARSE_ERROR.value
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Class to hold the section header token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class SectionHeader(object):
|
|
10
|
+
"""Dataclass to hold a section header name."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
name: str
|
|
14
|
+
token_name: str = TokenName.SECTION_HEADER.value
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Class to hold the state transition token."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Transition(object):
|
|
10
|
+
"""Dataclass to hold state transitions."""
|
|
11
|
+
|
|
12
|
+
line_number: int
|
|
13
|
+
current_state: str
|
|
14
|
+
condition: str
|
|
15
|
+
next_state: str
|
|
16
|
+
token_name: str = TokenName.TRANSITION.value
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Standard blank init file for the package."""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""List of regex the lexer uses."""
|
|
2
|
+
|
|
3
|
+
ASSIGNMENT = (
|
|
4
|
+
r"^(?:[\s]*)(?P<name>[\w]+)(?:(?:[\s]*)(?P<equals>=)(?:(?:[\s]*)(?P<value>(?:(?!\s*fail\s*\().)+))?)?(?:[\s]*)$"
|
|
5
|
+
)
|
|
6
|
+
COMMENT = r"^(?:[\s]*)(?P<comment>(?:[#;]+)(?:.*))(?:[\s]*)$"
|
|
7
|
+
CPM_PARAMETER_VALIDATION = r"^(?:[\s]*)(?P<name>[\w\\]+)(?:(?:[\s]*,[\s]*)(?:source)(?:[\s]*)=(?:[\s]*)(?P<source>[^, ]*))(?:(?:[\s]*,[\s]*)(?:mandatory)(?:[\s]*)=(?:[\s]*)(?P<mandatory>[^,]*))?(?:(?:[\s]*,[\s]*)(?:allowcharacters)(?:[\s]*)=(?:[\s]*)(?P<allowcharacters>.*))?$"
|
|
8
|
+
FAIL_STATE = r"^(?:[\s]*)(?P<name>[\w]+)(?:[\s]*)=(?:[\s]*)(?:[\s]*)(?:fail)(?:[\s]*)\((?:[\s]*)(?P<message>.*)(?:[\s])?,(?:[\s]*)(?P<code>[0-9]+)\)(?:[\s]*)$"
|
|
9
|
+
SECTION_HEADER = r"^(?:[\s]*)\[(?P<name>[\w]+(?:[\s]+[\w]+)*)](?:[\s]*)$"
|
|
10
|
+
TRANSITION = r"^(?:[\s]*)(?P<current>[\w]+)(?:[\s]*,[\s]*)(?P<condition>[\w]+)(?:[\s]*,[\s]*)(?P<next>[\w]+)(?:[\s]*)$"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Enum to hold the allowed token names."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TokenName(Enum):
|
|
7
|
+
"""CLass to hold the token name."""
|
|
8
|
+
|
|
9
|
+
ASSIGNMENT = "Assignment"
|
|
10
|
+
COMMENT = "Comment"
|
|
11
|
+
FAIL_STATE = "Fail State"
|
|
12
|
+
CPM_PARAMETER_VALIDATION = "CPM Parameter Validation"
|
|
13
|
+
PARSE_ERROR = "Parse Error"
|
|
14
|
+
SECTION_HEADER = "Section Header"
|
|
15
|
+
TRANSITION = "Transition"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Module for aggregating all token types used in the lexer."""
|
|
2
|
+
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from tpc_plugin_parser.lexer.tokens.assignment import Assignment
|
|
7
|
+
from tpc_plugin_parser.lexer.tokens.comment import Comment
|
|
8
|
+
from tpc_plugin_parser.lexer.tokens.cpm_parameter_validation import (
|
|
9
|
+
CPMParameterValidation,
|
|
10
|
+
)
|
|
11
|
+
from tpc_plugin_parser.lexer.tokens.fail_state import FailState
|
|
12
|
+
from tpc_plugin_parser.lexer.tokens.parse_error import ParseError
|
|
13
|
+
from tpc_plugin_parser.lexer.tokens.section_header import SectionHeader
|
|
14
|
+
from tpc_plugin_parser.lexer.tokens.transition import Transition
|
|
15
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
16
|
+
|
|
17
|
+
ALL_TOKEN_TYPES = Assignment | Comment | FailState | CPMParameterValidation | ParseError | SectionHeader | Transition
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TokenSpecs(TypedDict):
|
|
21
|
+
pattern: re.Pattern
|
|
22
|
+
token_name: TokenName
|
|
23
|
+
processor_method: str
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Parser module for reading and processing configuration files."""
|
|
2
|
+
|
|
3
|
+
from tpc_plugin_parser.lexer.lexer import Lexer
|
|
4
|
+
from tpc_plugin_parser.lexer.tokens.section_header import SectionHeader
|
|
5
|
+
from tpc_plugin_parser.lexer.utilities.token_name import TokenName
|
|
6
|
+
from tpc_plugin_parser.lexer.utilities.types import ALL_TOKEN_TYPES
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Parser(object):
|
|
10
|
+
"""Object to handle parsing ini files."""
|
|
11
|
+
|
|
12
|
+
__slots__ = (
|
|
13
|
+
"_process_file",
|
|
14
|
+
"_prompts_file",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def __init__(self, process_file: str, prompts_file: str) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Initializes the Parser with the given process and prompts files.
|
|
20
|
+
|
|
21
|
+
:param process_file (str): Content of the process file.
|
|
22
|
+
:param prompts_file (str): Content of the prompts file.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
process_lexer = Lexer(source=process_file)
|
|
26
|
+
self._prepare_process(lexed_process=process_lexer)
|
|
27
|
+
|
|
28
|
+
prompts_lexer = Lexer(source=prompts_file)
|
|
29
|
+
self._prepare_prompts(lexed_prompts=prompts_lexer)
|
|
30
|
+
|
|
31
|
+
def _prepare_process(self, lexed_process: Lexer) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Prepare the process file from the lexed result.
|
|
34
|
+
|
|
35
|
+
:param lexed_process: Result of lexing the process file.
|
|
36
|
+
"""
|
|
37
|
+
self._process_file = self._process_lex(lexed_file=lexed_process)
|
|
38
|
+
|
|
39
|
+
def _prepare_prompts(self, lexed_prompts: Lexer) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Prepare the prompts file from the lexed result.
|
|
42
|
+
|
|
43
|
+
:param lexed_prompts: Result of lexing the prompts file.
|
|
44
|
+
"""
|
|
45
|
+
self._prompts_file = self._process_lex(lexed_file=lexed_prompts)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _process_lex(lexed_file: Lexer):
|
|
49
|
+
"""
|
|
50
|
+
Process a lex and return the results.
|
|
51
|
+
|
|
52
|
+
:param lexed_file: Result of lexing a file.
|
|
53
|
+
|
|
54
|
+
:return: Result of processing the lexed file.
|
|
55
|
+
"""
|
|
56
|
+
current_section_name: str = "default"
|
|
57
|
+
section_entries: list[ALL_TOKEN_TYPES,] = []
|
|
58
|
+
sorted_lex = {}
|
|
59
|
+
for lexed_line in lexed_file.tokens:
|
|
60
|
+
if lexed_line[0] == TokenName.SECTION_HEADER:
|
|
61
|
+
sorted_lex[current_section_name] = section_entries
|
|
62
|
+
if isinstance(lexed_line[1], SectionHeader):
|
|
63
|
+
current_section_name = lexed_line[1].name
|
|
64
|
+
else:
|
|
65
|
+
# This should never be reached, this is here to satisfy typing.
|
|
66
|
+
current_section_name = "UNKNOWN"
|
|
67
|
+
section_entries = []
|
|
68
|
+
continue
|
|
69
|
+
section_entries.append(lexed_line[1])
|
|
70
|
+
sorted_lex[current_section_name] = section_entries
|
|
71
|
+
return sorted_lex
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def process_file(self):
|
|
75
|
+
"""
|
|
76
|
+
Returns the parsed process configuration.
|
|
77
|
+
|
|
78
|
+
:return: List of tokens from the process file.
|
|
79
|
+
"""
|
|
80
|
+
return self._process_file
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def prompts_file(self):
|
|
84
|
+
"""
|
|
85
|
+
Returns the parsed prompts file.
|
|
86
|
+
|
|
87
|
+
:return: List of tokens from the prompts file.
|
|
88
|
+
"""
|
|
89
|
+
return self._prompts_file
|