Skip to content

Commit 739ab86

Browse files
committed
Update scripts/gdextension_cython_preprocessor.py to suppport is_(virtual|abstract|exposed) flags in class config, and better support type parsing
1 parent 84bd5bb commit 739ab86

File tree

1 file changed

+58
-41
lines changed

1 file changed

+58
-41
lines changed

scripts/gdextension_cython_preprocessor.py

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
PRAGMA_RE = re.compile(r"^(?P<indentation>\s*)# godot_extension:\s+(?P<pragma>.*)$")
1010
CLASS_RE = re.compile(r"^cdef class (?P<class_name>\w+)")
1111
METHOD_RE = re.compile(
12-
r"^cdef\s+(inline\s+)?(?P<return_type>\w+\s*\*?)\s+(?P<method_name>\w+)\((?P<param>.*)\):"
12+
r"^cdef\s+(inline\s+)?(?P<return_type>\w+((\s*\*\s*)|(\s+)))(?P<method_name>\w+)\((?P<parameters>.*)\):"
1313
)
1414

1515
INJECT_CODE_PRAGMA = "generate_code()"
@@ -32,6 +32,9 @@ class ClassDef:
3232
class_def_at_line: int
3333
class_name: str
3434
parent_class_name: str
35+
is_virtual: bool
36+
is_abstract: bool
37+
is_exposed: bool
3538
methods: List[MethodDef]
3639
inject_code_at_line: int
3740
register_class_hook: str | None
@@ -97,6 +100,9 @@ def __godot_extension_register_class():
97100
&{spec.class_name}.__godot_extension_create_instance,
98101
&{spec.class_name}.__godot_extension_free_instance,
99102
&{spec.class_name}.__godot_extension_get_virtual,
103+
{"True" if spec.is_virtual else "False"},
104+
{"True" if spec.is_abstract else "False"},
105+
{"True" if spec.is_exposed else "False"},
100106
)
101107
"""
102108
for method in spec.methods:
@@ -162,10 +168,10 @@ def generate_injected_code(spec: ClassDef) -> str:
162168
def handle_pointer_type(raw_type: str) -> str:
163169
# Type is not a real Godot object, but we still use `gd_object_t`
164170
# to represent the fact it is a pointer
165-
# if raw_type.endswith("*"):
166-
# return "gd_object_t"
167171
try:
168-
pointed_type, _ = raw_type.split()
172+
pointed_type, expect_star = raw_type.split()
173+
if not expect_star != "*":
174+
raise RuntimeError(f"bad type `{raw_type}`, expected `<type>` or `<type> *`")
169175
return f"{pointed_type}*"
170176
except ValueError:
171177
return raw_type.strip()
@@ -200,7 +206,7 @@ def extract_classes_from_code(code_lines: List[str]) -> List[ClassDef]:
200206

201207
else:
202208

203-
def _collect_method_signature():
209+
def _collect_method_signature() -> MethodDef:
204210
try:
205211
_, line = next(code_lines)
206212
is_staticmethod = line.strip() == "@staticmethod"
@@ -240,7 +246,32 @@ def _collect_method_signature():
240246
f"expected method signature `cdef [inline] (gd_xxx_t|void) foo({'' if is_staticmethod else 'self, '}gd_yyy_t bar, ...)`"
241247
)
242248

243-
return match, is_staticmethod
249+
return_type = handle_pointer_type(match.group("return_type"))
250+
251+
params = {}
252+
if match.group("parameters").strip():
253+
for i, raw_param in enumerate(match.group("parameters").split(",")):
254+
if i == 0 and not is_staticmethod:
255+
if raw_param != "self":
256+
raise RuntimeError(
257+
"expected first paramater for non static method to be `self`"
258+
)
259+
continue
260+
261+
try:
262+
param_type, param_name = raw_param.split()
263+
except ValueError:
264+
raise RuntimeError(f"bad parameter {raw_param!r}")
265+
params[param_name] = handle_pointer_type(param_type)
266+
267+
return MethodDef(
268+
is_staticmethod=is_staticmethod,
269+
method_name=match.group("method_name"),
270+
return_type=return_type,
271+
parameters=params,
272+
is_const=False,
273+
is_virtual=False,
274+
)
244275

245276
def _register_class_hook():
246277
if current_class is None:
@@ -253,17 +284,17 @@ def _register_class_hook():
253284
f"`# godot_extension: register_class_hook` can only be set once per `# godot_extension: class(...)` pragma"
254285
)
255286

256-
match, is_staticmethod = _collect_method_signature()
257-
if not is_staticmethod:
287+
signature = _collect_method_signature()
288+
if not signature.is_staticmethod:
258289
raise RuntimeError(
259290
f"`# godot_extension: register_class_hook` only allow accepts static method"
260291
)
261-
if match.group("param") or match.group("return_type") != "void":
292+
if signature.parameters or signature.return_type != "void":
262293
raise RuntimeError(
263294
f"`# godot_extension: register_class_hook` method must have no parameter and return void"
264295
)
265296

266-
current_class.register_class_hook = match.group("method_name")
297+
current_class.register_class_hook = signature.method_name
267298

268299
def _unregister_class_hook():
269300
if current_class is None:
@@ -276,17 +307,17 @@ def _unregister_class_hook():
276307
f"`# godot_extension: unregister_class_hook` can only be set once per `# godot_extension: class(...)` pragma"
277308
)
278309

279-
match, is_staticmethod = _collect_method_signature()
280-
if not is_staticmethod:
310+
signature = _collect_method_signature()
311+
if not signature.is_staticmethod:
281312
raise RuntimeError(
282313
f"`# godot_extension: unregister_class_hook` only allow accepts static method"
283314
)
284-
if match.group("param") or match.group("return_type") != "void":
315+
if signature.parameters or signature.return_type != "void":
285316
raise RuntimeError(
286317
f"`# godot_extension: unregister_class_hook` method must have no parameter and return void"
287318
)
288319

289-
current_class.unregister_class_hook = match.group("method_name")
320+
current_class.unregister_class_hook = signature.method_name
290321

291322
def _method(const: bool = False, virtual: bool = False) -> MethodDef:
292323
if current_class is None:
@@ -302,35 +333,18 @@ def _method(const: bool = False, virtual: bool = False) -> MethodDef:
302333
raise RuntimeError("`virtual` parameter must be a boolean")
303334
is_virtual = virtual
304335

305-
match, is_staticmethod = _collect_method_signature()
336+
signature = _collect_method_signature()
337+
signature.is_const = is_const
338+
signature.is_virtual = is_virtual
306339

307-
params = {}
308-
for i, raw_param in enumerate(match.group("param").split(",")):
309-
if i == 0 and not is_staticmethod:
310-
if raw_param != "self":
311-
raise RuntimeError(
312-
"expected first paramater for non static method to be `self`"
313-
)
314-
continue
315-
316-
try:
317-
param_type, param_name = raw_param.split()
318-
except ValueError:
319-
raise RuntimeError(f"bad parameter {raw_param!r}")
320-
params[param_name] = handle_pointer_type(param_type)
321-
322-
current_class.methods.append(
323-
MethodDef(
324-
method_name=match.group("method_name"),
325-
is_staticmethod=is_staticmethod,
326-
is_const=is_const,
327-
is_virtual=is_virtual,
328-
return_type=handle_pointer_type(match.group("return_type")),
329-
parameters=params,
330-
)
331-
)
340+
current_class.methods.append(signature)
332341

333-
def _class(parent: str) -> ClassDef:
342+
def _class(
343+
parent: str,
344+
is_virtual: bool = False,
345+
is_abstract: bool = False,
346+
is_exposed: bool = True,
347+
) -> ClassDef:
334348
nonlocal current_class
335349
nonlocal classes
336350

@@ -352,6 +366,9 @@ def _class(parent: str) -> ClassDef:
352366
class_def_at_line=class_def_at_line,
353367
class_name=match.group("class_name"),
354368
parent_class_name=parent,
369+
is_virtual=is_virtual,
370+
is_abstract=is_abstract,
371+
is_exposed=is_exposed,
355372
methods=[],
356373
inject_code_at_line=-1,
357374
register_class_hook=None,

0 commit comments

Comments
 (0)