├── VERSION ├── .gitignore ├── setup.py ├── MANIFEST.in ├── pyproject.toml ├── test.py ├── pari_utils ├── __init__.py ├── ret.py ├── paths.py ├── parser.py └── args.py ├── .github └── workflows │ └── test.yml ├── setup.cfg └── README.rst /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | build/ 3 | pari_utils.egg-info/ 4 | pari_utils/__pycache__ 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | setup() 5 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include VERSION 2 | include README.rst 3 | exclude setup.py 4 | exclude MANIFEST.in 5 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 40.6.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import doctest 5 | 6 | import pari_utils 7 | import pari_utils.args 8 | import pari_utils.paths 9 | import pari_utils.ret 10 | 11 | num_failed = 0 12 | for mod in [pari_utils, pari_utils.args, pari_utils.paths, pari_utils.ret]: 13 | res = doctest.testmod(mod, verbose=True) 14 | num_failed += res.failed 15 | sys.exit(num_failed) 16 | -------------------------------------------------------------------------------- /pari_utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility tools for PARI/GP 3 | """ 4 | #***************************************************************************** 5 | # Copyright (C) 2021 Vincent Delecroix 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 2 of the License, or 10 | # (at your option) any later version. 11 | # http://www.gnu.org/licenses/ 12 | #***************************************************************************** 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: { branches: [ "master" ] } 4 | pull_request: { branches: [ "master" ] } 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-20.04 9 | strategy: 10 | matrix: 11 | include: 12 | - python-version: "3.7" 13 | - python-version: "3.8" 14 | - python-version: "3.9" 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | - name: Set up PARI/GP 22 | run: sudo apt-get install -qy pari-gp libpari-dev 23 | - name: Run doctests 24 | shell: bash -l {0} 25 | run: | 26 | python --version 27 | ./test.py 28 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pari-utils 3 | version = file: VERSION 4 | description = utilities for PARI/GP 5 | long_description = file: README.rst 6 | long_description_content_type = text/x-rst 7 | author = Vincent Delecroix 8 | author_email = vincent.delecroix@u-bordeaux.fr 9 | url = https://github.com/sagemath/pari-utils 10 | download_url = https://pypi.org/project/pari-utils/#files 11 | license = GPL v3 12 | platforms = any 13 | classifiers = 14 | License :: OSI Approved :: GNU General Public License v3 (GPLv3) 15 | Programming Language :: C++ 16 | Programming Language :: Python 17 | Development Status :: 5 - Production/Stable 18 | Operating System :: Unix 19 | Intended Audience :: Science/Research 20 | Programming Language :: Python :: 3 21 | Programming Language :: Python :: 3.6 22 | Programming Language :: Python :: 3.7 23 | keywords = 24 | PARI/GP 25 | 26 | [options] 27 | packages = pari_utils 28 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pari-utils 2 | ========== 3 | 4 | A Python library to parse PARI/GP configuration and header files. This 5 | is mainly used in the code generation of https://github.com/sagemath/cypari2 6 | and https://github.com/sagemath/pari-jupyter. 7 | 8 | This library supports only Python 3. 9 | 10 | Installation 11 | ------------ 12 | 13 | Using pip 14 | ^^^^^^^^^ 15 | 16 | Requirements: 17 | 18 | - PARI/GP >= 2.9.4 (header files and library) 19 | - Python >= 3.7 20 | - setuptools >= 40.6.0, wheel 21 | 22 | Install pari-utils via the Python Package Index (PyPI) via 23 | 24 | :: 25 | 26 | $ pip install pari-utils [--user] 27 | 28 | (the optional option *--user* allows to install pari-utils for a single user 29 | and avoids using pip with administrator rights). Depending on your operating 30 | system the pip command might also be called pip2 or pip3. 31 | 32 | If you want to try the development version use 33 | 34 | :: 35 | 36 | $ pip install git+https://github.com/sagemath/pari-utils.git [--user] 37 | 38 | Contributing 39 | ------------ 40 | 41 | Submit pull request or get in touch with the SageMath developers. 42 | -------------------------------------------------------------------------------- /pari_utils/ret.py: -------------------------------------------------------------------------------- 1 | """ 2 | Return types for PARI calls 3 | """ 4 | 5 | #***************************************************************************** 6 | # Copyright (C) 2015 Jeroen Demeyer 7 | # 2021 Vincent Delecroix 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 2 of the License, or 12 | # (at your option) any later version. 13 | # http://www.gnu.org/licenses/ 14 | #***************************************************************************** 15 | 16 | class PariReturn(object): 17 | """ 18 | This class represents the return value of a PARI call. 19 | """ 20 | def __init__(self): 21 | self.name = "_ret" 22 | 23 | def __repr__(self): 24 | return self.ctype() 25 | 26 | def ctype(self): 27 | """ 28 | Return the C type of the result of the PARI call. 29 | """ 30 | raise NotImplementedError 31 | 32 | def assign_code(self, value): 33 | """ 34 | Return code to assign the result of the PARI call in ``value`` 35 | to the variable named ``self.name``. 36 | """ 37 | s = " cdef {ctype} {name} = {value}\n" 38 | return s.format(ctype=self.ctype(), name=self.name, value=value) 39 | 40 | def return_code(self): 41 | """ 42 | Return code to return from the Cython wrapper. 43 | """ 44 | s = " clear_stack()\n" 45 | s += " return {name}\n" 46 | return s.format(name=self.name) 47 | 48 | 49 | class PariReturnGEN(PariReturn): 50 | def ctype(self): 51 | return "GEN" 52 | def return_code(self): 53 | s = " return new_gen({name})\n" 54 | return s.format(name=self.name) 55 | 56 | class PariReturnInt(PariReturn): 57 | def ctype(self): 58 | return "int" 59 | 60 | class PariReturnLong(PariReturn): 61 | def ctype(self): 62 | return "long" 63 | 64 | class PariReturnULong(PariReturn): 65 | def ctype(self): 66 | return "unsigned long" 67 | 68 | class PariReturnVoid(PariReturn): 69 | def ctype(self): 70 | return "void" 71 | def assign_code(self, value): 72 | return " {value}\n".format(value=value) 73 | def return_code(self): 74 | s = " clear_stack()\n" 75 | return s 76 | 77 | 78 | pari_ret_types = { 79 | '': PariReturnGEN, 80 | 'm': PariReturnGEN, 81 | 'i': PariReturnInt, 82 | 'l': PariReturnLong, 83 | 'u': PariReturnULong, 84 | 'v': PariReturnVoid, 85 | } 86 | -------------------------------------------------------------------------------- /pari_utils/paths.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find out installation paths of PARI/GP 3 | """ 4 | 5 | #***************************************************************************** 6 | # Copyright (C) 2017 Jeroen Demeyer 7 | # 2021 Vincent Delecroix 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 2 of the License, or 12 | # (at your option) any later version. 13 | # http://www.gnu.org/licenses/ 14 | #***************************************************************************** 15 | 16 | import os 17 | from glob import glob 18 | from distutils.spawn import find_executable 19 | 20 | 21 | # find_executable() returns None if nothing was found 22 | gppath = find_executable("gp") 23 | if gppath is None: 24 | # This almost certainly won't work, but we need to put something here 25 | prefix = "." 26 | else: 27 | # Assume gppath is ${prefix}/bin/gp 28 | prefix = os.path.dirname(os.path.dirname(gppath)) 29 | 30 | 31 | def pari_share(): 32 | r""" 33 | Return the directory where the PARI data files are stored. 34 | 35 | >>> import os 36 | >>> from pari_utils.parser import pari_share 37 | >>> os.path.isfile(os.path.join(pari_share(), "pari.desc")) 38 | True 39 | """ 40 | if "PARI_SHARE" in os.environ: 41 | return os.environ["PARI_SHARE"] 42 | from subprocess import Popen, PIPE 43 | if not gppath: 44 | raise EnvironmentError("cannot find an installation of PARI/GP: make sure that the 'gp' program is in your $PATH") 45 | # Ignore GP_DATA_DIR environment variable 46 | env = dict(os.environ) 47 | env.pop("GP_DATA_DIR", None) 48 | gp = Popen([gppath, "-f", "-q"], stdin=PIPE, stdout=PIPE, env=env) 49 | out = gp.communicate(b"print(default(datadir))")[0] 50 | # Convert out to str if needed 51 | if not isinstance(out, str): 52 | from sys import getfilesystemencoding 53 | out = out.decode(getfilesystemencoding(), "surrogateescape") 54 | datadir = out.strip() 55 | if not os.path.isdir(datadir): 56 | # As a fallback, try a path relative to the prefix 57 | datadir = os.path.join(prefix, "share", "pari") 58 | if not os.path.isdir(datadir): 59 | raise EnvironmentError("PARI data directory {!r} does not exist".format(datadir)) 60 | return datadir 61 | 62 | 63 | def include_dirs(): 64 | """ 65 | Return a list of directories containing PARI include files. 66 | """ 67 | dirs = [os.path.join(prefix, "include")] 68 | return [d for d in dirs if os.path.isdir(os.path.join(d, "pari"))] 69 | 70 | 71 | def library_dirs(): 72 | """ 73 | Return a list of directories containing PARI library files. 74 | """ 75 | dirs = [os.path.join(prefix, s) for s in ("lib", "lib32", "lib64")] 76 | return [d for d in dirs if glob(os.path.join(d, "libpari*"))] 77 | -------------------------------------------------------------------------------- /pari_utils/parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Read and parse the file pari.desc 3 | """ 4 | 5 | #***************************************************************************** 6 | # Copyright (C) 2015 Jeroen Demeyer 7 | # (C) 2021 Vincent Delecroix 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 2 of the License, or 12 | # (at your option) any later version. 13 | # http://www.gnu.org/licenses/ 14 | #***************************************************************************** 15 | 16 | import os, re, io 17 | 18 | from .args import pari_arg_types 19 | from .ret import pari_ret_types 20 | from .paths import pari_share 21 | 22 | paren_re = re.compile(r"[(](.*)[)]") 23 | argname_re = re.compile(r"[ {]*&?([A-Za-z_][A-Za-z0-9_]*)") 24 | 25 | def read_pari_desc(): 26 | """ 27 | Read and parse the file ``pari.desc``. 28 | 29 | The output is a dictionary where the keys are GP function names 30 | and the corresponding values are dictionaries containing the 31 | ``(key, value)`` pairs from ``pari.desc``. 32 | 33 | >>> from pari_utils.parser import read_pari_desc 34 | >>> D = read_pari_desc() 35 | >>> Dcos = D["cos"] 36 | >>> if "description" in Dcos: _ = Dcos.pop("description") 37 | >>> Dcos.pop("doc").startswith('cosine of $x$.') 38 | True 39 | >>> Dcos == { 'class': 'basic', 40 | ... 'cname': 'gcos', 41 | ... 'function': 'cos', 42 | ... 'help': 'cos(x): cosine of x.', 43 | ... 'prototype': 'Gp', 44 | ... 'section': 'transcendental'} 45 | True 46 | """ 47 | pari_desc = os.path.join(pari_share(), 'pari.desc') 48 | with io.open(pari_desc, encoding="utf-8") as f: 49 | lines = f.readlines() 50 | 51 | n = 0 52 | N = len(lines) 53 | 54 | functions = {} 55 | while n < N: 56 | fun = {} 57 | while True: 58 | L = lines[n]; n += 1 59 | if L == "\n": 60 | break 61 | # As long as the next lines start with a space, append them 62 | while lines[n].startswith(" "): 63 | L += (lines[n])[1:]; n += 1 64 | key, value = L.split(":", 1) 65 | # Change key to an allowed identifier name 66 | key = key.lower().replace("-", "") 67 | fun[key] = value.strip() 68 | 69 | name = fun["function"] 70 | functions[name] = fun 71 | 72 | return functions 73 | 74 | def parse_prototype(proto, help, initial_args=[]): 75 | """ 76 | Parse arguments and return type of a PARI function. 77 | 78 | :param proto: a PARI prototype like ``"GD0,L,DGDGDG"`` 79 | :param help: a PARI help string like ``"qfbred(x,{flag=0},{d},{isd},{sd})"`` 80 | :param initial_args: other arguments to this function which come 81 | before the PARI arguments, for example a ``self`` argument. 82 | :returns: a tuple ``(args, ret)`` where 83 | 84 | - ``args`` is a list consisting of ``initial_args`` followed by 85 | :class:`PariArgument` instances with all arguments of this 86 | function. 87 | 88 | - ``ret`` is a :class:`PariReturn` instance with the return type of 89 | this function. 90 | 91 | >>> from pari_utils.parser import parse_prototype 92 | >>> proto = 'GD0,L,DGDGDG' 93 | >>> help = 'qfbred(x,{flag=0},{d},{isd},{sd})' 94 | >>> parse_prototype(proto, help) 95 | ([GEN x, long flag=0, GEN d=NULL, GEN isd=NULL, GEN sd=NULL], GEN) 96 | >>> proto = "GD&" 97 | >>> help = "sqrtint(x,{&r})" 98 | >>> parse_prototype(proto, help) 99 | ([GEN x, GEN* r=NULL], GEN) 100 | >>> parse_prototype("lp", "foo()", [str("TEST")]) 101 | (['TEST', prec precision=0], long) 102 | """ 103 | # Use the help string just for the argument names. 104 | # "names" should be an iterator over the argument names. 105 | m = paren_re.search(help) 106 | if m is None: 107 | names = iter([]) 108 | else: 109 | s = m.groups()[0] 110 | matches = [argname_re.match(x) for x in s.split(",")] 111 | names = (m.groups()[0] for m in matches if m is not None) 112 | 113 | # First, handle the return type 114 | try: 115 | c = proto[0] 116 | t = pari_ret_types[c] 117 | n = 1 # index in proto 118 | except (IndexError, KeyError): 119 | t = pari_ret_types[""] 120 | n = 0 # index in proto 121 | ret = t() 122 | 123 | # Go over the prototype characters and build up the arguments 124 | args = list(initial_args) 125 | have_default = False # Have we seen any default argument? 126 | while n < len(proto): 127 | c = proto[n]; n += 1 128 | 129 | # Parse default value 130 | if c == "D": 131 | default = "" 132 | if proto[n] not in pari_arg_types: 133 | while True: 134 | c = proto[n]; n += 1 135 | if c == ",": 136 | break 137 | default += c 138 | c = proto[n]; n += 1 139 | else: 140 | default = None 141 | 142 | try: 143 | t = pari_arg_types[c] 144 | if t is None: 145 | raise NotImplementedError('unsupported prototype character %r' % c) 146 | except KeyError: 147 | if c == ",": 148 | continue # Just skip additional commas 149 | else: 150 | raise ValueError('unknown prototype character %r' % c) 151 | 152 | arg = t(names, default, index=len(args)) 153 | if arg.default is not None: 154 | have_default = True 155 | elif have_default: 156 | # We have a non-default argument following a default 157 | # argument, which means trouble... 158 | # 159 | # A syntactical wart of Python is that it does not allow 160 | # that: something like def foo(x=None, y) is a SyntaxError 161 | # (at least with Python-2.7.13, Python-3.6.1 and Cython-0.25.2) 162 | # 163 | # A small number of GP functions (nfroots() for example) 164 | # wants to do this anyway. Luckily, this seems to occur only 165 | # for arguments of type GEN (prototype code "G") 166 | # 167 | # To work around this, we add a "fake" default value and 168 | # then raise an error if it was not given... 169 | if c != "G": 170 | raise NotImplementedError("non-default argument after default argument is only implemented for GEN arguments") 171 | arg.default = False 172 | args.append(arg) 173 | 174 | return (args, ret) 175 | 176 | def gp_functions(): 177 | """ 178 | Iterator through GP function names 179 | 180 | >>> from pari_utils.parser import gp_functions 181 | >>> it = gp_functions() 182 | >>> [next(it) for _ in range(5)] 183 | ['Catalan', 'Col', 'Colrev', 'Euler', 'I'] 184 | """ 185 | pari_desc = read_pari_desc() 186 | for name in sorted(pari_desc): 187 | data = pari_desc[name] 188 | if data['class'] == 'basic' and '_' not in name and name != '%' and name != '%#': 189 | yield name 190 | -------------------------------------------------------------------------------- /pari_utils/args.py: -------------------------------------------------------------------------------- 1 | """ 2 | Arguments for PARI calls 3 | """ 4 | 5 | #***************************************************************************** 6 | # Copyright (C) 2015 Jeroen Demeyer 7 | # (C) 2021 Vincent Delecroix 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 2 of the License, or 12 | # (at your option) any later version. 13 | # http://www.gnu.org/licenses/ 14 | #***************************************************************************** 15 | 16 | # Some replacements for reserved words 17 | replacements = {'char': 'character', 'return': 'return_value'} 18 | 19 | class PariArgument(object): 20 | """ 21 | This class represents one argument in a PARI call. 22 | """ 23 | def __init__(self, namesiter, default, index): 24 | """ 25 | Create a new argument for a PARI call. 26 | 27 | :param namesiter: iterator over all names of the arguments. 28 | Usually, the next name from this iterator is used as argument 29 | name. 30 | :param default: default value for this argument (``None`` 31 | means that the argument is not optional). 32 | :param index: (integer >= 0). Index of this argument in the 33 | list of arguments. Index 0 means a ``"self"`` argument which 34 | is treated specially. For a function which is not a method, 35 | start counting at 1. 36 | """ 37 | self.index = index 38 | try: 39 | self.name = self.get_argument_name(namesiter) 40 | except StopIteration: 41 | # No more names available, use something default. 42 | # This is used in listcreate() and polsturm() for example 43 | # which have deprecated arguments which are not listed in 44 | # the help. 45 | self.name = "_arg%s" % index 46 | self.undocumented = True 47 | else: 48 | self.undocumented = False 49 | 50 | if self.index == 0: # "self" argument can never have a default 51 | self.default = None 52 | elif default is None: 53 | self.default = self.always_default() 54 | elif default == "": 55 | self.default = self.default_default() 56 | else: 57 | self.default = default 58 | 59 | # Name for a temporary variable. Only a few classes actually use this. 60 | self.tmpname = "_" + self.name 61 | 62 | def __repr__(self): 63 | s = self._typerepr() + " " + self.name 64 | if self.default is not None: 65 | s += "=" + self.default 66 | return s 67 | 68 | def _typerepr(self): 69 | """ 70 | Return a string representing the type of this argument. 71 | """ 72 | return "(generic)" 73 | 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 | 82 | def always_default(self): 83 | """ 84 | If this returns not ``None``, it is a value which is always 85 | the default for this argument, which is then automatically 86 | optional. 87 | """ 88 | return None 89 | 90 | def default_default(self): 91 | """ 92 | The default value for an optional argument if no other default 93 | was specified in the prototype. 94 | """ 95 | return "NULL" 96 | 97 | def get_argument_name(self, namesiter): 98 | """ 99 | Return the name for this argument, given ``namesiter`` which is 100 | an iterator over the argument names given by the help string. 101 | """ 102 | n = next(namesiter) 103 | try: 104 | return replacements[n] 105 | except KeyError: 106 | return n 107 | 108 | def prototype_code(self): 109 | """ 110 | Return code to appear in the prototype of the Cython wrapper. 111 | """ 112 | raise NotImplementedError 113 | 114 | def deprecation_warning_code(self, function): 115 | """ 116 | Return code to appear in the function body to give a 117 | deprecation warning for this argument, if applicable. 118 | ``function`` is the function name to appear in the message. 119 | """ 120 | if not self.undocumented: 121 | return "" 122 | s = " if {name} is not None:\n" 123 | s += " from warnings import warn\n" 124 | s += " warn('argument {index} of the PARI/GP function {function} is undocumented and deprecated', DeprecationWarning)\n" 125 | return s.format(name=self.name, index=self.index, function=function) 126 | 127 | def convert_code(self): 128 | """ 129 | Return code to appear in the function body to convert this 130 | argument to something that PARI understand. This code can also 131 | contain extra checks. It will run outside of ``sig_on()``. 132 | """ 133 | return "" 134 | 135 | def c_convert_code(self): 136 | """ 137 | Return additional conversion code which will be run after 138 | ``convert_code`` and inside the ``sig_on()`` block. This must 139 | not involve any Python code (in particular, it should not raise 140 | exceptions). 141 | """ 142 | return "" 143 | 144 | def call_code(self): 145 | """ 146 | Return code to put this argument in a PARI function call. 147 | """ 148 | return self.name 149 | 150 | 151 | class PariArgumentObject(PariArgument): 152 | """ 153 | Class for arguments which are passed as generic Python ``object``. 154 | """ 155 | def prototype_code(self): 156 | """ 157 | Return code to appear in the prototype of the Cython wrapper. 158 | """ 159 | s = self.name 160 | if self.default is not None: 161 | # Default corresponds to None, actual default value should 162 | # be handled in convert_code() 163 | s += "=None" 164 | return s 165 | 166 | class PariArgumentClass(PariArgument): 167 | """ 168 | Class for arguments which are passed as a specific C/Cython class. 169 | 170 | The C/Cython type is given by ``self.ctype()``. 171 | """ 172 | def prototype_code(self): 173 | """ 174 | Return code to appear in the prototype of the Cython wrapper. 175 | """ 176 | s = self.ctype() + " " + self.name 177 | if self.default is not None: 178 | s += "=" + self.default 179 | return s 180 | 181 | 182 | class PariInstanceArgument(PariArgumentObject): 183 | """ 184 | ``self`` argument for ``Pari`` object. 185 | 186 | This argument is never actually used. 187 | """ 188 | def __init__(self): 189 | PariArgument.__init__(self, iter(["self"]), None, 0) 190 | def _typerepr(self): 191 | return "Pari" 192 | def ctype(self): 193 | return "GEN" 194 | 195 | 196 | class PariArgumentGEN(PariArgumentObject): 197 | def _typerepr(self): 198 | return "GEN" 199 | def ctype(self): 200 | return "GEN" 201 | def convert_code(self): 202 | """ 203 | Conversion to Gen 204 | """ 205 | if self.index == 0: 206 | # self argument 207 | s = "" 208 | elif self.default is None: 209 | s = " {name} = objtogen({name})\n" 210 | elif self.default is False: 211 | # This is actually a required argument 212 | # See parse_prototype() in parser.py why we need this 213 | s = " if {name} is None:\n" 214 | s += " raise TypeError(\"missing required argument: '{name}'\")\n" 215 | s += " {name} = objtogen({name})\n" 216 | else: 217 | s = " cdef bint _have_{name} = ({name} is not None)\n" 218 | s += " if _have_{name}:\n" 219 | s += " {name} = objtogen({name})\n" 220 | return s.format(name=self.name) 221 | def c_convert_code(self): 222 | """ 223 | Conversion Gen -> GEN 224 | """ 225 | if not self.default: 226 | # required argument 227 | s = " cdef GEN {tmp} = ({name}).g\n" 228 | elif self.default == "NULL": 229 | s = " cdef GEN {tmp} = NULL\n" 230 | s += " if _have_{name}:\n" 231 | s += " {tmp} = ({name}).g\n" 232 | elif self.default == "0": 233 | s = " cdef GEN {tmp} = gen_0\n" 234 | s += " if _have_{name}:\n" 235 | s += " {tmp} = ({name}).g\n" 236 | else: 237 | raise ValueError("default value %r for GEN argument %r is not supported" % (self.default, self.name)) 238 | return s.format(name=self.name, tmp=self.tmpname) 239 | def call_code(self): 240 | return self.tmpname 241 | 242 | class PariArgumentString(PariArgumentObject): 243 | def _typerepr(self): 244 | return "str" 245 | def ctype(self): 246 | return "char *" 247 | def convert_code(self): 248 | if self.default is None: 249 | s = " {name} = to_bytes({name})\n" 250 | s += " cdef char* {tmp} = {name}\n" 251 | else: 252 | s = " cdef char* {tmp}\n" 253 | s += " if {name} is None:\n" 254 | s += " {tmp} = {default}\n" 255 | s += " else:\n" 256 | s += " {name} = to_bytes({name})\n" 257 | s += " {tmp} = {name}\n" 258 | return s.format(name=self.name, tmp=self.tmpname, default=self.default) 259 | def call_code(self): 260 | return self.tmpname 261 | 262 | class PariArgumentVariable(PariArgumentObject): 263 | def _typerepr(self): 264 | return "var" 265 | def ctype(self): 266 | return "long" 267 | def default_default(self): 268 | return "-1" 269 | def convert_code(self): 270 | if self.default is None: 271 | s = " cdef long {tmp} = get_var({name})\n" 272 | else: 273 | s = " cdef long {tmp} = {default}\n" 274 | s += " if {name} is not None:\n" 275 | s += " {tmp} = get_var({name})\n" 276 | return s.format(name=self.name, tmp=self.tmpname, default=self.default) 277 | def call_code(self): 278 | return self.tmpname 279 | 280 | class PariArgumentLong(PariArgumentClass): 281 | def _typerepr(self): 282 | return "long" 283 | def ctype(self): 284 | return "long" 285 | def default_default(self): 286 | return "0" 287 | 288 | class PariArgumentULong(PariArgumentClass): 289 | def _typerepr(self): 290 | return "unsigned long" 291 | def ctype(self): 292 | return "unsigned long" 293 | def default_default(self): 294 | return "0" 295 | 296 | class PariArgumentPrec(PariArgumentClass): 297 | def _typerepr(self): 298 | return "prec" 299 | def ctype(self): 300 | return "long" 301 | def always_default(self): 302 | return "0" 303 | def get_argument_name(self, namesiter): 304 | return "precision" 305 | def c_convert_code(self): 306 | s = " {name} = prec_bits_to_words({name})\n" 307 | return s.format(name=self.name) 308 | 309 | class PariArgumentBitprec(PariArgumentClass): 310 | def _typerepr(self): 311 | return "bitprec" 312 | def ctype(self): 313 | return "long" 314 | def always_default(self): 315 | return "0" 316 | def get_argument_name(self, namesiter): 317 | return "precision" 318 | def c_convert_code(self): 319 | s = " if not {name}:\n" 320 | s += " {name} = default_bitprec()\n" 321 | return s.format(name=self.name) 322 | 323 | class PariArgumentSeriesPrec(PariArgumentClass): 324 | def _typerepr(self): 325 | return "serprec" 326 | def ctype(self): 327 | return "long" 328 | def default_default(self): 329 | return "-1" 330 | def get_argument_name(self, namesiter): 331 | return "serprec" 332 | def c_convert_code(self): 333 | s = " if {name} < 0:\n" 334 | s += " {name} = precdl # Global PARI series precision\n" 335 | return s.format(name=self.name) 336 | 337 | class PariArgumentGENPointer(PariArgumentObject): 338 | default = "NULL" 339 | def _typerepr(self): 340 | return "GEN*" 341 | def ctype(self): 342 | return "GEN*" 343 | def convert_code(self): 344 | """ 345 | Conversion to NULL or Gen 346 | """ 347 | s = " cdef bint _have_{name} = ({name} is not None)\n" 348 | s += " if _have_{name}:\n" 349 | s += " raise NotImplementedError(\"optional argument {name} not available\")\n" 350 | return s.format(name=self.name) 351 | def c_convert_code(self): 352 | """ 353 | Conversion Gen -> GEN 354 | """ 355 | s = " cdef GEN * {tmp} = NULL\n" 356 | return s.format(name=self.name, tmp=self.tmpname) 357 | def call_code(self): 358 | return self.tmpname 359 | 360 | 361 | pari_arg_types = { 362 | 'G': PariArgumentGEN, 363 | 'W': PariArgumentGEN, 364 | 'r': PariArgumentString, 365 | 's': PariArgumentString, 366 | 'L': PariArgumentLong, 367 | 'U': PariArgumentULong, 368 | 'n': PariArgumentVariable, 369 | 'p': PariArgumentPrec, 370 | 'b': PariArgumentBitprec, 371 | 'P': PariArgumentSeriesPrec, 372 | '&': PariArgumentGENPointer, 373 | 374 | # Codes which are known but not actually supported yet 375 | 'V': None, 376 | 'I': None, 377 | 'E': None, 378 | 'J': None, 379 | 'C': None, 380 | '*': None, 381 | '=': None} 382 | --------------------------------------------------------------------------------