Skip to content

Commit

Permalink
Merge pull request #28 from defeo/autogen_decl
Browse files Browse the repository at this point in the history
Autogeneration of pari declarations
  • Loading branch information
videlec authored Aug 21, 2017
2 parents 5dddb3d + 298bc5c commit 5ca809c
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 43 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ cypari2/auto_gen.pxi.tmp
cypari2/auto_gen.pxi
cypari2/auto_instance.pxi.tmp
cypari2/auto_instance.pxi
cypari2/auto_paridecl.pxd.tmp
cypari2/auto_paridecl.pxd
cypari2/closure.c
cypari2/convert.c
cypari2/gen.c
Expand Down
7 changes: 4 additions & 3 deletions autogen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ def rebuild(force=False):
pari_module_path = 'cypari2'
src_files = [join(pari_share(), b'pari.desc')] + \
glob.glob(join('autogen', '*.py'))
gen_files = [join(pari_module_path, 'auto_gen.pxi')]
gen_files = [join(pari_module_path, 'auto_paridecl.pxd'),
join(pari_module_path, 'auto_gen.pxi')]

if all(exists(f) for f in gen_files):
if not force and all(exists(f) for f in gen_files):
src_mtime = max(getmtime(f) for f in src_files)
gen_mtime = min(getmtime(f) for f in gen_files)

if gen_mtime > src_mtime and not force:
if gen_mtime > src_mtime:
return

G = PariFunctionGenerator()
Expand Down
19 changes: 16 additions & 3 deletions autogen/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ def _typerepr(self):
"""
return "(generic)"

def ctype(self):
"""
The corresponding C type. This is used for auto-generating
the declarations of the C function. In some cases, this is also
used for passing the argument from Python to Cython.
"""
raise NotImplementedError

def always_default(self):
"""
If this returns not ``None``, it is a value which is always
Expand Down Expand Up @@ -156,9 +164,6 @@ class PariArgumentClass(PariArgument):
The C/Cython type is given by ``self.ctype()``.
"""
def ctype(self):
raise NotImplementedError

def prototype_code(self):
"""
Return code to appear in the prototype of the Cython wrapper.
Expand All @@ -179,11 +184,15 @@ def __init__(self):
PariArgument.__init__(self, iter(["self"]), None, 0)
def _typerepr(self):
return "Pari"
def ctype(self):
return "GEN"


class PariArgumentGEN(PariArgumentObject):
def _typerepr(self):
return "GEN"
def ctype(self):
return "GEN"
def convert_code(self):
if self.index == 0:
# "self" is always of type Gen, we skip the conversion
Expand Down Expand Up @@ -219,6 +228,8 @@ def call_code(self):
class PariArgumentString(PariArgumentObject):
def _typerepr(self):
return "str"
def ctype(self):
return "char *"
def convert_code(self):
if self.default is None:
s = " {name} = to_bytes({name})\n"
Expand All @@ -237,6 +248,8 @@ def call_code(self):
class PariArgumentVariable(PariArgumentObject):
def _typerepr(self):
return "var"
def ctype(self):
return "long"
def default_default(self):
return "-1"
def convert_code(self):
Expand Down
80 changes: 72 additions & 8 deletions autogen/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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

from .args import PariArgumentGEN, PariInstanceArgument
from .parser import (read_pari_desc, read_decl, parse_prototype)
from .parser import read_pari_desc, parse_prototype
from .doc import get_rest_doc


Expand All @@ -42,12 +43,52 @@
"""
'''.format(__file__)

decl_banner='''# This file is auto-generated by {}
from .types cimport *
cdef extern from *:
'''.format(__file__)


function_re = re.compile(r"^[A-Za-z][A-Za-z0-9_]*$")
function_blacklist = {"O", # O(p^e) needs special parser support
"alias", # Not needed and difficult documentation
"listcreate", # "redundant and obsolete" according to PARI
"allocatemem", # Better hand-written support in Pari class
"global", # Invalid in Python (and obsolete)
"inline", # Total confusion
"uninline", # idem
"local", # idem
"my", # idem
"plot", # Graphical function undeclared in pari public headers
"plotbox", # idem
"plotclip", # idem
"plotcursor", # idem
"plotcolor", # idem
"plotcopy", # idem
"plotdraw", # idem
"plothsizes", # idem
"plotinit", # idem
"plotkill", # idem
"plotlines", # idem
"plotlinetype", # idem
"plotmove", # idem
"plotpoints", # idem
"plotpointsize", # idem
"plotpointtype", # idem
"plotrbox", # idem
"plotrecth", # idem
"plotrecthraw", # idem
"plotrline", # idem
"plotrmove", # idem
"plotrpoint", # idem
"plotscale", # idem
"plotstring", # idem
"ploth", # idem
"plothraw", # idem
"psdraw", # idem
"psplothraw", # idem
}

class PariFunctionGenerator(object):
Expand All @@ -59,9 +100,9 @@ class PariFunctionGenerator(object):
:class:`Pari`.
"""
def __init__(self):
self.declared = read_decl()
self.gen_filename = os.path.join('cypari2', 'auto_gen.pxi')
self.instance_filename = os.path.join('cypari2', 'auto_instance.pxi')
self.decl_filename = os.path.join('cypari2', 'auto_paridecl.pxd')

