Typhon-Language 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Typhon/Driver/configs.py +14 -0
- Typhon/Driver/debugging.py +148 -5
- Typhon/Driver/diagnostic.py +4 -3
- Typhon/Driver/language_server.py +25 -0
- Typhon/Driver/run.py +1 -1
- Typhon/Driver/translate.py +16 -11
- Typhon/Driver/utils.py +39 -1
- Typhon/Grammar/_typhon_parser.py +2920 -2718
- Typhon/Grammar/parser.py +80 -53
- Typhon/Grammar/parser_helper.py +68 -87
- Typhon/Grammar/syntax_errors.py +41 -20
- Typhon/Grammar/token_factory_custom.py +541 -485
- Typhon/Grammar/tokenizer_custom.py +52 -0
- Typhon/Grammar/typhon_ast.py +754 -76
- Typhon/Grammar/typhon_ast_error.py +438 -0
- Typhon/Grammar/unparse_custom.py +25 -0
- Typhon/LanguageServer/__init__.py +3 -0
- Typhon/LanguageServer/client/__init__.py +42 -0
- Typhon/LanguageServer/client/pyrefly.py +115 -0
- Typhon/LanguageServer/client/pyright.py +173 -0
- Typhon/LanguageServer/semantic_tokens.py +446 -0
- Typhon/LanguageServer/server.py +376 -0
- Typhon/LanguageServer/utils.py +65 -0
- Typhon/SourceMap/ast_match_based_map.py +199 -152
- Typhon/SourceMap/ast_matching.py +102 -87
- Typhon/SourceMap/datatype.py +275 -264
- Typhon/SourceMap/defined_name_retrieve.py +145 -0
- Typhon/Transform/comprehension_to_function.py +2 -5
- Typhon/Transform/const_member_to_final.py +12 -7
- Typhon/Transform/extended_patterns.py +139 -0
- Typhon/Transform/forbidden_statements.py +25 -0
- Typhon/Transform/if_while_let.py +122 -11
- Typhon/Transform/inline_statement_block_capture.py +22 -15
- Typhon/Transform/optional_operators_to_checked.py +14 -6
- Typhon/Transform/placeholder_to_function.py +0 -1
- Typhon/Transform/record_to_dataclass.py +22 -238
- Typhon/Transform/scope_check_rename.py +109 -29
- Typhon/Transform/transform.py +16 -12
- Typhon/Transform/type_abbrev_desugar.py +11 -15
- Typhon/Transform/type_annotation_check_expand.py +2 -2
- Typhon/Transform/utils/__init__.py +0 -0
- Typhon/Transform/utils/imports.py +83 -0
- Typhon/Transform/{utils.py → utils/jump_away.py} +2 -38
- Typhon/Transform/utils/make_class.py +135 -0
- Typhon/Transform/visitor.py +25 -0
- Typhon/Typing/pyrefly.py +145 -0
- Typhon/Typing/pyright.py +141 -144
- Typhon/Typing/result_diagnostic.py +1 -1
- Typhon/__main__.py +15 -1
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/METADATA +13 -6
- typhon_language-0.1.4.dist-info/RECORD +65 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/WHEEL +1 -1
- typhon_language-0.1.4.dist-info/licenses/LICENSE +201 -0
- typhon_language-0.1.2.dist-info/RECORD +0 -48
- typhon_language-0.1.2.dist-info/licenses/LICENSE +0 -21
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/entry_points.txt +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/top_level.txt +0 -0
Typhon/SourceMap/datatype.py
CHANGED
|
@@ -1,264 +1,275 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from ..Grammar.typhon_ast import PosAttributes, PosRange, get_pos_attributes_if_exists
|
|
3
|
-
from ..Driver.debugging import debug_verbose_print
|
|
4
|
-
from intervaltree import IntervalTree, Interval
|
|
5
|
-
from typing import Iterable
|
|
6
|
-
import ast
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
column_offset = other.column
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
result_lines
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
def
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def
|
|
217
|
-
intervals = self.interval_tree.
|
|
218
|
-
return [
|
|
219
|
-
(Range.from_interval(interval), interval.data) # type: ignore[misc]
|
|
220
|
-
for interval in intervals # type: ignore[misc]
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
def
|
|
224
|
-
intervals = self.interval_tree.
|
|
225
|
-
return [
|
|
226
|
-
(Range.from_interval(interval), interval.data) # type: ignore[misc]
|
|
227
|
-
for interval in intervals # type: ignore[misc]
|
|
228
|
-
]
|
|
229
|
-
|
|
230
|
-
def
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def
|
|
249
|
-
containers = self._container_intervals(range)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from ..Grammar.typhon_ast import PosAttributes, PosRange, get_pos_attributes_if_exists
|
|
3
|
+
from ..Driver.debugging import debug_verbose_print
|
|
4
|
+
from intervaltree import IntervalTree, Interval
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
import ast
|
|
7
|
+
|
|
8
|
+
from basedpyright.langserver import main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, unsafe_hash=True, order=True)
|
|
12
|
+
class Pos:
|
|
13
|
+
line: int
|
|
14
|
+
column: int
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def from_start_pos_attributes(attr: PosAttributes) -> "Pos":
|
|
18
|
+
return Pos(line=attr["lineno"], column=attr["col_offset"])
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def from_end_pos_attributes(attr: PosAttributes) -> "Pos | None":
|
|
22
|
+
if attr["end_lineno"] is None or attr["end_col_offset"] is None:
|
|
23
|
+
return None
|
|
24
|
+
return Pos(line=attr["end_lineno"], column=attr["end_col_offset"])
|
|
25
|
+
|
|
26
|
+
def col_back(self) -> "Pos":
|
|
27
|
+
if self.column == 0:
|
|
28
|
+
return self
|
|
29
|
+
else:
|
|
30
|
+
return Pos(line=self.line, column=self.column - 1)
|
|
31
|
+
|
|
32
|
+
def col_forward(self) -> "Pos":
|
|
33
|
+
return Pos(line=self.line, column=self.column + 1)
|
|
34
|
+
|
|
35
|
+
def calc_offset(self, other: "Pos") -> "Pos":
|
|
36
|
+
line_offset = other.line - self.line
|
|
37
|
+
if line_offset == 0:
|
|
38
|
+
column_offset = other.column - self.column
|
|
39
|
+
else:
|
|
40
|
+
column_offset = other.column
|
|
41
|
+
return Pos(line=line_offset, column=column_offset)
|
|
42
|
+
|
|
43
|
+
def apply_offset(self, offset: "Range") -> "Range":
|
|
44
|
+
# offset is (start_offset, offset_from_start_to_end)
|
|
45
|
+
start_column_offset = 0
|
|
46
|
+
if offset.start.line == 0:
|
|
47
|
+
start_column_offset = self.column + offset.start.column
|
|
48
|
+
new_start_column = start_column_offset
|
|
49
|
+
end_column_start = 0
|
|
50
|
+
if offset.end.line == 0:
|
|
51
|
+
end_column_start = new_start_column
|
|
52
|
+
debug_verbose_print(
|
|
53
|
+
f"Applying offset: pos={self}, offset={offset} -> new_start_column={
|
|
54
|
+
new_start_column
|
|
55
|
+
}, end_column_start={end_column_start} = {
|
|
56
|
+
Range(
|
|
57
|
+
Pos(self.line + offset.start.line, new_start_column),
|
|
58
|
+
Pos(
|
|
59
|
+
self.line + offset.start.line + offset.end.line,
|
|
60
|
+
end_column_start + offset.end.column,
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
}"
|
|
64
|
+
)
|
|
65
|
+
return Range(
|
|
66
|
+
Pos(self.line + offset.start.line, new_start_column),
|
|
67
|
+
Pos(
|
|
68
|
+
self.line + offset.start.line + offset.end.line,
|
|
69
|
+
end_column_start + offset.end.column,
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Common central datatype for source range. All are 0 based.
|
|
75
|
+
@dataclass(frozen=True, unsafe_hash=True, order=True)
|
|
76
|
+
class Range:
|
|
77
|
+
start: Pos
|
|
78
|
+
end: Pos
|
|
79
|
+
|
|
80
|
+
def contains(self, pos: Pos) -> bool:
|
|
81
|
+
return self.start <= pos < self.end
|
|
82
|
+
|
|
83
|
+
def includes(self, other: "Range") -> bool:
|
|
84
|
+
return self.start <= other.start and other.end <= self.end
|
|
85
|
+
|
|
86
|
+
def calc_offset(self, other: "Range") -> "Range":
|
|
87
|
+
offset = self.start.calc_offset(other.start)
|
|
88
|
+
end_offset = other.start.calc_offset(other.end)
|
|
89
|
+
return Range(offset, end_offset)
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def from_positions(
|
|
93
|
+
start_line: int, start_column: int, end_line: int, end_column: int
|
|
94
|
+
) -> "Range":
|
|
95
|
+
return Range(
|
|
96
|
+
start=Pos(line=start_line, column=start_column),
|
|
97
|
+
end=Pos(line=end_line, column=end_column),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def from_pos_range(pos_range: PosRange) -> "Range":
|
|
102
|
+
return Range(
|
|
103
|
+
start=Pos(line=pos_range["lineno"] - 1, column=pos_range["col_offset"]),
|
|
104
|
+
end=Pos(
|
|
105
|
+
line=pos_range["end_lineno"] - 1, column=pos_range["end_col_offset"]
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def from_pos_attr(attr: PosAttributes) -> "Range | None":
|
|
111
|
+
# Python ast position is 1-based for line, 0-based for column
|
|
112
|
+
if attr["end_lineno"] is None or attr["end_col_offset"] is None:
|
|
113
|
+
return None
|
|
114
|
+
return Range(
|
|
115
|
+
start=Pos(line=attr["lineno"] - 1, column=attr["col_offset"]),
|
|
116
|
+
end=Pos(line=attr["end_lineno"] - 1, column=attr["end_col_offset"]),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def from_pos_attr_may_not_end(attr: PosAttributes) -> "Range":
|
|
121
|
+
start_line = attr["lineno"]
|
|
122
|
+
start_column = attr["col_offset"]
|
|
123
|
+
end_line = (
|
|
124
|
+
attr["end_lineno"] if attr["end_lineno"] is not None else attr["lineno"]
|
|
125
|
+
)
|
|
126
|
+
end_column = (
|
|
127
|
+
attr["end_col_offset"]
|
|
128
|
+
if attr["end_col_offset"] is not None
|
|
129
|
+
else attr["col_offset"] + 1
|
|
130
|
+
)
|
|
131
|
+
if end_line == start_line and start_column == end_column:
|
|
132
|
+
end_column += 1 # Ensure non-zero length
|
|
133
|
+
return Range(
|
|
134
|
+
start=Pos(line=start_line - 1, column=start_column),
|
|
135
|
+
end=Pos(line=end_line - 1, column=end_column),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def from_interval(interval: Interval) -> "Range":
|
|
140
|
+
return Range(start=interval.begin, end=interval.end) # type: ignore[misc]
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def from_ast_node(node: ast.AST) -> "Range | None":
|
|
144
|
+
attr = get_pos_attributes_if_exists(node)
|
|
145
|
+
if attr is None:
|
|
146
|
+
return None
|
|
147
|
+
return Range.from_pos_attr(attr)
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def from_syntax_error(e: SyntaxError) -> "Range":
|
|
151
|
+
start_line = e.lineno or 1
|
|
152
|
+
start_column = e.offset or 0
|
|
153
|
+
end_line = e.end_lineno or start_line
|
|
154
|
+
end_column = e.end_offset or (start_column + 1)
|
|
155
|
+
return Range(
|
|
156
|
+
start=Pos(line=start_line - 1, column=start_column),
|
|
157
|
+
end=Pos(line=end_line - 1, column=end_column),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def merge_ranges(ranges: Iterable["Range"]) -> "Range | None":
|
|
162
|
+
if not ranges:
|
|
163
|
+
return None
|
|
164
|
+
debug_verbose_print("Merging ranges: ", ranges)
|
|
165
|
+
ranges = list(ranges)
|
|
166
|
+
start = min(r.start for r in ranges)
|
|
167
|
+
end = max([r.end for r in ranges] + [start])
|
|
168
|
+
debug_verbose_print(f"Merged range: start={start}, end={end}")
|
|
169
|
+
return Range(start=start, end=end)
|
|
170
|
+
|
|
171
|
+
def of_string(self, source: str) -> str:
|
|
172
|
+
lines = source.splitlines()
|
|
173
|
+
return self.of_lines(lines)
|
|
174
|
+
|
|
175
|
+
def of_lines(self, lines: list[str]) -> str:
|
|
176
|
+
start_line = self.start.line
|
|
177
|
+
end_line = self.end.line
|
|
178
|
+
start_column = self.start.column
|
|
179
|
+
end_column = self.end.column
|
|
180
|
+
|
|
181
|
+
def get_in_line(line: str, start_col: int, end_col: int) -> str:
|
|
182
|
+
return line[start_col : min(end_col, len(line))]
|
|
183
|
+
|
|
184
|
+
if start_line >= len(lines) or end_line >= len(lines):
|
|
185
|
+
return ""
|
|
186
|
+
if start_line == end_line:
|
|
187
|
+
the_line = lines[start_line]
|
|
188
|
+
return get_in_line(the_line, start_column, end_column)
|
|
189
|
+
else:
|
|
190
|
+
result_lines: list[str] = []
|
|
191
|
+
result_lines.append(lines[start_line][start_column:])
|
|
192
|
+
for line_num in range(start_line, end_line):
|
|
193
|
+
result_lines.append(lines[line_num])
|
|
194
|
+
result_lines.append(get_in_line(lines[end_line], 0, end_column))
|
|
195
|
+
return "\n".join(result_lines)
|
|
196
|
+
|
|
197
|
+
def deconstruct_str(self):
|
|
198
|
+
return f"Range(Pos({self.start.line}, {self.start.column}), Pos({self.end.line}, {self.end.column}))"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
type RangeInterval[T] = tuple[Range, T]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# Wrapper around IntervalTree for Range keys
|
|
205
|
+
class RangeIntervalTree[T]:
|
|
206
|
+
def __init__(self):
|
|
207
|
+
self.interval_tree = IntervalTree()
|
|
208
|
+
|
|
209
|
+
def add(self, range: Range, data: T):
|
|
210
|
+
self.interval_tree.addi( # type: ignore[misc]
|
|
211
|
+
range.start,
|
|
212
|
+
range.end,
|
|
213
|
+
data,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def at(self, pos: Pos) -> list[RangeInterval[T]]:
|
|
217
|
+
intervals = self.interval_tree.at(pos) # type: ignore[misc]
|
|
218
|
+
return [
|
|
219
|
+
(Range.from_interval(interval), interval.data) # type: ignore[misc]
|
|
220
|
+
for interval in intervals # type: ignore[misc]
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
def overlap(self, range: Range) -> list[RangeInterval[T]]:
|
|
224
|
+
intervals = self.interval_tree.overlap(range.start, range.end) # type: ignore[misc]
|
|
225
|
+
return [
|
|
226
|
+
(Range.from_interval(interval), interval.data) # type: ignore[misc]
|
|
227
|
+
for interval in intervals # type: ignore[misc]
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
def envelop(self, range: Range) -> list[RangeInterval[T]]:
|
|
231
|
+
intervals = self.interval_tree.envelop(range.start, range.end) # type: ignore[misc]
|
|
232
|
+
return [
|
|
233
|
+
(Range.from_interval(interval), interval.data) # type: ignore[misc]
|
|
234
|
+
for interval in intervals # type: ignore[misc]
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
def _container_intervals(self, range: Range) -> list[Interval]:
|
|
238
|
+
# TODO: How to write this even IntervalTree doesn't have containers method?
|
|
239
|
+
start_intervals = self.interval_tree.at(range.start) # type: ignore[misc]
|
|
240
|
+
if not start_intervals:
|
|
241
|
+
return []
|
|
242
|
+
return [
|
|
243
|
+
interval # type: ignore[misc]
|
|
244
|
+
for interval in start_intervals # type: ignore[misc]
|
|
245
|
+
if (interval.contains_point(range.end) or interval.end == range.end) # type: ignore[misc]
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
def containers(self, range: Range) -> list[RangeInterval[T]]:
|
|
249
|
+
containers = self._container_intervals(range)
|
|
250
|
+
result: list[RangeInterval[T]] = []
|
|
251
|
+
for interval in containers:
|
|
252
|
+
result.append((Range.from_interval(interval), interval.data)) # type: ignore[misc]
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
def minimal_containers(self, range: Range) -> list[RangeInterval[T]]:
|
|
256
|
+
containers = self._container_intervals(range)
|
|
257
|
+
debug_verbose_print(f"Minimal containers for range {range}: {containers}")
|
|
258
|
+
if not containers:
|
|
259
|
+
return []
|
|
260
|
+
result: list[RangeInterval[T]] = []
|
|
261
|
+
interval: Interval
|
|
262
|
+
for i, interval in enumerate(containers):
|
|
263
|
+
is_minimal = True
|
|
264
|
+
for j, other_interval in enumerate(containers):
|
|
265
|
+
if (
|
|
266
|
+
i != j
|
|
267
|
+
and interval.contains_interval(other_interval) # type: ignore[misc]
|
|
268
|
+
and not interval.range_matches(other_interval) # type: ignore[misc]
|
|
269
|
+
):
|
|
270
|
+
is_minimal = False
|
|
271
|
+
break
|
|
272
|
+
if is_minimal:
|
|
273
|
+
debug_verbose_print(f" Minimal container: {interval}")
|
|
274
|
+
result.append((Range.from_interval(interval), interval.data)) # type: ignore[misc]
|
|
275
|
+
return result
|