docdsl 0.0.1__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.
- docdsl-0.0.1/LICENSE +22 -0
- docdsl-0.0.1/PKG-INFO +179 -0
- docdsl-0.0.1/README.md +155 -0
- docdsl-0.0.1/docdsl/__init__.py +19 -0
- docdsl-0.0.1/docdsl/commands.py +338 -0
- docdsl-0.0.1/docdsl/dsl_builtins.py +119 -0
- docdsl-0.0.1/docdsl/dtformats.py +190 -0
- docdsl-0.0.1/docdsl/exceptions.py +201 -0
- docdsl-0.0.1/docdsl/grammar.py +208 -0
- docdsl-0.0.1/docdsl/parser.py +77 -0
- docdsl-0.0.1/docdsl/text_to_regex.py +266 -0
- docdsl-0.0.1/docdsl/translator.py +94 -0
- docdsl-0.0.1/docdsl.egg-info/PKG-INFO +179 -0
- docdsl-0.0.1/docdsl.egg-info/SOURCES.txt +17 -0
- docdsl-0.0.1/docdsl.egg-info/dependency_links.txt +1 -0
- docdsl-0.0.1/docdsl.egg-info/requires.txt +1 -0
- docdsl-0.0.1/docdsl.egg-info/top_level.txt +1 -0
- docdsl-0.0.1/pyproject.toml +50 -0
- docdsl-0.0.1/setup.cfg +4 -0
docdsl-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Shine Jayakumar
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
docdsl-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: docdsl
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A domain-specific language (DSL) for building readable text extraction rules.
|
|
5
|
+
Author-email: Shine Jayakumar <shinejayakumar@yahoo.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shine-jayakumar/docdsl
|
|
8
|
+
Project-URL: Repository, https://github.com/shine-jayakumar/docdsl
|
|
9
|
+
Project-URL: Documentation, https://github.com/shine-jayakumar/docdsl/blob/master/Documentation.md
|
|
10
|
+
Project-URL: Issues, https://github.com/shine-jayakumar/docdsl/issues
|
|
11
|
+
Keywords: dsl,regex,parser,text-extraction,pattern-matching,textx
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Topic :: Text Processing
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: textX<5,>=4.4
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# DocDSL
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
A declarative DSL for extracting structured information from OCR output, PDFs, reports, and other semi-structured documents.
|
|
34
|
+
|
|
35
|
+
Instead of writing large, difficult-to-maintain regular expressions, **DocDSL** lets you describe **what** to extract using a simple, readable language while keeping extraction rules separate from your application code.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install docdsl
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# Quick Start
|
|
48
|
+
|
|
49
|
+
## 1. Define your entities
|
|
50
|
+
|
|
51
|
+
Entities are reusable regular expressions that can be referenced throughout your DSL.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from docdsl import DSLTranslator, Entity
|
|
55
|
+
|
|
56
|
+
NAME = Entity(
|
|
57
|
+
name="NAME",
|
|
58
|
+
pattern=r"[A-Za-z ,.'-]+"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
POSTCODE = Entity(
|
|
62
|
+
name="POSTCODE",
|
|
63
|
+
pattern=r"[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}"
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 2. Create a translator
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
translator = DSLTranslator(
|
|
71
|
+
entities=[
|
|
72
|
+
NAME,
|
|
73
|
+
POSTCODE,
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 3. Write your extraction rules
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
dsl = """
|
|
82
|
+
FIND "Name:";
|
|
83
|
+
SKIP UNTIL NEWLINE;
|
|
84
|
+
CAPTURE TARGET [@NAME];
|
|
85
|
+
"""
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 4. Generate the regular expression
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
pattern = translator.translate(dsl)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Your DSL is translated into a regular expression that can be used as part of your document extraction pipeline.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
# Features
|
|
99
|
+
|
|
100
|
+
- Declarative, English-like extraction language
|
|
101
|
+
- Reusable named entities
|
|
102
|
+
- Readable extraction rules
|
|
103
|
+
- Conditional extraction using `IF`
|
|
104
|
+
- Multi-line capture
|
|
105
|
+
- Capture between delimiters
|
|
106
|
+
- Built-in helper tokens
|
|
107
|
+
- Friendly syntax and validation errors
|
|
108
|
+
- Exact error locations with line and column information
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
# Entities
|
|
113
|
+
|
|
114
|
+
Entities represent reusable regular expressions.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
TITLE = Entity(
|
|
118
|
+
name="TITLE",
|
|
119
|
+
pattern=r"(?:Mr|Mrs|Ms|Miss|Dr)\.?"
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Supply your entities when creating the translator.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
translator = DSLTranslator(
|
|
127
|
+
entities=[
|
|
128
|
+
TITLE,
|
|
129
|
+
NAME,
|
|
130
|
+
POSTCODE,
|
|
131
|
+
]
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Inside the DSL, entities are referenced using the `@` prefix.
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
@TITLE
|
|
139
|
+
@NAME
|
|
140
|
+
@POSTCODE
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
If the DSL references an entity that was not supplied to `DSLTranslator`, an `UndefinedEntity` exception is raised.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
# Documentation
|
|
148
|
+
|
|
149
|
+
For the complete language reference, see **DSL_REFERENCE.md**.
|
|
150
|
+
|
|
151
|
+
The reference includes:
|
|
152
|
+
|
|
153
|
+
- Complete command reference
|
|
154
|
+
- DSL syntax
|
|
155
|
+
- Built-in helper tokens
|
|
156
|
+
- Conditional statements
|
|
157
|
+
- Examples
|
|
158
|
+
- Best practices
|
|
159
|
+
- Exception reference
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
# Requirements
|
|
164
|
+
|
|
165
|
+
- Python 3.11+
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
# Contributing
|
|
170
|
+
|
|
171
|
+
Contributions, bug reports and feature requests are welcome.
|
|
172
|
+
|
|
173
|
+
If you discover a bug or have an idea for improving **DocDSL**, please open an issue or submit a pull request.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
# License
|
|
178
|
+
|
|
179
|
+
Released under the MIT License.
|
docdsl-0.0.1/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# DocDSL
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
A declarative DSL for extracting structured information from OCR output, PDFs, reports, and other semi-structured documents.
|
|
10
|
+
|
|
11
|
+
Instead of writing large, difficult-to-maintain regular expressions, **DocDSL** lets you describe **what** to extract using a simple, readable language while keeping extraction rules separate from your application code.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install docdsl
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Quick Start
|
|
24
|
+
|
|
25
|
+
## 1. Define your entities
|
|
26
|
+
|
|
27
|
+
Entities are reusable regular expressions that can be referenced throughout your DSL.
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from docdsl import DSLTranslator, Entity
|
|
31
|
+
|
|
32
|
+
NAME = Entity(
|
|
33
|
+
name="NAME",
|
|
34
|
+
pattern=r"[A-Za-z ,.'-]+"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
POSTCODE = Entity(
|
|
38
|
+
name="POSTCODE",
|
|
39
|
+
pattern=r"[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}"
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 2. Create a translator
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
translator = DSLTranslator(
|
|
47
|
+
entities=[
|
|
48
|
+
NAME,
|
|
49
|
+
POSTCODE,
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 3. Write your extraction rules
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
dsl = """
|
|
58
|
+
FIND "Name:";
|
|
59
|
+
SKIP UNTIL NEWLINE;
|
|
60
|
+
CAPTURE TARGET [@NAME];
|
|
61
|
+
"""
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 4. Generate the regular expression
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
pattern = translator.translate(dsl)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Your DSL is translated into a regular expression that can be used as part of your document extraction pipeline.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
# Features
|
|
75
|
+
|
|
76
|
+
- Declarative, English-like extraction language
|
|
77
|
+
- Reusable named entities
|
|
78
|
+
- Readable extraction rules
|
|
79
|
+
- Conditional extraction using `IF`
|
|
80
|
+
- Multi-line capture
|
|
81
|
+
- Capture between delimiters
|
|
82
|
+
- Built-in helper tokens
|
|
83
|
+
- Friendly syntax and validation errors
|
|
84
|
+
- Exact error locations with line and column information
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
# Entities
|
|
89
|
+
|
|
90
|
+
Entities represent reusable regular expressions.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
TITLE = Entity(
|
|
94
|
+
name="TITLE",
|
|
95
|
+
pattern=r"(?:Mr|Mrs|Ms|Miss|Dr)\.?"
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Supply your entities when creating the translator.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
translator = DSLTranslator(
|
|
103
|
+
entities=[
|
|
104
|
+
TITLE,
|
|
105
|
+
NAME,
|
|
106
|
+
POSTCODE,
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Inside the DSL, entities are referenced using the `@` prefix.
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
@TITLE
|
|
115
|
+
@NAME
|
|
116
|
+
@POSTCODE
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If the DSL references an entity that was not supplied to `DSLTranslator`, an `UndefinedEntity` exception is raised.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Documentation
|
|
124
|
+
|
|
125
|
+
For the complete language reference, see **DSL_REFERENCE.md**.
|
|
126
|
+
|
|
127
|
+
The reference includes:
|
|
128
|
+
|
|
129
|
+
- Complete command reference
|
|
130
|
+
- DSL syntax
|
|
131
|
+
- Built-in helper tokens
|
|
132
|
+
- Conditional statements
|
|
133
|
+
- Examples
|
|
134
|
+
- Best practices
|
|
135
|
+
- Exception reference
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
# Requirements
|
|
140
|
+
|
|
141
|
+
- Python 3.11+
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
# Contributing
|
|
146
|
+
|
|
147
|
+
Contributions, bug reports and feature requests are welcome.
|
|
148
|
+
|
|
149
|
+
If you discover a bug or have an idea for improving **DocDSL**, please open an issue or submit a pull request.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# License
|
|
154
|
+
|
|
155
|
+
Released under the MIT License.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File: __init__.py
|
|
3
|
+
Project: Docdsl
|
|
4
|
+
File Created: Wed 15 Jul 2026 08:07:46 (Shine Jayakumar)
|
|
5
|
+
Author: Shine Jayakumar (shinejayakumar@yahoo.com)
|
|
6
|
+
-----
|
|
7
|
+
Last Modified: Wed 15 Jul 2026 08:07:46 (Shine Jayakumar)
|
|
8
|
+
Modified By: Shine Jayakumar (shinejayakumar@yahoo.com)
|
|
9
|
+
-----
|
|
10
|
+
Copyright (c) 2026 Shine Jayakumar
|
|
11
|
+
SPDX-License-Identifier: MIT
|
|
12
|
+
|
|
13
|
+
Licensed under the MIT License.
|
|
14
|
+
See the LICENSE file in the project root for the full license text.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__version__ = "0.0.1"
|
|
18
|
+
|
|
19
|
+
from .translator import DSLTranslator, Entity
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File: commands.py
|
|
3
|
+
Project: Docdsl
|
|
4
|
+
File Created: Wed 15 Jul 2026 08:07:54 (Shine Jayakumar)
|
|
5
|
+
Author: Shine Jayakumar (shinejayakumar@yahoo.com)
|
|
6
|
+
-----
|
|
7
|
+
Last Modified: Wed 15 Jul 2026 08:07:54 (Shine Jayakumar)
|
|
8
|
+
Modified By: Shine Jayakumar (shinejayakumar@yahoo.com)
|
|
9
|
+
-----
|
|
10
|
+
Copyright (c) 2026 Shine Jayakumar
|
|
11
|
+
SPDX-License-Identifier: MIT
|
|
12
|
+
|
|
13
|
+
Licensed under the MIT License.
|
|
14
|
+
See the LICENSE file in the project root for the full license text.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from .dsl_builtins import *
|
|
20
|
+
from .text_to_regex import TextToRegex
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Entity:
|
|
25
|
+
parent: object
|
|
26
|
+
name: str
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def group_name(self):
|
|
30
|
+
return f"{self.name[1:]}"
|
|
31
|
+
|
|
32
|
+
def to_regex(self):
|
|
33
|
+
return f"__{self.name[1:]}__"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Command:
|
|
38
|
+
parent: object
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Find(Command):
|
|
43
|
+
items: list = None
|
|
44
|
+
|
|
45
|
+
def to_regex(self):
|
|
46
|
+
pat_parts = [item.to_regex() for item in self.items]
|
|
47
|
+
pattern = "|".join(pat_parts)
|
|
48
|
+
pattern = f"(?:{pattern})" if len(self.items) > 1 else pattern
|
|
49
|
+
return pattern
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class FindItem(Command):
|
|
54
|
+
|
|
55
|
+
text: str = ""
|
|
56
|
+
ignorecase: bool = False
|
|
57
|
+
|
|
58
|
+
def to_regex(self):
|
|
59
|
+
pat = TextToRegex().get_regex(self.text)
|
|
60
|
+
pat = f"(?i:{pat})" if self.ignorecase else pat
|
|
61
|
+
return pat
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class Skip(Command):
|
|
66
|
+
|
|
67
|
+
position: str = ""
|
|
68
|
+
until: str = ""
|
|
69
|
+
ignorecase: bool = False
|
|
70
|
+
|
|
71
|
+
def to_regex(self):
|
|
72
|
+
if self.position:
|
|
73
|
+
return self.position.to_regex()
|
|
74
|
+
|
|
75
|
+
if self.until:
|
|
76
|
+
until = re.escape(self.until)
|
|
77
|
+
until = f"(?i:{until})" if self.ignorecase else until
|
|
78
|
+
pat = rf".*?{until}"
|
|
79
|
+
return pat
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class SkipLines(Command):
|
|
84
|
+
|
|
85
|
+
until: str
|
|
86
|
+
|
|
87
|
+
def to_regex(self):
|
|
88
|
+
until = re.escape(self.until)
|
|
89
|
+
pat = rf"[\s\S]*?{until}"
|
|
90
|
+
return pat
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class SkipNLines(Command):
|
|
95
|
+
|
|
96
|
+
lines: int
|
|
97
|
+
|
|
98
|
+
def to_regex(self):
|
|
99
|
+
pat = rf"(?:.*[\r\n]){{{self.lines}}}"
|
|
100
|
+
return pat
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
#@dataclass
|
|
104
|
+
#class Capture(Command):
|
|
105
|
+
#
|
|
106
|
+
# items: list = None
|
|
107
|
+
# until: str = ""
|
|
108
|
+
# position: Position = None
|
|
109
|
+
#
|
|
110
|
+
# def to_regex(self):
|
|
111
|
+
# target_count = sum([bool(item.target) for item in self.items])
|
|
112
|
+
# if not target_count:
|
|
113
|
+
# raise Exception("CAPTURE requires at least one target entity")
|
|
114
|
+
# if target_count > 1:
|
|
115
|
+
# raise Exception("CAPTURE cannot have more than one target entity")
|
|
116
|
+
#
|
|
117
|
+
# target = self.items[-1]
|
|
118
|
+
# pat_parts = [item.to_regex() for item in self.items[:-1]]
|
|
119
|
+
# target_pat = target.target.to_regex()
|
|
120
|
+
# group_name = target.target.entity.group_name
|
|
121
|
+
# pat_parts.append(f"(?P<{group_name}>{target_pat})")
|
|
122
|
+
# if self.until:
|
|
123
|
+
# until_pat = TextToRegex().get_regex(self.until)
|
|
124
|
+
# pat_parts.append(until_pat)
|
|
125
|
+
# elif self.position:
|
|
126
|
+
# pos = self.position.to_regex()
|
|
127
|
+
# pat_parts.append(pos)
|
|
128
|
+
# return "".join(pat_parts)
|
|
129
|
+
|
|
130
|
+
#def __repr__(self):
|
|
131
|
+
# return (
|
|
132
|
+
# "Capture(items=[\n"
|
|
133
|
+
# f"{self.items}\n]\n,"
|
|
134
|
+
# f"until={self.until},\n"
|
|
135
|
+
# f"position={self.position}\n"
|
|
136
|
+
# ")"
|
|
137
|
+
# )
|
|
138
|
+
@dataclass
|
|
139
|
+
class TargetEntity(Command):
|
|
140
|
+
|
|
141
|
+
entity: Entity
|
|
142
|
+
|
|
143
|
+
def to_regex(self):
|
|
144
|
+
return self.entity.to_regex()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class Capture(Command):
|
|
150
|
+
|
|
151
|
+
items: list = None
|
|
152
|
+
target: TargetEntity = None
|
|
153
|
+
until: str = ""
|
|
154
|
+
position: Position = None
|
|
155
|
+
|
|
156
|
+
def to_regex(self):
|
|
157
|
+
pat_parts = [item.to_regex() for item in self.items]
|
|
158
|
+
target_pat = self.target.entity.to_regex()
|
|
159
|
+
group_name = self.target.entity.group_name
|
|
160
|
+
pat_parts.append(f"(?P<{group_name}>{target_pat})")
|
|
161
|
+
if self.until:
|
|
162
|
+
until_pat = TextToRegex().get_regex(self.until)
|
|
163
|
+
pat_parts.append(until_pat)
|
|
164
|
+
elif self.position:
|
|
165
|
+
pos = self.position.to_regex()
|
|
166
|
+
pat_parts.append(pos)
|
|
167
|
+
return "".join(pat_parts)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
#@dataclass
|
|
172
|
+
#class CaptureItem(Command):
|
|
173
|
+
#
|
|
174
|
+
# optional: bool = False
|
|
175
|
+
# builtin: Builtin=None
|
|
176
|
+
# entity: str = None
|
|
177
|
+
# target: TargetEntity = None
|
|
178
|
+
#
|
|
179
|
+
# def to_regex(self):
|
|
180
|
+
# if self.builtin:
|
|
181
|
+
# pat = self.builtin.to_regex()
|
|
182
|
+
# pat = f"{pat}?" if self.optional else pat
|
|
183
|
+
# return pat
|
|
184
|
+
#
|
|
185
|
+
# if self.entity:
|
|
186
|
+
# pat = self.entity.to_regex()
|
|
187
|
+
# pat = f"{pat}?" if self.optional else pat
|
|
188
|
+
# return pat
|
|
189
|
+
#
|
|
190
|
+
# if self.target:
|
|
191
|
+
# return self.target.entity.to_regex()
|
|
192
|
+
|
|
193
|
+
@dataclass
|
|
194
|
+
class CaptureItem(Command):
|
|
195
|
+
|
|
196
|
+
optional: bool = False
|
|
197
|
+
builtin: Builtin=None
|
|
198
|
+
entity: str = None
|
|
199
|
+
|
|
200
|
+
def to_regex(self):
|
|
201
|
+
if self.builtin:
|
|
202
|
+
pat = self.builtin.to_regex()
|
|
203
|
+
pat = f"{pat}?" if self.optional else pat
|
|
204
|
+
return pat
|
|
205
|
+
|
|
206
|
+
if self.entity:
|
|
207
|
+
pat = self.entity.to_regex()
|
|
208
|
+
pat = f"{pat}?" if self.optional else pat
|
|
209
|
+
return pat
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass
|
|
213
|
+
class CaptureNLines(Command):
|
|
214
|
+
|
|
215
|
+
lines: int
|
|
216
|
+
target: TargetEntity
|
|
217
|
+
|
|
218
|
+
def to_regex(self):
|
|
219
|
+
group_name = self.target.entity.group_name
|
|
220
|
+
#pat = rf"(?P<{entity}>(?:.*[\r\n]){{{self.lines}}})"
|
|
221
|
+
pat = rf"(?P<{group_name}>(?:.*\r?\n){{{self.lines}}})"
|
|
222
|
+
return pat
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@dataclass
|
|
226
|
+
class CaptureLines(Command):
|
|
227
|
+
|
|
228
|
+
until: str
|
|
229
|
+
target: TargetEntity
|
|
230
|
+
ignorecase: bool = False
|
|
231
|
+
|
|
232
|
+
def to_regex(self):
|
|
233
|
+
group_name = self.target.entity.group_name
|
|
234
|
+
between = AcrossLines(None, None).to_regex()
|
|
235
|
+
until = TextToRegex().get_regex(self.until)
|
|
236
|
+
until = f"(?i:{until})" if self.ignorecase else until
|
|
237
|
+
pat = rf"(?P<{group_name}>{between}){until}"
|
|
238
|
+
return pat
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@dataclass
|
|
242
|
+
class CaptureBetween(Command):
|
|
243
|
+
|
|
244
|
+
start: str
|
|
245
|
+
end: str
|
|
246
|
+
target: TargetEntity
|
|
247
|
+
scope: CaptureLinesScope = None
|
|
248
|
+
|
|
249
|
+
def to_regex(self):
|
|
250
|
+
group_name = self.target.entity.group_name
|
|
251
|
+
start = self.start.to_regex()
|
|
252
|
+
end = self.end.to_regex()
|
|
253
|
+
between = (
|
|
254
|
+
AcrossLines(None, None).to_regex() if not self.scope
|
|
255
|
+
else self.scope.to_regex()
|
|
256
|
+
)
|
|
257
|
+
pat = rf"{start}(?P<{group_name}>{between}){end}"
|
|
258
|
+
return pat
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@dataclass
|
|
262
|
+
class CaptureBetweenItem(Command):
|
|
263
|
+
|
|
264
|
+
text: str
|
|
265
|
+
ignorecase: bool = False
|
|
266
|
+
|
|
267
|
+
def to_regex(self):
|
|
268
|
+
pat = TextToRegex().get_regex(self.text)
|
|
269
|
+
pat = f"(?i:{pat})" if self.ignorecase else pat
|
|
270
|
+
return pat
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@dataclass
|
|
274
|
+
class If(Command):
|
|
275
|
+
|
|
276
|
+
items: list = None
|
|
277
|
+
commands: list[Command] = None
|
|
278
|
+
|
|
279
|
+
def _get_item_pattern(self) -> str:
|
|
280
|
+
pat_parts = [item.to_regex() for item in self.items]
|
|
281
|
+
pat = "|".join(pat_parts)
|
|
282
|
+
pat = f"({pat})" if len(pat_parts) > 1 else pat
|
|
283
|
+
return pat
|
|
284
|
+
|
|
285
|
+
def _get_command_pattern(self) -> str:
|
|
286
|
+
pat_parts = [cmd.to_regex() for cmd in self.commands]
|
|
287
|
+
pat = "".join(pat_parts)
|
|
288
|
+
return pat
|
|
289
|
+
|
|
290
|
+
def to_regex(self):
|
|
291
|
+
#pat_parts = [item.to_regex() for item in self.items]
|
|
292
|
+
#pat = "|".join(pat_parts)
|
|
293
|
+
#pat = f"({pat})" if len(pat_parts) > 1 else pat
|
|
294
|
+
|
|
295
|
+
#pat += self.action.to_regex()
|
|
296
|
+
pat = self._get_item_pattern()
|
|
297
|
+
pat += self._get_command_pattern()
|
|
298
|
+
pat = f"(?:{pat})?"
|
|
299
|
+
return pat
|
|
300
|
+
|
|
301
|
+
#for item in self.items:
|
|
302
|
+
# pat = self.item.to_regex()
|
|
303
|
+
#pat = ""
|
|
304
|
+
#if self.keyword:
|
|
305
|
+
# kwd_pattern = TextToRegex().get_regex(self.keyword)
|
|
306
|
+
# pat += kwd_pattern
|
|
307
|
+
#elif self.entity:
|
|
308
|
+
# ent = f"__{self.entity}__"
|
|
309
|
+
# pat += ent
|
|
310
|
+
#action_pattern = self.action.to_regex()
|
|
311
|
+
#pat += action_pattern
|
|
312
|
+
#pat = f"(?:{pat})?"
|
|
313
|
+
#return pat
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@dataclass
|
|
318
|
+
class IfItem(Command):
|
|
319
|
+
|
|
320
|
+
text: str = ""
|
|
321
|
+
entity: Entity = None
|
|
322
|
+
ignorecase: bool = False
|
|
323
|
+
|
|
324
|
+
def to_regex(self):
|
|
325
|
+
if self.text:
|
|
326
|
+
pat = TextToRegex().get_regex(self.text)
|
|
327
|
+
pat = f"(?i:{pat})" if self.ignorecase else pat
|
|
328
|
+
return pat
|
|
329
|
+
|
|
330
|
+
# for entity
|
|
331
|
+
pat = self.entity.to_regex()
|
|
332
|
+
pat = f"(?i:{pat})" if self.ignorecase else pat
|
|
333
|
+
return pat
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == "__main__":
|
|
338
|
+
pass
|