proj-flow 0.18.0__py3-none-any.whl → 0.19.0__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.
- proj_flow/__init__.py +1 -1
- proj_flow/ext/webidl/__init__.py +12 -0
- proj_flow/ext/webidl/base/__init__.py +2 -0
- proj_flow/ext/webidl/base/config.py +425 -0
- proj_flow/ext/webidl/cli/__init__.py +10 -0
- proj_flow/ext/webidl/cli/cmake.py +82 -0
- proj_flow/ext/webidl/cli/depfile.py +83 -0
- proj_flow/ext/webidl/cli/gen.py +157 -0
- proj_flow/ext/webidl/cli/init.py +26 -0
- proj_flow/ext/webidl/cli/root.py +12 -0
- proj_flow/ext/webidl/cli/updater.py +20 -0
- proj_flow/ext/webidl/data/init/flow_webidl.cmake +26 -0
- proj_flow/ext/webidl/data/templates/cmake.mustache +45 -0
- proj_flow/ext/webidl/data/templates/depfile.mustache +6 -0
- proj_flow/ext/webidl/data/templates/partials/cxx/attribute-decl.mustache +1 -0
- proj_flow/ext/webidl/data/templates/partials/cxx/in-out.mustache +2 -0
- proj_flow/ext/webidl/data/templates/partials/cxx/includes.mustache +6 -0
- proj_flow/ext/webidl/data/templates/partials/cxx/operation-decl.mustache +12 -0
- proj_flow/ext/webidl/data/templates/partials/cxx/type.mustache +6 -0
- proj_flow/ext/webidl/data/types/cxx.json +47 -0
- proj_flow/ext/webidl/model/__init__.py +2 -0
- proj_flow/ext/webidl/model/ast.py +586 -0
- proj_flow/ext/webidl/model/builders.py +230 -0
- proj_flow/ext/webidl/registry.py +23 -0
- {proj_flow-0.18.0.dist-info → proj_flow-0.19.0.dist-info}/METADATA +2 -1
- {proj_flow-0.18.0.dist-info → proj_flow-0.19.0.dist-info}/RECORD +29 -6
- {proj_flow-0.18.0.dist-info → proj_flow-0.19.0.dist-info}/WHEEL +0 -0
- {proj_flow-0.18.0.dist-info → proj_flow-0.19.0.dist-info}/entry_points.txt +0 -0
- {proj_flow-0.18.0.dist-info → proj_flow-0.19.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
# Copyright (c) 2026 Marcin Zdun
|
|
2
|
+
# This code is licensed under MIT license (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, TypeVar, cast
|
|
8
|
+
|
|
9
|
+
from pywebidl2 import expr, parser, raw_parse, validate
|
|
10
|
+
|
|
11
|
+
from proj_flow.ext.webidl.base.config import ExtAttrsContextBuilders, TypeReplacement
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _flatten_union(ast: expr.IdlType) -> expr.IdlType:
|
|
15
|
+
arguments = cast(list[expr.IdlType], ast.idl_type)
|
|
16
|
+
result: list[expr.IdlType] = []
|
|
17
|
+
for argument in arguments:
|
|
18
|
+
if not argument.union:
|
|
19
|
+
result.append(argument)
|
|
20
|
+
continue
|
|
21
|
+
argument = _flatten_union(argument)
|
|
22
|
+
if argument.nullable:
|
|
23
|
+
result.append(argument)
|
|
24
|
+
sub_args = cast(list[expr.IdlType], argument.idl_type)
|
|
25
|
+
result.extend(sub_args)
|
|
26
|
+
ast.idl_type = result
|
|
27
|
+
return ast
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class TypeVisitor:
|
|
32
|
+
known_types: set[str]
|
|
33
|
+
types: dict[str, TypeReplacement]
|
|
34
|
+
local_references: set[str] = field(default_factory=set)
|
|
35
|
+
modules_or_includes: set[str] = field(default_factory=set)
|
|
36
|
+
|
|
37
|
+
def on_type_name(self, type_name: str):
|
|
38
|
+
if type_name in self.known_types:
|
|
39
|
+
self.local_references.add(type_name)
|
|
40
|
+
return type_name
|
|
41
|
+
|
|
42
|
+
replacement = self.types.get(type_name)
|
|
43
|
+
if replacement:
|
|
44
|
+
if replacement.module_or_include:
|
|
45
|
+
self.modules_or_includes.add(replacement.module_or_include)
|
|
46
|
+
return replacement.name
|
|
47
|
+
|
|
48
|
+
return type_name
|
|
49
|
+
|
|
50
|
+
def on_type(self, type: "Type"):
|
|
51
|
+
if type.nullable:
|
|
52
|
+
replacement = self.types.get("optional")
|
|
53
|
+
if replacement and replacement.module_or_include:
|
|
54
|
+
self.modules_or_includes.add(replacement.module_or_include)
|
|
55
|
+
|
|
56
|
+
type.name = self.on_type_name(type.idl_name)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class TypeArg:
|
|
61
|
+
arg_type: "Type | expr.IdlType"
|
|
62
|
+
first: bool = False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Type:
|
|
67
|
+
idl_name: str
|
|
68
|
+
name: str | None = field(default=None)
|
|
69
|
+
arguments: list[TypeArg] = field(default_factory=list)
|
|
70
|
+
nullable: bool = field(default=False)
|
|
71
|
+
generic: bool = field(default=False)
|
|
72
|
+
ext_attrs: dict = field(default_factory=dict)
|
|
73
|
+
|
|
74
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
75
|
+
visitor.on_type(self)
|
|
76
|
+
for arg in self.arguments:
|
|
77
|
+
if isinstance(arg.arg_type, Type):
|
|
78
|
+
arg.arg_type.populate_references(visitor)
|
|
79
|
+
|
|
80
|
+
def __patched_type(self):
|
|
81
|
+
if self.generic and self.idl_name == "sequence":
|
|
82
|
+
if self.ext_attrs.get("unique"):
|
|
83
|
+
self.idl_name = "set"
|
|
84
|
+
elif self.ext_attrs.get("span"):
|
|
85
|
+
self.idl_name = "span"
|
|
86
|
+
elif self.generic and self.idl_name == "record":
|
|
87
|
+
first_arg = (
|
|
88
|
+
self.arguments[0].arg_type
|
|
89
|
+
if self.arguments and isinstance(self.arguments[0].arg_type, Type)
|
|
90
|
+
else None
|
|
91
|
+
)
|
|
92
|
+
if first_arg:
|
|
93
|
+
first_arg.idl_name = self.ext_attrs.get("key") or "string"
|
|
94
|
+
for attr in ["unique", "span", "key"]:
|
|
95
|
+
if attr in self.ext_attrs:
|
|
96
|
+
del self.ext_attrs[attr]
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def from_idl(
|
|
101
|
+
ast: expr.IdlType,
|
|
102
|
+
builders: ExtAttrsContextBuilders,
|
|
103
|
+
parent_ast_attrs: list[expr.ExtendedAttribute],
|
|
104
|
+
) -> "Type | expr.IdlType":
|
|
105
|
+
ext_attrs = builders.from_idl(lambda b: b.type, ast.ext_attrs)
|
|
106
|
+
parent_attrs = builders.from_idl(lambda b: b.type, parent_ast_attrs)
|
|
107
|
+
builders.property_fixup(ext_attrs, parent_attrs)
|
|
108
|
+
nullable = ext_attrs.get("nullable", False)
|
|
109
|
+
if "nullable" in ext_attrs:
|
|
110
|
+
del ext_attrs["nullable"]
|
|
111
|
+
|
|
112
|
+
if ast.union:
|
|
113
|
+
ast = _flatten_union(ast)
|
|
114
|
+
args = cast(list[expr.IdlType], ast.idl_type)
|
|
115
|
+
arguments = [TypeArg(Type.from_idl(arg, builders, [])) for arg in args]
|
|
116
|
+
if arguments:
|
|
117
|
+
arguments[0].first = True
|
|
118
|
+
return Type(
|
|
119
|
+
idl_name="union",
|
|
120
|
+
arguments=arguments,
|
|
121
|
+
nullable=ast.nullable or nullable,
|
|
122
|
+
generic=True,
|
|
123
|
+
ext_attrs=ext_attrs,
|
|
124
|
+
).__patched_type()
|
|
125
|
+
|
|
126
|
+
if isinstance(ast.idl_type, str):
|
|
127
|
+
return Type(
|
|
128
|
+
idl_name=ast.idl_type,
|
|
129
|
+
nullable=ast.nullable or nullable,
|
|
130
|
+
ext_attrs=ext_attrs,
|
|
131
|
+
).__patched_type()
|
|
132
|
+
|
|
133
|
+
if ast.generic:
|
|
134
|
+
arguments: list[TypeArg] = []
|
|
135
|
+
if isinstance(ast.idl_type, str):
|
|
136
|
+
arguments = [TypeArg(Type(idl_name=ast.idl_type), True)]
|
|
137
|
+
elif isinstance(ast.idl_type, expr.IdlType):
|
|
138
|
+
arguments = [TypeArg(Type.from_idl(ast.idl_type, builders, []), True)]
|
|
139
|
+
else:
|
|
140
|
+
arguments = [
|
|
141
|
+
TypeArg(Type.from_idl(arg, builders, []))
|
|
142
|
+
for arg in cast(list[expr.IdlType], ast.idl_type)
|
|
143
|
+
]
|
|
144
|
+
if arguments:
|
|
145
|
+
arguments[0].first = True
|
|
146
|
+
return Type(
|
|
147
|
+
idl_name=ast.generic,
|
|
148
|
+
arguments=arguments,
|
|
149
|
+
nullable=ast.nullable or nullable,
|
|
150
|
+
generic=True,
|
|
151
|
+
ext_attrs=ext_attrs,
|
|
152
|
+
).__patched_type()
|
|
153
|
+
|
|
154
|
+
inner = ast.idl_type
|
|
155
|
+
if isinstance(inner, expr.IdlType):
|
|
156
|
+
inner.nullable = inner.nullable or ast.nullable or nullable
|
|
157
|
+
inner.ext_attrs = [*ast.ext_attrs, *inner.ext_attrs]
|
|
158
|
+
inner.generic = inner.generic or ast.generic
|
|
159
|
+
return Type.from_idl(inner, builders, parent_ast_attrs)
|
|
160
|
+
|
|
161
|
+
print(type(ast.idl_type), ast.generic != "")
|
|
162
|
+
print(ast)
|
|
163
|
+
return ast
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class Attribute:
|
|
168
|
+
name: str
|
|
169
|
+
type: Type | expr.IdlType
|
|
170
|
+
readonly: bool
|
|
171
|
+
static: bool
|
|
172
|
+
ext_attrs: dict
|
|
173
|
+
|
|
174
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
175
|
+
if isinstance(self.type, Type):
|
|
176
|
+
self.type.populate_references(visitor)
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def from_idl(ast: expr.Attribute, builders: ExtAttrsContextBuilders):
|
|
180
|
+
ext_attrs = builders.from_idl(lambda b: b.attribute, ast.ext_attrs)
|
|
181
|
+
type = Type.from_idl(ast.idl_type, builders, ast.ext_attrs)
|
|
182
|
+
return Attribute(
|
|
183
|
+
name=ast.name,
|
|
184
|
+
type=type,
|
|
185
|
+
readonly=ast.readonly,
|
|
186
|
+
static=ast.special == "static",
|
|
187
|
+
ext_attrs=ext_attrs,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass
|
|
192
|
+
class Constant:
|
|
193
|
+
name: str
|
|
194
|
+
type: Type | expr.IdlType
|
|
195
|
+
value: expr.Expression
|
|
196
|
+
ext_attrs: dict
|
|
197
|
+
|
|
198
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
199
|
+
if isinstance(self.type, Type):
|
|
200
|
+
self.type.populate_references(visitor)
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def from_idl(ast: expr.Const, builders: ExtAttrsContextBuilders):
|
|
204
|
+
ext_attrs = builders.from_idl(lambda b: b.attribute, ast.ext_attrs)
|
|
205
|
+
type = Type.from_idl(ast.idl_type, builders, ast.ext_attrs)
|
|
206
|
+
return Constant(
|
|
207
|
+
name=ast.name,
|
|
208
|
+
type=type,
|
|
209
|
+
value=ast.value,
|
|
210
|
+
ext_attrs=ext_attrs,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class Argument:
|
|
216
|
+
name: str
|
|
217
|
+
type: Type | expr.IdlType
|
|
218
|
+
ext_attrs: dict
|
|
219
|
+
first: bool = False
|
|
220
|
+
|
|
221
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
222
|
+
if isinstance(self.type, Type):
|
|
223
|
+
self.type.populate_references(visitor)
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def from_idl(ast: expr.Argument, builders: ExtAttrsContextBuilders):
|
|
227
|
+
ext_attrs = builders.from_idl(lambda b: b.argument, ast.ext_attrs)
|
|
228
|
+
type = Type.from_idl(ast.idl_type, builders, ast.ext_attrs)
|
|
229
|
+
return Argument(
|
|
230
|
+
name=ast.name,
|
|
231
|
+
type=type,
|
|
232
|
+
ext_attrs=ext_attrs,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass
|
|
237
|
+
class Operation:
|
|
238
|
+
name: str
|
|
239
|
+
type: Type | expr.IdlType | None
|
|
240
|
+
arguments: list[Argument]
|
|
241
|
+
static: bool
|
|
242
|
+
ext_attrs: dict
|
|
243
|
+
|
|
244
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
245
|
+
if isinstance(self.type, Type):
|
|
246
|
+
self.type.populate_references(visitor)
|
|
247
|
+
for arg in self.arguments:
|
|
248
|
+
arg.populate_references(visitor)
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def from_idl(ast: expr.Operation, builders: ExtAttrsContextBuilders):
|
|
252
|
+
ext_attrs = builders.from_idl(lambda b: b.operation, ast.ext_attrs)
|
|
253
|
+
type = (
|
|
254
|
+
Type.from_idl(ast.idl_type, builders, ast.ext_attrs)
|
|
255
|
+
if ast.idl_type
|
|
256
|
+
else None
|
|
257
|
+
)
|
|
258
|
+
arguments = [Argument.from_idl(arg, builders) for arg in ast.arguments]
|
|
259
|
+
if arguments:
|
|
260
|
+
arguments[0].first = True
|
|
261
|
+
return Operation(
|
|
262
|
+
name=ast.name,
|
|
263
|
+
type=type,
|
|
264
|
+
arguments=arguments,
|
|
265
|
+
static=ast.special == "static",
|
|
266
|
+
ext_attrs=ext_attrs,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class MemberContainer:
|
|
272
|
+
constants: list[Constant]
|
|
273
|
+
attributes: list[Attribute]
|
|
274
|
+
operations: list[Operation]
|
|
275
|
+
|
|
276
|
+
@staticmethod
|
|
277
|
+
def from_idl(
|
|
278
|
+
ast: expr.Interface | expr.InterfaceMixin, builders: ExtAttrsContextBuilders
|
|
279
|
+
):
|
|
280
|
+
constants: list[Constant] = []
|
|
281
|
+
attributes: list[Attribute] = []
|
|
282
|
+
operations: list[Operation] = []
|
|
283
|
+
for member in ast.members:
|
|
284
|
+
if isinstance(member, expr.Const):
|
|
285
|
+
constants.append(Constant.from_idl(member, builders))
|
|
286
|
+
continue
|
|
287
|
+
|
|
288
|
+
if isinstance(member, expr.Attribute):
|
|
289
|
+
attributes.append(Attribute.from_idl(member, builders))
|
|
290
|
+
continue
|
|
291
|
+
|
|
292
|
+
if isinstance(member, expr.Operation):
|
|
293
|
+
operations.append(Operation.from_idl(member, builders))
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
print(f"warning: unsupported member type `{getattr(member, 'type')}'")
|
|
297
|
+
|
|
298
|
+
return MemberContainer(
|
|
299
|
+
constants=constants,
|
|
300
|
+
attributes=attributes,
|
|
301
|
+
operations=operations,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def update(existing: dict, incoming: dict):
|
|
306
|
+
for key in incoming:
|
|
307
|
+
val = incoming[key]
|
|
308
|
+
if val or key not in existing:
|
|
309
|
+
existing[key] = val
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@dataclass
|
|
313
|
+
class Interface:
|
|
314
|
+
name: str
|
|
315
|
+
inheritance: str | None
|
|
316
|
+
partial: bool
|
|
317
|
+
constants: list[Constant]
|
|
318
|
+
attributes: list[Attribute]
|
|
319
|
+
operations: list[Operation]
|
|
320
|
+
ext_attrs: dict
|
|
321
|
+
has_constants: bool = field(default=False)
|
|
322
|
+
has_attributes: bool = field(default=False)
|
|
323
|
+
has_operations: bool = field(default=False)
|
|
324
|
+
|
|
325
|
+
def populate_references(self, visitor: TypeVisitor):
|
|
326
|
+
if self.inheritance:
|
|
327
|
+
visitor.on_type_name(self.inheritance)
|
|
328
|
+
for item in self.constants:
|
|
329
|
+
item.populate_references(visitor)
|
|
330
|
+
for item in self.attributes:
|
|
331
|
+
item.populate_references(visitor)
|
|
332
|
+
for item in self.operations:
|
|
333
|
+
item.populate_references(visitor)
|
|
334
|
+
|
|
335
|
+
@staticmethod
|
|
336
|
+
def from_idl(ast: expr.Interface, builders: ExtAttrsContextBuilders):
|
|
337
|
+
members = MemberContainer.from_idl(ast, builders)
|
|
338
|
+
return Interface(
|
|
339
|
+
name=ast.name,
|
|
340
|
+
inheritance=ast.inheritance,
|
|
341
|
+
partial=ast.partial,
|
|
342
|
+
constants=members.constants,
|
|
343
|
+
attributes=members.attributes,
|
|
344
|
+
operations=members.operations,
|
|
345
|
+
ext_attrs=builders.from_idl(lambda b: b.interface, ast.ext_attrs),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@dataclass
|
|
350
|
+
class InterfaceMixin:
|
|
351
|
+
partial: bool
|
|
352
|
+
constants: list[Constant]
|
|
353
|
+
attributes: list[Attribute]
|
|
354
|
+
operations: list[Operation]
|
|
355
|
+
ext_attrs: dict
|
|
356
|
+
|
|
357
|
+
@staticmethod
|
|
358
|
+
def from_idl(ast: expr.InterfaceMixin, builders: ExtAttrsContextBuilders):
|
|
359
|
+
members = MemberContainer.from_idl(ast, builders)
|
|
360
|
+
return InterfaceMixin(
|
|
361
|
+
partial=ast.partial,
|
|
362
|
+
constants=members.constants,
|
|
363
|
+
attributes=members.attributes,
|
|
364
|
+
operations=members.operations,
|
|
365
|
+
ext_attrs=builders.from_idl(lambda b: b.interface, ast.ext_attrs),
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
@dataclass
|
|
370
|
+
class EnumInfo:
|
|
371
|
+
name: str
|
|
372
|
+
NAME: str
|
|
373
|
+
items: list[str]
|
|
374
|
+
ext_attrs: dict
|
|
375
|
+
|
|
376
|
+
@staticmethod
|
|
377
|
+
def from_idl(ast: expr.Enum, builders: ExtAttrsContextBuilders):
|
|
378
|
+
return EnumInfo(
|
|
379
|
+
name=ast.name,
|
|
380
|
+
NAME=ast.name.upper(),
|
|
381
|
+
items=[cast(str, value.value) for value in ast.values],
|
|
382
|
+
ext_attrs=builders.from_idl(lambda b: b.enum, ast.ext_attrs),
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@dataclass
|
|
387
|
+
class MergedDefinitions:
|
|
388
|
+
interfaces: dict[str, Interface] = field(default_factory=dict)
|
|
389
|
+
enum: list[EnumInfo] = field(default_factory=list)
|
|
390
|
+
|
|
391
|
+
def order(self, types: dict[str, TypeReplacement]):
|
|
392
|
+
dependency_tree: dict[str, set[str]] = {}
|
|
393
|
+
modules_or_includes: set[str] = set()
|
|
394
|
+
|
|
395
|
+
known_names = set[str]()
|
|
396
|
+
known_names.update(item.name for item in self.enum)
|
|
397
|
+
known_names.update(self.interfaces.keys())
|
|
398
|
+
|
|
399
|
+
for name, interface in self.interfaces.items():
|
|
400
|
+
visitor = TypeVisitor(known_names, types)
|
|
401
|
+
interface.populate_references(visitor)
|
|
402
|
+
modules_or_includes.update(visitor.modules_or_includes)
|
|
403
|
+
if name in visitor.local_references:
|
|
404
|
+
visitor.local_references.remove(name)
|
|
405
|
+
dependency_tree[name] = visitor.local_references
|
|
406
|
+
|
|
407
|
+
order = list[str]()
|
|
408
|
+
while dependency_tree:
|
|
409
|
+
layer = [
|
|
410
|
+
name
|
|
411
|
+
for name in sorted(dependency_tree.keys())
|
|
412
|
+
if not dependency_tree[name]
|
|
413
|
+
]
|
|
414
|
+
order.extend(layer)
|
|
415
|
+
|
|
416
|
+
if not layer:
|
|
417
|
+
for key in dependency_tree:
|
|
418
|
+
layer.append(key)
|
|
419
|
+
break
|
|
420
|
+
|
|
421
|
+
for key in layer:
|
|
422
|
+
del dependency_tree[key]
|
|
423
|
+
|
|
424
|
+
for key in layer:
|
|
425
|
+
for dependencies in dependency_tree.values():
|
|
426
|
+
if key in dependencies:
|
|
427
|
+
dependencies.remove(key)
|
|
428
|
+
|
|
429
|
+
return ([self.interfaces[key] for key in order], modules_or_includes)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
P_ = TypeVar("P_")
|
|
433
|
+
AST_ = TypeVar("AST_")
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class PartialSetDict[P_, AST_](dict[str, list[P_]]):
|
|
437
|
+
def append(self, name: str, item: AST_, builders: ExtAttrsContextBuilders):
|
|
438
|
+
conv = self.convert(item, builders)
|
|
439
|
+
try:
|
|
440
|
+
self[name].append(conv)
|
|
441
|
+
except KeyError:
|
|
442
|
+
self[name] = [conv]
|
|
443
|
+
|
|
444
|
+
def extend(self, additional: dict[str, list[P_]]):
|
|
445
|
+
for name, partials in additional.items():
|
|
446
|
+
try:
|
|
447
|
+
self[name].extend(partials)
|
|
448
|
+
except KeyError:
|
|
449
|
+
self[name] = [*partials]
|
|
450
|
+
|
|
451
|
+
def convert(self, item: AST_, builders: ExtAttrsContextBuilders) -> P_: ...
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class InterfacePartialSet(PartialSetDict[Interface | str, expr.Interface | str]):
|
|
455
|
+
def convert(self, item: expr.Interface | str, builders: ExtAttrsContextBuilders):
|
|
456
|
+
return item if isinstance(item, str) else Interface.from_idl(item, builders)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class MixinPartialSet(PartialSetDict[InterfaceMixin, expr.InterfaceMixin]):
|
|
460
|
+
def convert(self, item: expr.InterfaceMixin, builders: ExtAttrsContextBuilders):
|
|
461
|
+
return InterfaceMixin.from_idl(item, builders)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
@dataclass
|
|
465
|
+
class IdlSyntaxError:
|
|
466
|
+
path: str
|
|
467
|
+
error: parser.SyntaxErrorInfo
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
@dataclass
|
|
471
|
+
class Definitions:
|
|
472
|
+
interfaces: InterfacePartialSet = field(default_factory=InterfacePartialSet)
|
|
473
|
+
mixins: MixinPartialSet = field(default_factory=MixinPartialSet)
|
|
474
|
+
enums: list[EnumInfo] = field(default_factory=list)
|
|
475
|
+
|
|
476
|
+
@staticmethod
|
|
477
|
+
def from_idl(ast: expr.Definitions, builders: ExtAttrsContextBuilders):
|
|
478
|
+
result = Definitions()
|
|
479
|
+
|
|
480
|
+
for definition in ast.definitions:
|
|
481
|
+
if isinstance(definition, expr.Includes):
|
|
482
|
+
result.interfaces.append(
|
|
483
|
+
definition.target, definition.includes, builders
|
|
484
|
+
)
|
|
485
|
+
continue
|
|
486
|
+
if isinstance(definition, expr.Interface):
|
|
487
|
+
result.interfaces.append(definition.name, definition, builders)
|
|
488
|
+
continue
|
|
489
|
+
if isinstance(definition, expr.InterfaceMixin):
|
|
490
|
+
result.mixins.append(definition.name, definition, builders)
|
|
491
|
+
continue
|
|
492
|
+
if isinstance(definition, expr.Enum):
|
|
493
|
+
result.enums.append(EnumInfo.from_idl(definition, builders))
|
|
494
|
+
continue
|
|
495
|
+
|
|
496
|
+
return result
|
|
497
|
+
|
|
498
|
+
@staticmethod
|
|
499
|
+
def merge(*partials: "Definitions"):
|
|
500
|
+
intermediate = Definitions()
|
|
501
|
+
for partial in partials:
|
|
502
|
+
intermediate.interfaces.extend(partial.interfaces)
|
|
503
|
+
intermediate.mixins.extend(partial.mixins)
|
|
504
|
+
intermediate.enums.extend(partial.enums)
|
|
505
|
+
|
|
506
|
+
interfaces: dict[str, Interface] = {}
|
|
507
|
+
mixins: dict[str, Interface] = {}
|
|
508
|
+
|
|
509
|
+
for name, mixin_set in intermediate.mixins.items():
|
|
510
|
+
mixin = Interface(
|
|
511
|
+
name=name,
|
|
512
|
+
inheritance=None,
|
|
513
|
+
partial=True,
|
|
514
|
+
constants=[],
|
|
515
|
+
attributes=[],
|
|
516
|
+
operations=[],
|
|
517
|
+
ext_attrs={},
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
for partial in mixin_set:
|
|
521
|
+
mixin.constants.extend(partial.constants)
|
|
522
|
+
mixin.attributes.extend(partial.attributes)
|
|
523
|
+
mixin.operations.extend(partial.operations)
|
|
524
|
+
update(mixin.ext_attrs, partial.ext_attrs)
|
|
525
|
+
|
|
526
|
+
mixins[name] = mixin
|
|
527
|
+
|
|
528
|
+
for name, interface_set in intermediate.interfaces.items():
|
|
529
|
+
target = Interface(
|
|
530
|
+
name=name,
|
|
531
|
+
inheritance=None,
|
|
532
|
+
partial=False,
|
|
533
|
+
constants=[],
|
|
534
|
+
attributes=[],
|
|
535
|
+
operations=[],
|
|
536
|
+
ext_attrs={},
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
for partial in interface_set:
|
|
540
|
+
if isinstance(partial, str):
|
|
541
|
+
try:
|
|
542
|
+
partial = mixins[partial]
|
|
543
|
+
except KeyError:
|
|
544
|
+
message = f"{name} cannot find {partial} to include"
|
|
545
|
+
raise RuntimeError(message)
|
|
546
|
+
|
|
547
|
+
target.inheritance = target.inheritance or partial.inheritance
|
|
548
|
+
target.constants.extend(partial.constants)
|
|
549
|
+
target.attributes.extend(partial.attributes)
|
|
550
|
+
target.operations.extend(partial.operations)
|
|
551
|
+
update(target.ext_attrs, partial.ext_attrs)
|
|
552
|
+
|
|
553
|
+
target.has_constants = len(target.constants) > 0
|
|
554
|
+
target.has_attributes = len(target.attributes) > 0
|
|
555
|
+
target.has_operations = len(target.operations) > 0
|
|
556
|
+
interfaces[name] = target
|
|
557
|
+
|
|
558
|
+
return MergedDefinitions(
|
|
559
|
+
interfaces=interfaces,
|
|
560
|
+
enum=intermediate.enums,
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
@staticmethod
|
|
564
|
+
def parse_and_merge(
|
|
565
|
+
names: list[str],
|
|
566
|
+
builders: ExtAttrsContextBuilders,
|
|
567
|
+
):
|
|
568
|
+
partials: list[Definitions] = []
|
|
569
|
+
syntax_errors: list[IdlSyntaxError] = []
|
|
570
|
+
|
|
571
|
+
for name in names:
|
|
572
|
+
path = Path(name)
|
|
573
|
+
text = path.read_text(encoding="UTF-8")
|
|
574
|
+
errors = validate(text)
|
|
575
|
+
for error in errors:
|
|
576
|
+
syntax_errors.append(IdlSyntaxError(path=name, error=error))
|
|
577
|
+
if errors:
|
|
578
|
+
continue
|
|
579
|
+
|
|
580
|
+
ast = cast(expr.Definitions, raw_parse(text))
|
|
581
|
+
definitions = Definitions.from_idl(ast, builders)
|
|
582
|
+
partials.append(definitions)
|
|
583
|
+
|
|
584
|
+
if syntax_errors:
|
|
585
|
+
return syntax_errors
|
|
586
|
+
return Definitions.merge(*partials)
|