99PRAGMA_RE = re .compile (r"^(?P<indentation>\s*)# godot_extension:\s+(?P<pragma>.*)$" )
1010CLASS_RE = re .compile (r"^cdef class (?P<class_name>\w+)" )
1111METHOD_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
1515INJECT_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:
162168def 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