1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
#!/usr/bin/env python3
# encoding: utf-8
"""
Function.py
Created by Graham Dennis on 2008-07-10.
Copyright (c) 2008-2012, Graham Dennis
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
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
class Function(object):
"""
The idea behind Function objects is that they wrap C functions that will be in
the generated source file that will need to be flexible in terms of what
arguments they have. This way, one object can add function arguments to the
functions 'belonging' to other objects without the second object needing to be
involved.
An example of an application of this is passing the cross-propagation dimension
variable to delta-a operators in the cross-propagation integrator. As the
cross-propagation integrator is a normal integrator, it wouldn't normally need
to pass the propagation dimension variable to the delta a operator as the
propagation dimension value is stored in a global variable. With the function
objects, the cross-propagation operator just needs to add additional arguments
to the cross-propagation delta-a calculation function and the delta-a operator
and these variables are then passed through these functions.
In a similar way other variables like cycle number of a looping segment could
be passed to children if a use for that can be found. This behaviour was
present in xmds-1, but hasn't been added to xpdeint as there hasn't been a need
for it, and it isn't clear how this behaviour would work in the face of nested
looping segments.
"""
def __init__(self, name, args, implementation,
description = None, returnType = 'void', predicate = lambda: True):
"""
Initialise a `Function` object with C name `functionName`, arguments `args` and a
return type of `returnType`. The `implementation` argument must be a function that
returns the body of the C function.
The `args` argument is an array of 2-tuples of the form ``('argType', 'argName')``.
`predicate` is a callable that will cause the function not to be defined if it returns false
"""
self.name = name
self.args = args[:]
self.implementationContents = implementation
self.returnType = returnType
self.description = description
# Note that the predicate is actually used by _ScriptElement
self.predicate = predicate
def _prototype(self):
"""
Return as a string the C function prototype that can be used for both
function declaration and definition.
For example, return ``void _segment3(int myInt)``.
"""
argumentString = ', '.join([arg[0] + ' ' + arg[1] for arg in self.args])
return ''.join([self.returnType, ' ', self.name, '(', argumentString, ')'])
def prototype(self):
"""
Return as a string the C function prototype for this function.
For example, return ``void _segment3(int myInt);\\n``.
"""
return self._prototype() + ';\n'
def implementation(self):
"""
Return as a string the C function implementation for this function.
"""
implementationBodyString = self.implementationContents(self)
result = []
if self.description:
result.append('// ' + self.description + '\n')
result.extend([self._prototype(), '\n{\n'])
for line in implementationBodyString.splitlines(True):
result.extend([' ', line])
result.append('}\n')
return ''.join(result)
def call(self, arguments = None, parentFunction = None, **KWs):
"""
Return as a string the C code to call this function with arguments `arguments`
and any keyword arguments.
"""
availableArguments = {}
# The precendence for the arguments is KWs, arguments, parentFunction.
# So we add arguments to availableArguments in the opposite order.
if parentFunction:
availableArguments.update([(arg[1], arg[1]) for arg in parentFunction.args])
if arguments:
availableArguments.update(arguments)
if KWs:
availableArguments.update(KWs)
argumentString = ', '.join([str(availableArguments[arg[1]]) for arg in self.args])
return ''.join([self.name, '(', argumentString, ');'])
|