def can_handle_function(self, function, cname="", **kwds):
"""
Expand All @@ -75,8 +116,6 @@ def can_handle_function(self, function, cname="", **kwds):
True
>>> G.can_handle_function("_bnfinit", "bnfinit0", **{"class":"basic"})
False
>>> G.can_handle_function("bnfinit", "BNFINIT0", **{"class":"basic"})
False
>>> G.can_handle_function("bnfinit", "bnfinit0", **{"class":"hard"})
False
"""
Expand All @@ -86,9 +125,6 @@ def can_handle_function(self, function, cname="", **kwds):
if not function_re.match(function):
# Not a legal function name, like "!_"
return False
if cname not in self.declared:
# PARI function not in paridecl.pxd or declinl.pxi
return False
cls = kwds.get("class", "unknown")
sec = kwds.get("section", "unknown")
if cls not in ("basic", "highlevel"):
Expand All @@ -100,7 +136,7 @@ def can_handle_function(self, function, cname="", **kwds):
return False
return True

def handle_pari_function(self, function, cname="", prototype="", help="", obsolete=None, **kwds):
def handle_pari_function(self, function, cname, prototype="", help="", obsolete=None, **kwds):
r"""
Handle one PARI function: decide whether or not to add the
function, in which file (as method of :class:`Gen` or
Expand All @@ -114,10 +150,12 @@ def handle_pari_function(self, function, cname="", prototype="", help="", obsole
>>> G = PariFunctionGenerator()
>>> G.gen_file = sys.stdout
>>> G.instance_file = sys.stdout
>>> G.decl_file = sys.stdout
>>> G.handle_pari_function("bnfinit",
... cname="bnfinit0", prototype="GD0,L,DGp",
... help=r"bnfinit(P,{flag=0},{tech=[]}): compute...",
... **{"class":"basic", "section":"number_fields"})
GEN bnfinit0(GEN, long, GEN, long)
def bnfinit(P, long flag=0, tech=None, long precision=0):
...
cdef GEN _P = P.g
Expand All @@ -135,6 +173,7 @@ def bnfinit(P, long flag=0, tech=None, long precision=0):
... cname="ellmodulareqn", prototype="LDnDn",
... help=r"ellmodulareqn(N,{x},{y}): return...",
... **{"class":"basic", "section":"elliptic_curves"})
GEN ellmodulareqn(long, long, long)
def ellmodulareqn(self, long N, x=None, y=None):
...
cdef long _x = -1
Expand All @@ -152,6 +191,7 @@ def ellmodulareqn(self, long N, x=None, y=None):
... help=r"setrand(n): reset the seed...",
... doc=r"reseeds the random number generator...",
... **{"class":"basic", "section":"programming/specific"})
void setrand(GEN)
def setrand(n):
r'''
Reseeds the random number generator using the seed :math:`n`. No value is
Expand All @@ -171,6 +211,7 @@ def setrand(n):
... help="bernvec(x): this routine is obsolete, use bernfrac repeatedly.",
... obsolete="2007-03-30",
... **{"class":"basic", "section":"transcendental"})
GEN bernvec(long)
def bernvec(self, long x):
r'''
This routine is obsolete, kept for backward compatibility only.
Expand All @@ -189,6 +230,8 @@ def bernvec(self, long x):

doc = get_rest_doc(function)

self.write_declaration(cname, args, ret, self.decl_file)

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

def write_declaration(self, cname, args, ret, file):
"""
Write a .pxd declaration of a PARI library function.
INPUT:
- ``cname`` -- name of the PARI C library call
- ``args``, ``ret`` -- output from ``parse_prototype``
- ``file`` -- a file object where the declaration should be
written to
"""
args = ", ".join(a.ctype() for a in args)
s = ' {ret} {function}({args})'.format(ret=ret.ctype(), function=cname, args=args)
print(s, file=file)

def write_method(self, function, cname, args, ret, cargs, file, doc, obsolete):
"""
Write Cython code with a method to call one PARI function.
Expand Down Expand Up @@ -264,6 +324,8 @@ def __call__(self):
self.gen_file.write(gen_banner)
self.instance_file = open(self.instance_filename + '.tmp', 'w')
self.instance_file.write(instance_banner)
self.decl_file = open(self.decl_filename + '.tmp', 'w')
self.decl_file.write(decl_banner)

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

