Skip to content

Commit 5ca809c

Browse files
authored
Merge pull request #28 from defeo/autogen_decl
Autogeneration of pari declarations
2 parents 5dddb3d + 298bc5c commit 5ca809c

File tree

8 files changed

+102
-43
lines changed

8 files changed

+102
-43
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ cypari2/auto_gen.pxi.tmp
44
cypari2/auto_gen.pxi
55
cypari2/auto_instance.pxi.tmp
66
cypari2/auto_instance.pxi
7+
cypari2/auto_paridecl.pxd.tmp
8+
cypari2/auto_paridecl.pxd
79
cypari2/closure.c
810
cypari2/convert.c
911
cypari2/gen.c

autogen/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ def rebuild(force=False):
1212
pari_module_path = 'cypari2'
1313
src_files = [join(pari_share(), b'pari.desc')] + \
1414
glob.glob(join('autogen', '*.py'))
15-
gen_files = [join(pari_module_path, 'auto_gen.pxi')]
15+
gen_files = [join(pari_module_path, 'auto_paridecl.pxd'),
16+
join(pari_module_path, 'auto_gen.pxi')]
1617

17-
if all(exists(f) for f in gen_files):
18+
if not force and all(exists(f) for f in gen_files):
1819
src_mtime = max(getmtime(f) for f in src_files)
1920
gen_mtime = min(getmtime(f) for f in gen_files)
2021

21-
if gen_mtime > src_mtime and not force:
22+
if gen_mtime > src_mtime:
2223
return
2324

2425
G = PariFunctionGenerator()

autogen/args.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ def _typerepr(self):
7171
"""
7272
return "(generic)"
7373

74+
def ctype(self):
75+
"""
76+
The corresponding C type. This is used for auto-generating
77+
the declarations of the C function. In some cases, this is also
78+
used for passing the argument from Python to Cython.
79+
"""
80+
raise NotImplementedError
81+
7482
def always_default(self):
7583
"""
7684
If this returns not ``None``, it is a value which is always
@@ -156,9 +164,6 @@ class PariArgumentClass(PariArgument):
156164
157165
The C/Cython type is given by ``self.ctype()``.
158166
"""
159-
def ctype(self):
160-
raise NotImplementedError
161-
162167
def prototype_code(self):
163168
"""
164169
Return code to appear in the prototype of the Cython wrapper.
@@ -179,11 +184,15 @@ def __init__(self):
179184
PariArgument.__init__(self, iter(["self"]), None, 0)
180185
def _typerepr(self):
181186
return "Pari"
187+
def ctype(self):
188+
return "GEN"
182189

183190

184191
class PariArgumentGEN(PariArgumentObject):
185192
def _typerepr(self):
186193
return "GEN"
194+
def ctype(self):
195+
return "GEN"
187196
def convert_code(self):
188197
if self.index == 0:
189198
# "self" is always of type Gen, we skip the conversion
@@ -219,6 +228,8 @@ def call_code(self):
219228
class PariArgumentString(PariArgumentObject):
220229
def _typerepr(self):
221230
return "str"
231+
def ctype(self):
232+
return "char *"
222233
def convert_code(self):
223234
if self.default is None:
224235
s = " {name} = to_bytes({name})\n"
@@ -237,6 +248,8 @@ def call_code(self):
237248
class PariArgumentVariable(PariArgumentObject):
238249
def _typerepr(self):
239250
return "var"
251+
def ctype(self):
252+
return "long"
240253
def default_default(self):
241254
return "-1"
242255
def convert_code(self):

autogen/generator.py

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#*****************************************************************************
66
# Copyright (C) 2015 Jeroen Demeyer <[email protected]>
7+
# 2017 Vincent Delecroix <[email protected]>
78
#
89
# This program is free software: you can redistribute it and/or modify
910
# it under the terms of the GNU General Public License as published by
@@ -16,7 +17,7 @@
1617
import os, re, sys
1718

1819
from .args import PariArgumentGEN, PariInstanceArgument
19-
from .parser import (read_pari_desc, read_decl, parse_prototype)
20+
from .parser import read_pari_desc, parse_prototype
2021
from .doc import get_rest_doc
2122

