Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autogeneration of pari declarations #28

Merged
merged 4 commits into from
Aug 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3428,7 +3431,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