self.gen_file.close()
self.instance_file.close()
self.decl_file.close()

# All done? Let's commit.
os.rename(self.gen_filename + '.tmp', self.gen_filename)
os.rename(self.instance_filename + '.tmp', self.instance_filename)
os.rename(self.decl_filename + '.tmp', self.decl_filename)
26 changes: 0 additions & 26 deletions autogen/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from .ret import pari_ret_types
from .paths import pari_share


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

Expand Down Expand Up @@ -71,31 +70,6 @@ def read_pari_desc():

return functions


decl_re = re.compile(" ([A-Za-z][A-Za-z0-9_]*)[(]")

def read_decl():
"""
Read the files ``paridecl.pxd`` and ``declinl.pxi`` and return a set
of all declared PARI library functions.
We do a simple regexp search, so there might be false positives.
The main use is to skip undeclared functions.
EXAMPLES::
>>> from autogen.parser import read_decl
>>> sorted(read_decl())
['ABC_to_bnr', ..., 'zx_to_zv']
"""
s = set()
with open(os.path.join("cypari2", "paridecl.pxd")) as f:
s.update(decl_re.findall(f.read()))
with open(os.path.join("cypari2", "declinl.pxi")) as f:
s.update(decl_re.findall(f.read()))
return s


def parse_prototype(proto, help, initial_args=[]):
"""
Parse arguments and return type of a PARI function.
Expand Down
7 changes: 5 additions & 2 deletions cypari2/gen.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT
from cysignals.memory cimport sig_free, check_malloc
from cysignals.signals cimport sig_check, sig_on, sig_off, sig_block, sig_unblock

from .paridecl cimport *
from .types cimport *
from .string_utils cimport to_string, to_bytes
from .paripriv cimport *
from .convert cimport (integer_to_gen, gen_to_integer,
Expand All @@ -81,6 +81,9 @@ from .pari_instance cimport (prec_bits_to_words, prec_words_to_bits,
from .stack cimport new_gen, new_gen_noclear, clear_stack
from .closure cimport objtoclosure

from .paridecl cimport *
from .auto_paridecl cimport *

include 'auto_gen.pxi'

@cython.final
Expand Down Expand Up @@ -3426,7 +3429,7 @@ cdef class Gen(Gen_auto):
<... 'int'>
"""
sig_on()
cdef GEN g = anell(self.g, n)
cdef GEN g = ellan(self.g, n)
if python_ints:
v = [gtolong(gel(g, i+1)) for i in range(glength(g))]
clear_stack()
Expand Down
3 changes: 2 additions & 1 deletion cypari2/pari_instance.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ from cysignals.signals cimport sig_check, sig_on, sig_off

from .string_utils cimport to_string, to_bytes
from .paridecl cimport *
from .auto_paridecl cimport *
from .paripriv cimport *
from .gen cimport Gen, objtogen
from .stack cimport new_gen, new_gen_noclear, clear_stack
Expand Down Expand Up @@ -1341,7 +1342,7 @@ cdef class Pari(Pari_auto):
"""
if x is None:
sig_on()
return new_gen(listcreate())
return new_gen(mklist())
cdef Gen t0 = objtogen(x)
sig_on()
return new_gen(gtolist(t0.g))
Expand Down
1 change: 1 addition & 0 deletions cypari2/paridecl.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -2987,6 +2987,7 @@ cdef extern from *: # PARI headers already included by types.pxd
GEN listput0(GEN list, GEN object, long index)
void listsort(GEN list, long flag)
GEN matsize(GEN x)
GEN mklist()
GEN mklistcopy(GEN x)
GEN normalize(GEN x)
GEN normalizepol(GEN x)
Expand Down

0 comments on commit 5ca809c

Please sign in to comment.