2223

@@ -42,12 +43,52 @@
4243
"""
4344
'''.format(__file__)
4445

46+
decl_banner='''# This file is auto-generated by {}
47+
48+
from .types cimport *
49+
50+
cdef extern from *:
51+
'''.format(__file__)
52+
4553

4654
function_re = re.compile(r"^[A-Za-z][A-Za-z0-9_]*$")
4755
function_blacklist = {"O", # O(p^e) needs special parser support
4856
"alias", # Not needed and difficult documentation
4957
"listcreate", # "redundant and obsolete" according to PARI
5058
"allocatemem", # Better hand-written support in Pari class
59+
"global", # Invalid in Python (and obsolete)
60+
"inline", # Total confusion
61+
"uninline", # idem
62+
"local", # idem
63+
"my", # idem
64+
"plot", # Graphical function undeclared in pari public headers
65+
"plotbox", # idem
66+
"plotclip", # idem
67+
"plotcursor", # idem
68+
"plotcolor", # idem
69+
"plotcopy", # idem
70+
"plotdraw", # idem
71+
"plothsizes", # idem
72+
"plotinit", # idem
73+
"plotkill", # idem
74+
"plotlines", # idem
75+
"plotlinetype", # idem
76+
"plotmove", # idem
77+
"plotpoints", # idem
78+
"plotpointsize", # idem
79+
"plotpointtype", # idem
80+
"plotrbox", # idem
81+
"plotrecth", # idem
82+
"plotrecthraw", # idem
83+
"plotrline", # idem
84+
"plotrmove", # idem
85+
"plotrpoint", # idem
86+
"plotscale", # idem
87+
"plotstring", # idem
88+
"ploth", # idem
89+
"plothraw", # idem
90+
"psdraw", # idem
91+
"psplothraw", # idem
5192
}
5293

5394
class PariFunctionGenerator(object):
@@ -59,9 +100,9 @@ class PariFunctionGenerator(object):
59100
:class:`Pari`.
60101
"""
61102
def __init__(self):
62-
self.declared = read_decl()
63103
self.gen_filename = os.path.join('cypari2', 'auto_gen.pxi')
64104
self.instance_filename = os.path.join('cypari2', 'auto_instance.pxi')
105+
self.decl_filename = os.path.join('cypari2', 'auto_paridecl.pxd')
65106

66107
def can_handle_function(self, function, cname="", **kwds):
67108
"""
@@ -75,8 +116,6 @@ def can_handle_function(self, function, cname="", **kwds):
75116
True
76117
>>> G.can_handle_function("_bnfinit", "bnfinit0", **{"class":"basic"})
77118
False
78-
>>> G.can_handle_function("bnfinit", "BNFINIT0", **{"class":"basic"})
79-
False
80119
>>> G.can_handle_function("bnfinit", "bnfinit0", **{"class":"hard"})
81120
False
82121
"""
@@ -86,9 +125,6 @@ def can_handle_function(self, function, cname="", **kwds):
86125
if not function_re.match(function):
87126
# Not a legal function name, like "!_"
88127
return False
89-
if cname not in self.declared:
90-
# PARI function not in paridecl.pxd or declinl.pxi
91-
return False
92128
cls = kwds.get("class", "unknown")
93129
sec = kwds.get("section", "unknown")
94130
if cls not in ("basic", "highlevel"):
@@ -100,7 +136,7 @@ def can_handle_function(self, function, cname="", **kwds):
100136
return False
101137
return True
102138

