4040# recognize a function definition in rst
4141is_func = re .compile (r"\.\.( )+(c:)?function( )*::" )
4242# rename types to avoid python -- c name collisions
43- rename_types = [(re .compile (r"\bfmpz\b" ),"fmpz_struct" ),(re .compile (r"\bfmpq\b" ), "fmpq_struct" )]
43+ rename_types = [
44+ (re .compile (r"\bfmpz\b" ),"fmpz_struct" ),
45+ (re .compile (r"\bfmpq\b" ), "fmpq_struct" ),
46+ (re .compile (r"\bin\b" ), "in_" ),
47+ (re .compile (r"\blambda\b" ), "lambda_" ),
48+ ]
4449# comment out functions which use these types
4550comment_types = re .compile (r"(\bFILE\b)|(\bmpz_t\b)|(\bmpq_t\b)" )
4651comment_set = set (["FILE" , "mpz_t" , "mpq_t" ])
47- c_types = set (["char" , "short" , "long" , "int" , "float" , "double" ])
52+ c_types = set (["void" , " char" , "short" , "long" , "int" , "float" , "double" ])
4853type_modifers = re .compile (r"\*|(\bconst\b)|(\bunsigned\b)|(\bsigned\b)" )
4954import_dict = {}
5055
@@ -57,8 +62,14 @@ def get_cython_struct_types(file):
5762 l = line .strip ()
5863 if l [:8 ] == "ctypedef" :
5964 if l [- 1 ] == ']' :
65+ # ctypedef foo foo_t[0]
6066 l = l [:l .rfind ('[' )]
67+ elif '(' in l :
68+ # ctypedef int (*foo_func)(...)
69+ l = l [l .index ('(' ):].lstrip ('(*' )
70+ l = l [:l .index (')' )]
6171 else :
72+ # ctypedef foo:
6273 l = l .strip (':' )
6374 ret .append (l .split ()[- 1 ])
6475 return ret
@@ -67,25 +78,30 @@ def fill_import_dict(pyflintlibdir):
6778 """
6879 Get a map from cython structs to the pxd that defines them
6980 """
70- with os .scandir (pyflintlibdir ) as entry :
81+ import_dict ['fmpq_struct' ] = 'types.fmpq'
82+
83+ with os .scandir (pyflintlibdir + '/types' ) as entry :
7184 for f in entry :
7285 if fnmatch .fnmatch (f .name , "*.pxd" ):
7386 with open (f .path ) as pxd :
7487 for t in get_cython_struct_types (pxd ):
75- import_dict [t ] = f .name .split ('.' )[0 ]
88+ import_dict [t ] = 'types.' + f .name .split ('.' )[0 ]
7689
7790def undecorate (str ):
7891 """
7992 remove variable name, const, ``*``, etc. to just get types
8093 """
8194 ret = str .strip ()
82- ret = ret [:ret .rfind (' ' )]
83- ret = re .sub (type_modifers , '' , ret )
84- return ret .strip ()
95+ if ' ' in ret :
96+ ret = ret [:ret .rfind (' ' )]
97+ ret = re .sub (type_modifers , '' , ret ).strip ()
98+ return ret
8599
86100def get_parameter_types (str ):
87101 params = str [str .find ("(" ) + 1 : str .rfind (")" )].split ("," )
88- return [undecorate (s ) for s in params ]
102+ ret_type = str .split ('(' )[0 ].rsplit (' ' , 1 )[0 ]
103+ params .append (ret_type )
104+ return [undecorate (s ) for s in params if s ]
89105
90106def clean_types (function ):
91107 ret = function .strip ()
@@ -98,8 +114,15 @@ def get_functions(file):
98114 Get a list of functions from an rst file
99115 """
100116 ret = []
117+ macros = []
101118 in_list = False
102119 for line in file :
120+ # Keep track of the macros
121+ # We want to give them types in cython...
122+ if line .startswith ('.. macro' ):
123+ macros .append (line .strip ())
124+ continue
125+
103126 m = is_func .match (line )
104127 if m :
105128 ret .append ( clean_types (line [m .end ():]))
@@ -110,7 +133,7 @@ def get_functions(file):
110133 in_list = False
111134 else :
112135 ret .append (clean_types (line ))
113- return ret
136+ return ret , macros
114137
115138def get_all_types (function_list ):
116139 ret = set ()
@@ -119,6 +142,11 @@ def get_all_types(function_list):
119142 ret .add (t )
120143 return ret
121144
145+
146+ def has_types (line , types ):
147+ return any (t in types for t in get_parameter_types (line ))
148+
149+
122150def gen_imports (function_list ):
123151 """
124152 Generate import statements for known functions.
@@ -132,10 +160,12 @@ def gen_imports(function_list):
132160 imports [import_dict [t ]].append (t )
133161 else :
134162 ret .add (t )
135- for k ,v in imports .items ():
136- types = ", " .join (v )
163+ for k ,v in sorted ( imports .items () ):
164+ types = ", " .join (sorted ( v ) )
137165 print ("from flint.flintlib." + k + " cimport " + types )
138- return ret
166+ return sorted (ret )
167+
168+
139169
140170def generate_pxd_file (h_name , opts ):
141171 fill_import_dict (opts .flint_lib_dir )
@@ -146,14 +176,18 @@ def generate_pxd_file(h_name, opts):
146176 docdir = opts .flint_doc_dir
147177 name = name [6 :]
148178 with open (os .path .join (docdir , name + ".rst" )) as f :
149- l = get_functions (f )
150- s = gen_imports (l )
179+ l , macros = get_functions (f )
180+ unknown_types = gen_imports (l )
181+ print ()
182+ for t in unknown_types :
183+ print ("# unknown type " + t )
151184 print ()
152- print ("\n # unimported types " , s - comment_set )
185+ for m in macros :
186+ print ("# " + m )
153187 print ()
154188 print (r'cdef extern from "' + h_name + r'.h":' )
155189 for f in l :
156- if comment_types . search ( f ):
190+ if has_types ( f , unknown_types ):
157191 print (" # " + f )
158192 else :
159193 print (" " + f )
0 commit comments