103-
def handle_pari_function(self, function, cname="", prototype="", help="", obsolete=None, **kwds):
139+
def handle_pari_function(self, function, cname, prototype="", help="", obsolete=None, **kwds):
104140
r"""
105141
Handle one PARI function: decide whether or not to add the
106142
function, in which file (as method of :class:`Gen` or
@@ -114,10 +150,12 @@ def handle_pari_function(self, function, cname="", prototype="", help="", obsole
114150
>>> G = PariFunctionGenerator()
115151
>>> G.gen_file = sys.stdout
116152
>>> G.instance_file = sys.stdout
153+
>>> G.decl_file = sys.stdout
117154
>>> G.handle_pari_function("bnfinit",
118155
... cname="bnfinit0", prototype="GD0,L,DGp",
119156
... help=r"bnfinit(P,{flag=0},{tech=[]}): compute...",
120157
... **{"class":"basic", "section":"number_fields"})
158+
GEN bnfinit0(GEN, long, GEN, long)
121159
def bnfinit(P, long flag=0, tech=None, long precision=0):
122160
...
123161
cdef GEN _P = P.g
@@ -135,6 +173,7 @@ def bnfinit(P, long flag=0, tech=None, long precision=0):
135173
... cname="ellmodulareqn", prototype="LDnDn",
136174
... help=r"ellmodulareqn(N,{x},{y}): return...",
137175
... **{"class":"basic", "section":"elliptic_curves"})
176+
GEN ellmodulareqn(long, long, long)
138177
def ellmodulareqn(self, long N, x=None, y=None):
139178
...
140179
cdef long _x = -1
@@ -152,6 +191,7 @@ def ellmodulareqn(self, long N, x=None, y=None):
152191
... help=r"setrand(n): reset the seed...",
153192
... doc=r"reseeds the random number generator...",
154193
... **{"class":"basic", "section":"programming/specific"})
194+
void setrand(GEN)
155195
def setrand(n):
156196
r'''
157197
Reseeds the random number generator using the seed :math:`n`. No value is
@@ -171,6 +211,7 @@ def setrand(n):
171211
... help="bernvec(x): this routine is obsolete, use bernfrac repeatedly.",
172212
... obsolete="2007-03-30",
173213
... **{"class":"basic", "section":"transcendental"})
214+
GEN bernvec(long)
174215
def bernvec(self, long x):
175216
r'''
176217
This routine is obsolete, kept for backward compatibility only.
@@ -189,6 +230,8 @@ def bernvec(self, long x):
189230

190231
doc = get_rest_doc(function)
191232

233+
self.write_declaration(cname, args, ret, self.decl_file)
234+
192235
if len(args) > 0 and isinstance(args[0], PariArgumentGEN):
193236
# If the first argument is a GEN, write a method of the
194237
# Gen class.
@@ -201,6 +244,23 @@ def bernvec(self, long x):
201244
self.write_method(function, cname, args, ret, args[1:],
202245
self.instance_file, doc, obsolete)
203246

247+
def write_declaration(self, cname, args, ret, file):
248+
"""
249+
Write a .pxd declaration of a PARI library function.
250+
251+
INPUT:
252+
253+
- ``cname`` -- name of the PARI C library call
254+
255+
- ``args``, ``ret`` -- output from ``parse_prototype``
256+
257+
- ``file`` -- a file object where the declaration should be
258+
written to
259+
"""
260+
args = ", ".join(a.ctype() for a in args)
261+
s = ' {ret} {function}({args})'.format(ret=ret.ctype(), function=cname, args=args)
262+
print(s, file=file)
263+
204264
def write_method(self, function, cname, args, ret, cargs, file, doc, obsolete):
205265
"""
206266
Write Cython code with a method to call one PARI function.
@@ -264,6 +324,8 @@ def __call__(self):
264324
self.gen_file.write(gen_banner)
265325
self.instance_file = open(self.instance_filename + '.tmp', 'w')
266326
self.instance_file.write(instance_banner)
327+
self.decl_file = open(self.decl_filename + '.tmp', 'w')
328+
self.decl_file.write(decl_banner)
267329

268330
for v in D:
269331
if not self.can_handle_function(**v):
@@ -276,7 +338,9 @@ def __call__(self):
276338

277339
self.gen_file.close()
278340
self.instance_file.close()
341+
self.decl_file.close()
279342

280343
# All done? Let's commit.
281344
os.rename(self.gen_filename + '.tmp', self.gen_filename)
282345
os.rename(self.instance_filename + '.tmp', self.instance_filename)
346+
os.rename(self.decl_filename + '.tmp', self.decl_filename)

autogen/parser.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from .ret import pari_ret_types
2121
from .paths import pari_share
2222

23-
2423
paren_re = re.compile(r"[(](.*)[)]")
2524
argname_re = re.compile(r"[ {]*([A-Za-z_][A-Za-z0-9_]*)")
2625

@@ -71,31 +70,6 @@ def read_pari_desc():
7170

7271
return functions
7372

74-
75-
decl_re = re.compile(" ([A-Za-z][A-Za-z0-9_]*)[(]")
76-
77-
def read_decl():
78-
"""
79-
Read the files ``paridecl.pxd`` and ``declinl.pxi`` and return a set
80-
of all declared PARI library functions.
81-
82-
We do a simple regexp search, so there might be false positives.
83-
The main use is to skip undeclared functions.
84-
85-
EXAMPLES::
86-
87-
>>> from autogen.parser import read_decl
88-
>>> sorted(read_decl())
89-
['ABC_to_bnr', ..., 'zx_to_zv']
90-
"""
91-
s = set()
92-
with open(os.path.join("cypari2", "paridecl.pxd")) as f:
93-
s.update(decl_re.findall(f.read()))
94-
with open(os.path.join("cypari2", "declinl.pxi")) as f:
95-
s.update(decl_re.findall(f.read()))
96-
return s
97-
98-
9973
def parse_prototype(proto, help, initial_args=[]):
10074
"""
10175
Parse arguments and return type of a PARI function.

cypari2/gen.pyx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT
7171
from cysignals.memory cimport sig_free, check_malloc
7272
from cysignals.signals cimport sig_check, sig_on, sig_off, sig_block, sig_unblock
7373

74-
from .paridecl cimport *
74+
from .types cimport *
7575
from .string_utils cimport to_string, to_bytes
7676
from .paripriv cimport *
7777
from .convert cimport (integer_to_gen, gen_to_integer,
@@ -81,6 +81,9 @@ from .pari_instance cimport (prec_bits_to_words, prec_words_to_bits,
8181
from .stack cimport new_gen, new_gen_noclear, clear_stack
8282
from .closure cimport objtoclosure
8383

84+
from .paridecl cimport *
85+
from .auto_paridecl cimport *
86+
8487
include 'auto_gen.pxi'
8588

8689
@cython.final
@@ -3426,7 +3429,7 @@ cdef class Gen(Gen_auto):
34263429
<... 'int'>
34273430
"""
34283431
sig_on()
3429-
cdef GEN g = anell(self.g, n)
3432+
cdef GEN g = ellan(self.g, n)
34303433
if python_ints:
34313434
v = [gtolong(gel(g, i+1)) for i in range(glength(g))]
34323435
clear_stack()

cypari2/pari_instance.pyx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ from cysignals.signals cimport sig_check, sig_on, sig_off
259259

260260
from .string_utils cimport to_string, to_bytes
261261
from .paridecl cimport *
262+
from .auto_paridecl cimport *
262263
from .paripriv cimport *
263264
from .gen cimport Gen, objtogen
264265
from .stack cimport new_gen, new_gen_noclear, clear_stack
@@ -1341,7 +1342,7 @@ cdef class Pari(Pari_auto):
13411342
"""
13421343
if x is None:
13431344
sig_on()
1344-
return new_gen(listcreate())
1345+
return new_gen(mklist())
13451346
cdef Gen t0 = objtogen(x)
13461347
sig_on()
13471348
return new_gen(gtolist(t0.g))

cypari2/paridecl.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,6 +2987,7 @@ cdef extern from *: # PARI headers already included by types.pxd
29872987
GEN listput0(GEN list, GEN object, long index)
29882988
void listsort(GEN list, long flag)
29892989
GEN matsize(GEN x)
2990+
GEN mklist()
29902991
GEN mklistcopy(GEN x)
29912992
GEN normalize(GEN x)
29922993
GEN normalizepol(GEN x)

0 commit comments

Comments
 (0)