Changes in / [ff431ca:d5014e4] in sasmodels
- Files:
-
- 3 deleted
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/guide/orientation/orientation.rst
r82592da r5fb0634 4 4 ================== 5 5 6 With two dimensional small angle diffraction data SasViewwill calculate6 With two dimensional small angle diffraction data sasmodels will calculate 7 7 scattering from oriented particles, applicable for example to shear flow 8 8 or orientation in a magnetic field. 9 9 10 10 In general we first need to define the reference orientation 11 of the particles with respect to the incoming neutron or X-ray beam. This 12 is done using three angles: $\theta$ and $\phi$ define the orientation of 13 the axis of the particle, angle $\Psi$ is defined as the orientation of 14 the major axis of the particle cross section with respect to its starting 15 position along the beam direction. The figures below are for an elliptical 16 cross section cylinder, but may be applied analogously to other shapes of 17 particle. 11 of the particle's $a$-$b$-$c$ axes with respect to the incoming 12 neutron or X-ray beam. This is done using three angles: $\theta$ and $\phi$ 13 define the orientation of the $c$-axis of the particle, and angle $\Psi$ is 14 defined as the orientation of the major axis of the particle cross section 15 with respect to its starting position along the beam direction (or 16 equivalently, as rotation about the $c$ axis). There is an unavoidable 17 ambiguity when $c$ is aligned with $z$ in that $\phi$ and $\Psi$ both 18 serve to rotate the particle about $c$, but this symmetry is destroyed 19 when $\theta$ is not a multiple of 180. 20 21 The figures below are for an elliptical cross section cylinder, but may 22 be applied analogously to other shapes of particle. 18 23 19 24 .. note:: … … 29 34 30 35 Definition of angles for oriented elliptical cylinder, where axis_ratio 31 b/a is shown >1 ,Note that rotation $\theta$, initially in the $x$-$z$36 b/a is shown >1. Note that rotation $\theta$, initially in the $x$-$z$ 32 37 plane, is carried out first, then rotation $\phi$ about the $z$-axis, 33 38 finally rotation $\Psi$ is around the axis of the cylinder. The neutron 34 or X-ray beam is along the $ z$ axis.39 or X-ray beam is along the $-z$ axis. 35 40 36 41 .. figure:: … … 40 45 with $\Psi$ = 0. 41 46 42 Having established the mean direction of the particle we can then apply 43 angular orientation distributions. This is done by a numerical integration 44 over a range of angles in a similar way to particle size dispersity. 45 In the current version of sasview the orientational dispersity is defined 46 with respect to the axes of the particle. 47 Having established the mean direction of the particle (the view) we can then 48 apply angular orientation distributions (jitter). This is done by a numerical 49 integration over a range of angles in a similar way to particle size 50 dispersity. The orientation dispersity is defined with respect to the 51 $a$-$b$-$c$ axes of the particle, with roll angle $\Psi$ about the $c$-axis, 52 yaw angle $\theta$ about the $b$-axis and pitch angle $\phi$ about the 53 $a$-axis. 54 55 More formally, starting with axes $a$-$b$-$c$ of the particle aligned 56 with axes $x$-$y$-$z$ of the laboratory frame, the orientation dispersity 57 is applied first, using the 58 `Tait-Bryan <https://en.wikipedia.org/wiki/Euler_angles#Conventions_2>`_ 59 $x$-$y'$-$z''$ convention with angles $\Delta\phi$-$\Delta\theta$-$\Delta\Psi$. 60 The reference orientation then follows, using the 61 `Euler angles <https://en.wikipedia.org/wiki/Euler_angles#Conventions>`_ 62 $z$-$y'$-$z''$ with angles $\phi$-$\theta$-$\Psi$. This is implemented 63 using rotation matrices as 64 65 .. math:: 66 67 R = R_z(\phi)\, R_y(\theta)\, R_z(\Psi)\, 68 R_x(\Delta\phi)\, R_y(\Delta\theta)\, R_z(\Delta\Psi) 69 70 To transform detector $(q_x, q_y)$ values into $(q_a, q_b, q_c)$ for the 71 shape in its canonical orientation, use 72 73 .. math:: 74 75 [q_a, q_b, q_c]^T = R^{-1} \, [q_x, q_y, 0]^T 76 77 78 The inverse rotation is easily calculated by rotating the opposite directions 79 in the reverse order, so 80 81 .. math:: 82 83 R^{-1} = R_z(-\Delta\Psi)\, R_y(-\Delta\theta)\, R_x(-\Delta\phi)\, 84 R_z(-\Psi)\, R_y(-\theta)\, R_z(-\phi) 85 47 86 48 87 The $\theta$ and $\phi$ orientation parameters for the cylinder only appear 49 when fitting 2d data. On introducing "Orientational Distribution" in 50 theangles, "distribution of theta" and "distribution of phi" parameters will88 when fitting 2d data. On introducing "Orientational Distribution" in the 89 angles, "distribution of theta" and "distribution of phi" parameters will 51 90 appear. These are actually rotations about the axes $\delta_1$ and $\delta_2$ 52 of the cylinder, the $b$ and $a$ axes of the cylinder cross section. (When 53 $\theta = \phi = 0$ these are parallel to the $Y$ and $X$ axes of the 54 instrument.) The third orientation distribution, in $\Psi$, is about the $c$ 55 axis of the particle. Some experimentation may be required to understand the 56 2d patterns fully. A number of different shapes of distribution are 57 available, as described for polydispersity, see :ref:`polydispersityhelp` . 91 of the cylinder, which correspond to the $b$ and $a$ axes of the cylinder 92 cross section. (When $\theta = \phi = 0$ these are parallel to the $Y$ and 93 $X$ axes of the instrument.) The third orientation distribution, in $\Psi$, 94 is about the $c$ axis of the particle. Some experimentation may be required 95 to understand the 2d patterns fully. A number of different shapes of 96 distribution are available, as described for size dispersity, see 97 :ref:`polydispersityhelp`. 58 98 59 Earlier versions of SasView had numerical integration issues in some 60 circumstances when distributions passed through 90 degrees. The distributions 61 in particle coordinates are more robust, but should still be approached with 62 care for large ranges of angle. 99 Given that the angular dispersion distribution is defined in cartesian space, 100 over a cube defined by 101 102 .. math:: 103 104 [-\Delta \theta, \Delta \theta] \times 105 [-\Delta \phi, \Delta \phi] \times 106 [-\Delta \Psi, \Delta \Psi] 107 108 but the orientation is defined over a sphere, we are left with a 109 `map projection <https://en.wikipedia.org/wiki/List_of_map_projections>`_ 110 problem, with different tradeoffs depending on how values in $\Delta\theta$ 111 and $\Delta\phi$ are translated into latitude/longitude on the sphere. 112 113 Sasmodels is using the 114 `equirectangular projection <https://en.wikipedia.org/wiki/Equirectangular_projection>`_. 115 In this projection, square patches in angular dispersity become wedge-shaped 116 patches on the sphere. To correct for the changing point density, there is a 117 scale factor of $\sin(\Delta\theta)$ that applies to each point in the 118 integral. This is not enough, though. Consider a shape which is tumbling 119 freely around the $b$ axis, with $\Delta\theta$ uniform in $[-180, 180]$. At 120 $\pm 90$, all points in $\Delta\phi$ map to the pole, so the jitter will have 121 a distinct angular preference. If the spin axis is along the beam (which 122 will be the case for $\theta=90$ and $\Psi=90$) the scattering pattern 123 should be circularly symmetric, but it will go to zero at $q_x = 0$ due to the 124 $\sin(\Delta\theta)$ correction. This problem does not appear for a shape 125 that is tumbling freely around the $a$ axis, with $\Delta\phi$ uniform in 126 $[-180, 180]$, so swap the $a$ and $b$ axes so $\Delta\theta < \Delta\phi$ 127 and adjust $\Psi$ by 90. This works with the current sasmodels shapes due to 128 symmetry. 129 130 Alternative projections were considered. 131 The `sinusoidal projection <https://en.wikipedia.org/wiki/Sinusoidal_projection>`_ 132 works by scaling $\Delta\phi$ as $\Delta\theta$ increases, and dropping those 133 points outside $[-180, 180]$. The distortions are a little less for middle 134 ranges of $\Delta\theta$, but they are still severe for large $\Delta\theta$ 135 and the model is much harder to explain. 136 The `azimuthal equidistance projection <https://en.wikipedia.org/wiki/Azimuthal_equidistant_projection>`_ 137 also improves on the equirectangular projection by extending the range of 138 reasonable values for the $\Delta\theta$ range, with $\Delta\phi$ forming a 139 wedge that cuts to the opposite side of the sphere rather than cutting to the 140 pole. This projection has the nice property that distance from the center are 141 preserved, and that $\Delta\theta$ and $\Delta\phi$ act the same. 142 The `azimuthal equal area projection <https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection>`_ 143 is like the azimuthal equidistance projection, but it preserves area instead 144 of distance. It also has the same behaviour for $\Delta\theta$ and $\Delta\phi$. 145 The `Guyou projection <https://en.wikipedia.org/wiki/Guyou_hemisphere-in-a-square_projection>`_ 146 has an excellent balance with reasonable distortion in both $\Delta\theta$ 147 and $\Delta\phi$, as well as preserving small patches. However, it requires 148 considerably more computational overhead, and we have not yet derived the 149 formula for the distortion correction, measuring the degree of stretch at 150 the point $(\Delta\theta, \Delta\phi)$ on the map. 63 151 64 152 .. note:: 65 Note that the form factors for oriented particles are also performing 66 numerical integrations over one or more variables, so care should be taken, 67 especially with very large particles or more extreme aspect ratios. In such 68 cases results may not be accurate, particularly at very high Q, unless the model 69 has been specifically coded to use limiting forms of the scattering equations. 70 71 For best numerical results keep the $\theta$ distribution narrower than the $\phi$ 72 distribution. Thus for asymmetric particles, such as elliptical_cylinder, you may 73 need to reorder the sizes of the three axes to acheive the desired result. 74 This is due to the issues of mapping a rectangular distribution onto the 75 surface of a sphere. 153 Note that the form factors for oriented particles are performing 154 numerical integrations over one or more variables, so care should be 155 taken, especially with very large particles or more extreme aspect 156 ratios. In such cases results may not be accurate, particularly at very 157 high Q, unless the model has been specifically coded to use limiting 158 forms of the scattering equations. 76 159 77 Users can experiment with the values of *Npts* and *Nsigs*, the number of steps 78 used in the integration and the range spanned in number of standard deviations. 79 The standard deviation is entered in units of degrees. For a "rectangular" 80 distribution the full width should be $\pm \sqrt(3)$ ~ 1.73 standard deviations. 81 The new "uniform" distribution avoids this by letting you directly specify the 160 For best numerical results keep the $\theta$ distribution narrower than 161 the $\phi$ distribution. Thus for asymmetric particles, such as 162 elliptical_cylinder, you may need to reorder the sizes of the three axes 163 to acheive the desired result. This is due to the issues of mapping a 164 rectanglar distribution onto the surface of a sphere. 165 166 Users can experiment with the values of *Npts* and *Nsigs*, the number of steps 167 used in the integration and the range spanned in number of standard deviations. 168 The standard deviation is entered in units of degrees. For a "rectangular" 169 distribution the full width should be $\pm \sqrt(3)$ ~ 1.73 standard deviations. 170 The new "uniform" distribution avoids this by letting you directly specify the 82 171 half width. 83 172 84 The angular distributions will be truncated outside of the range -180 to +18085 degrees, so beware of using saying a broad Gaussian distribution with large value86 of *Nsigs*, as the array of *Npts* may be truncated to many fewer points than would 87 give a good integration,as well as becoming rather meaningless. (At some point 88 in the future the actual polydispersity arrays may be made available to the user 89 for inspection.)173 The angular distributions may be truncated outside of the range -180 to +180 174 degrees, so beware of using saying a broad Gaussian distribution with large 175 value of *Nsigs*, as the array of *Npts* may be truncated to many fewer 176 points than would give a good integration,as well as becoming rather 177 meaningless. (At some point in the future the actual dispersion arrays may be 178 made available to the user for inspection.) 90 179 91 180 Some more detailed technical notes are provided in the developer section of 92 181 this manual :ref:`orientation_developer` . 93 182 183 This definition of orientation is new to SasView 4.2. In earlier versions, 184 the orientation distribution appeared as a distribution of view angles. 185 This led to strange effects when $c$ was aligned with $z$, where changes 186 to the $\phi$ angle served only to rotate the shape about $c$, rather than 187 having a consistent interpretation as the pitch of the shape relative to 188 the flow field defining the reference orientation. Prior to SasView 4.1, 189 the reference orientation was defined using a Tait-Bryan convention, making 190 it difficult to control. Now, rotation in $\theta$ modifies the spacings 191 in the refraction pattern, and rotation in $\phi$ rotates it in the detector 192 plane. 193 194 94 195 *Document History* 95 196 96 197 | 2017-11-06 Richard Heenan 198 | 2017-12-20 Paul Kienzle -
doc/guide/plugin.rst
rc654160 r7e6bc45e 538 538 If the scattering is dependent on the orientation of the shape, then you 539 539 will need to include *orientation* parameters *theta*, *phi* and *psi* 540 at the end of the parameter table. Shape orientation uses *a*, *b* and *c* 541 axes, corresponding to the *x*, *y* and *z* axes in the laboratory coordinate 542 system, with *z* along the beam and *x*-*y* in the detector plane, with *x* 543 horizontal and *y* vertical. The *psi* parameter rotates the shape 544 about its *c* axis, the *theta* parameter then rotates the *c* axis toward 545 the *x* axis of the detector, then *phi* rotates the shape in the detector 546 plane. (Prior to these rotations, orientation dispersity will be applied 547 as roll-pitch-yaw, rotating *c*, then *b* then *a* in the shape coordinate 548 system.) A particular *qx*, *qy* point on the detector, then corresponds 549 to *qa*, *qb*, *qc* with respect to the shape. 550 551 The oriented C model is called as *Iqabc(qa, qb, qc, par1, par2, ...)* where 540 at the end of the parameter table. As described in the section 541 :ref:`orientation`, the individual $(q_x, q_y)$ points on the detector will 542 be rotated into $(q_a, q_b, q_c)$ points relative to the sample in its 543 canonical orientation with $a$-$b$-$c$ aligned with $x$-$y$-$z$ in the 544 laboratory frame and beam travelling along $-z$. 545 546 The oriented C model is called using *Iqabc(qa, qb, qc, par1, par2, ...)* where 552 547 *par1*, etc. are the parameters to the model. If the shape is rotationally 553 548 symmetric about *c* then *psi* is not needed, and the model is called -
explore/jitter.py
rff10479 r8cfb486 165 165 # constants in kernel_iq.c 166 166 'equirectangular', 'sinusoidal', 'guyou', 'azimuthal_equidistance', 167 'azimuthal_equal_area', 167 168 ] 168 169 def draw_mesh(ax, view, jitter, radius=1.2, n=11, dist='gaussian', -
sasmodels/autoc.py
r67cc0ff r15be191 4 4 from __future__ import print_function 5 5 6 import ast7 6 import inspect 8 from functools import reduce9 7 10 8 import numpy as np … … 95 93 constants[name] = obj 96 94 # Claim all constants are declared on line 1 97 snippets.append('#line 1 "%s" '%escaped_filename)98 snippets.append( define_constant(name, obj))95 snippets.append('#line 1 "%s"\n'%escaped_filename) 96 snippets.append(py2c.define_constant(name, obj)) 99 97 elif isinstance(obj, special.Gauss): 100 98 for var, value in zip(("N", "Z", "W"), (obj.n, obj.z, obj.w)): 101 99 var = "GAUSS_"+var 102 100 constants[var] = value 103 snippets.append('#line 1 "%s" '%escaped_filename)104 snippets.append( define_constant(var, value))101 snippets.append('#line 1 "%s"\n'%escaped_filename) 102 snippets.append(py2c.define_constant(var, value)) 105 103 #libs.append('lib/gauss%d.c'%obj.n) 106 104 source = (source.replace(name+'.n', 'GAUSS_N') … … 121 119 122 120 # translate source 123 ordered_code = [code[name] for name in ordered_dag(depends) if name in code]121 ordered_code = [code[name] for name in py2c.ordered_dag(depends) if name in code] 124 122 functions = py2c.translate(ordered_code, constants) 125 123 snippets.extend(functions) … … 127 125 # update model info 128 126 info.source = unique_libs 129 info.c_code = " \n".join(snippets)127 info.c_code = "".join(snippets) 130 128 info.Iq = info.Iqac = info.Iqabc = info.Iqxy = info.form_volume = None 131 132 def define_constant(name, value):133 if isinstance(value, int):134 parts = ["int ", name, " = ", "%d"%value, ";"]135 elif isinstance(value, float):136 parts = ["double ", name, " = ", "%.15g"%value, ";"]137 else:138 # extend constant arrays to a multiple of 4; not sure if this139 # is necessary, but some OpenCL targets broke if the number140 # of parameters in the parameter table was not a multiple of 4,141 # so do it for all constant arrays to be safe.142 if len(value)%4 != 0:143 value = list(value) + [0.]*(4 - len(value)%4)144 elements = ["%.15g"%v for v in value]145 parts = ["double ", name, "[]", " = ",146 "{\n ", ", ".join(elements), "\n};"]147 return "".join(parts)148 149 150 # Modified from the following:151 #152 # http://code.activestate.com/recipes/578272-topological-sort/153 # Copyright (C) 2012 Sam Denton154 # License: MIT155 def ordered_dag(dag):156 # type: (Dict[T, Set[T]]) -> Iterator[T]157 dag = dag.copy()158 159 # make leaves depend on the empty set160 leaves = reduce(set.union, dag.values()) - set(dag.keys())161 dag.update({node: set() for node in leaves})162 while True:163 leaves = set(node for node, links in dag.items() if not links)164 if not leaves:165 break166 for node in leaves:167 yield node168 dag = {node: (links-leaves)169 for node, links in dag.items() if node not in leaves}170 if dag:171 raise ValueError("Cyclic dependes exists amongst these items:\n%s"172 % ", ".join(str(node) for node in dag.keys())) -
sasmodels/modelinfo.py
r67cc0ff r5ab99b7 794 794 info.structure_factor = getattr(kernel_module, 'structure_factor', False) 795 795 info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y']) 796 info.c_code = getattr(kernel_module, 'c_code', None)797 796 info.source = getattr(kernel_module, 'source', []) 798 797 info.c_code = getattr(kernel_module, 'c_code', None) … … 810 809 info.sesans = getattr(kernel_module, 'sesans', None) # type: ignore 811 810 # Default single and opencl to True for C models. Python models have callable Iq. 812 info.opencl = getattr(kernel_module, 'opencl', not callable(info.Iq))813 info.single = getattr(kernel_module, 'single', not callable(info.Iq))814 811 info.random = getattr(kernel_module, 'random', None) 815 812 … … 827 824 except Exception as exc: 828 825 logger.warn(str(exc) + " while converting %s from C to python"%name) 826 827 # Needs to come after autoc.convert since the Iq symbol may have been 828 # converted from python to C 829 info.opencl = getattr(kernel_module, 'opencl', not callable(info.Iq)) 830 info.single = getattr(kernel_module, 'single', not callable(info.Iq)) 829 831 830 832 if callable(info.Iq) and parameters.has_2d: -
sasmodels/models/_cylpy.py
r67cc0ff rc01ed3e 140 140 py2c = True 141 141 142 # TODO: "#define INVALID (expr)" is not supported 142 143 def invalid(v): 143 144 return v.radius < 0 or v.length < 0 … … 206 207 phi_pd=10, phi_pd_n=5) 207 208 208 qx, qy = 0.2 * np.cos(2.5), 0.2 * np.sin(2.5)209 qx, qy = 0.2 * cos(2.5), 0.2 * sin(2.5) 209 210 # After redefinition of angles, find new tests values. Was 10 10 in old coords 210 211 tests = [ -
sasmodels/py2c.py
r0bd0877 r0bd0877 1 """ 2 py2c 3 ~~~~ 4 5 Convert simple numeric python code into C code. 6 7 The translate() function works on 8 9 Variables definition in C 10 ------------------------- 11 Defining variables within the translate function is a bit of a guess work, 12 using following rules: 13 * By default, a variable is a 'double'. 14 * Variable in a for loop is an int. 15 * Variable that is references with brackets is an array of doubles. The 16 variable within the brackets is integer. For example, in the 17 reference 'var1[var2]', var1 is a double array, and var2 is an integer. 18 * Assignment to an argument makes that argument an array, and the index 19 in that assignment is 0. 20 For example, the following python code:: 21 def func(arg1, arg2): 22 arg2 = 17. 23 is translated to the following C code:: 24 double func(double arg1) 25 { 26 arg2[0] = 17.0; 27 } 28 For example, the following python code is translated to the 29 following C code:: 30 31 def func(arg1, arg2): double func(double arg1) { 32 arg2 = 17. arg2[0] = 17.0; 33 } 34 * All functions are defined as double, even if there is no 35 return statement. 36 37 Based on codegen.py: 38 1 r""" 2 py2c 3 ~~~~ 4 5 Convert simple numeric python code into C code. 6 7 This code is intended to translate direct algorithms for scientific code 8 (mostly if statements and for loops operating on double precision values) 9 into C code. Unlike projects like numba, cython, pypy and nuitka, the 10 :func:`translate` function returns the corresponding C which can then be 11 compiled with tinycc or sent to the GPU using CUDA or OpenCL. 12 13 There is special handling certain constructs, such as *for i in range* and 14 small integer powers. 15 16 **TODO: make a nice list of supported constructs*** 17 18 Imports are not supported, but they are at least ignored so that properly 19 constructed code can be run via python or translated to C without change. 20 21 Most other python constructs are **not** supported: 22 * classes 23 * builtin types (dict, set, list) 24 * exceptions 25 * with context 26 * del 27 * yield 28 * async 29 * list slicing 30 * multiple return values 31 * "is/is not", "in/not in" conditionals 32 33 There is limited support for list and list comprehensions, so long as they 34 can be represented by a fixed array whose size is known at compile time, and 35 they are small enough to be stored on the stack. 36 37 Variables definition in C 38 ------------------------- 39 Defining variables within the translate function is a bit of a guess work, 40 using following rules: 41 * By default, a variable is a 'double'. 42 * Variable in a for loop is an int. 43 * Variable that is references with brackets is an array of doubles. The 44 variable within the brackets is integer. For example, in the 45 reference 'var1[var2]', var1 is a double array, and var2 is an integer. 46 * Assignment to an argument makes that argument an array, and the index 47 in that assignment is 0. 48 For example, the following python code:: 49 def func(arg1, arg2): 50 arg2 = 17. 51 is translated to the following C code:: 52 double func(double arg1) 53 { 54 arg2[0] = 17.0; 55 } 56 For example, the following python code is translated to the 57 following C code:: 58 59 def func(arg1, arg2): double func(double arg1) { 60 arg2 = 17. arg2[0] = 17.0; 61 } 62 * All functions are defined as double, even if there is no 63 return statement. 64 65 Debugging 66 --------- 67 68 *print* is partially supported using a simple regular expression. This 69 requires a stylized form. Be sure to use print as a function instead of 70 the print statement. If you are including substition variables, use the 71 % string substitution style. Include parentheses around the substitution 72 tuple, even if there is only one item; do not include the final comma even 73 if it is a single item (yes, it won't be a tuple, but it makes the regexp 74 much simpler). Keep the item on a single line. Here are three forms that work:: 75 76 print("x") => printf("x\n"); 77 print("x %g"%(a)) => printf("x %g\n", a); 78 print("x %g %g %g"%(a, b, c)) => printf("x %g %g %g\n", a, b, c); 79 80 You can generate *main* using the *if __name__ == "__main__":* construct. 81 This does a simple substitution with "def main():" before translation and 82 a substitution with "int main(int argc, double *argv[])" after translation. 83 The result is that the content of the *if* block becomes the content of *main*. 84 Along with the print statement, you can run and test a translation standalone 85 using:: 86 87 python py2c.py source.py 88 cc source.c 89 ./a.out 90 91 Known issues 92 ------------ 93 The following constructs may cause problems: 94 95 * implicit arrays: possible namespace collision for variable "vec#" 96 * swap fails: "x,y = y,x" will set x==y 97 * top-level statements: code outside a function body causes errors 98 * line number skew: each statement should be tagged with its own #line 99 to avoid skew as comments are skipped and loop bodies are wrapped with 100 braces, etc. 101 102 References 103 ---------- 104 105 Based on a variant of codegen.py: 106 107 https://github.com/andreif/codegen 39 108 :copyright: Copyright 2008 by Armin Ronacher. 40 109 :license: BSD. 41 110 """ 42 """ 43 Update Notes44 ============45 11/22/2017, O.E.Each 'visit_*' method is to build a C statement string. It46 shold insert 4 blanks per indentation level. 47 The 'body' method will combine all the strings, by adding 48 the'current_statement' to the c_proc string list49 11/2017, OE: variables, argument definition implemented. 50 Note: Anargument is considered an array if it is the target of an51 assignment. In that case it is translated to <var>[0]52 11/27/2017, OE: 'pow' basicly working53 /12/2017, OE: Multiple assignment: a1,a2,...,an=b1,b2,...bn implemented54 /12/2017, OE: Power function, including special cases of55 square(x)(pow(x,2)) and cube(x)(pow(x,3)), implemented in56 translate_power, called from visit_BinOp57 12/07/2017, OE: Translation of integer division, '\\' in python, implemented58 in translate_integer_divide, called from visit_BinOp59 12/07/2017, OE: C variable definition handled in 'define_c_vars'60 : Python integer division, '//', translated to C in61 'translate_integer_divide'62 12/15/2017, OE: Precedence maintained by writing opening and closing63 parenthesesm '(',')', in procedure 'visit_BinOp'.64 12/18/2017, OE: Added call to 'add_current_line()' at the beginning65 of visit_Return66 01/4/2018, O.E. Added functions 'get_files_names()', , 'print_usage()'. The get_files_names() 67 function retrieves the in/out file names form the command line, and returns 68 true/false if the number of parameters is valid. In case of no input 69 parameters, usage is prompt and the program terminates. 70 01/4/2018, O.E. 'translate(functions, constants=None)' returns string, instaed of a list 71 01/04/2017 O.E. Fixed bug in 'visit_If': visiting node.orelse in case else exists. 72 01/05/2018 O.E. New class C_Vector defined. It holds name, type, size and initial values. 73 Assignment to that class in 'sequence_visit'.74 Reading and inserting declaration in 'insert_c_vars' 75 76 77 """ 111 112 # Update Notes 113 # ============ 114 # 2017-11-22, OE: Each 'visit_*' method is to build a C statement string. It 115 # shold insert 4 blanks per indentation level. The 'body' 116 # method will combine all the strings, by adding the 117 # 'current_statement' to the c_proc string list 118 # 2017-11-22, OE: variables, argument definition implemented. Note: An 119 # argument is considered an array if it is the target of an 120 # assignment. In that case it is translated to <var>[0] 121 # 2017-11-27, OE: 'pow' basicly working 122 # 2017-12-07, OE: Multiple assignment: a1,a2,...,an=b1,b2,...bn implemented 123 # 2017-12-07, OE: Power function, including special cases of 124 # square(x)(pow(x,2)) and cube(x)(pow(x,3)), implemented in 125 # translate_power, called from visit_BinOp 126 # 2017-12-07, OE: Translation of integer division, '\\' in python, implemented 127 # in translate_integer_divide, called from visit_BinOp 128 # 2017-12-07, OE: C variable definition handled in 'define_c_vars' 129 # : Python integer division, '//', translated to C in 130 # 'translate_integer_divide' 131 # 2017-12-15, OE: Precedence maintained by writing opening and closing 132 # parenthesesm '(',')', in procedure 'visit_BinOp'. 133 # 2017-12-18, OE: Added call to 'add_current_line()' at the beginning 134 # of visit_Return 135 # 2018-01-03, PK: Update interface for use in sasmodels 136 # 2018-01-03, PK: support "expr if cond else expr" syntax 137 # 2018-01-03, PK: x//y => (int)((x)/(y)) and x/y => ((double)(x)/(double)(y)) 138 # 2018-01-03, PK: True/False => true/false 139 # 2018-01-03, PK: f(x) was introducing an extra semicolon 140 # 2018-01-03, PK: simplistic print function, for debugging 141 # 2018-01-03, PK: while expr: ... => while (expr) { ... } 142 # 2018-01-04, OE: Fixed bug in 'visit_If': visiting node.orelse in case else exists. 143 144 from __future__ import print_function 145 146 import sys 78 147 import ast 79 import sys80 148 from ast import NodeVisitor 81 149 … … 117 185 118 186 119 def to_source(tree, constants=None, fname=None, lineno=0): 120 """ 121 This function can convert a syntax tree into C sourcecode. 122 """ 123 generator = SourceGenerator(constants=constants, fname=fname, lineno=lineno) 124 generator.visit(tree) 125 # c_code = "\n".join(generator.c_proc) 126 c_code = "".join(generator.c_proc) 127 return (c_code, generator.warnings) 128 187 # TODO: should not allow eval of arbitrary python 129 188 def isevaluable(s): 130 189 try: … … 133 192 except Exception: 134 193 return False 135 136 class C_Vector():137 def __init__(self):138 self.size = 0139 self.type = "double"140 self.values = []141 self.assign_line = -1142 self.name = ""143 144 def get_leading_space(self, indent_with=" ", indent_level = 1):145 leading_space = ""146 for n in range(indent_level):147 leading_space += indent_with148 return leading_space149 150 def item_declare_string(self, indent_with=" ", indent_level = 1):151 declare_string = self.name + "[" + str(len(self.values)) + "]"152 return declare_string153 154 def declare_string(self, indent_with=" ", indent_level = 1):155 declare_string = self.get_leading_space(indent_with, indent_level)156 declare_string += self.type + " " + item_declare_string(indent_with, indent_level)157 # self.name + "[" + str(len(self.values)) + "];\n"158 return declare_string159 160 def get_assign(self, indent_with=" ", indent_level = 1):161 assign_loop = []162 c_string = self.get_leading_space(indent_with, indent_level - 1)163 c_string += "for (n=0 ; n < " + str(len(self.values)) + " ; n++) {"164 assign_loop.append(c_string)165 for n,value in enumerate(self.values):166 c_string = self.get_leading_space(indent_with, indent_level)167 c_string += self.name + "[" + str(n) + "] = " + str(value) + ";"168 assign_loop.append(c_string)169 c_string = self.get_leading_space(indent_with, indent_level - 1) + "}"170 assign_loop.append(c_string)171 return (assign_loop)172 194 173 195 class SourceGenerator(NodeVisitor): … … 203 225 self.c_vectors = [] 204 226 self.c_constants = constants if constants is not None else {} 227 self.in_expr = False 205 228 self.in_subref = False 206 229 self.in_subscript = False … … 209 232 self.is_sequence = False 210 233 self.visited_args = False 211 self.inside_if = False212 self.C_Vectors = []213 self.assign_target = ""214 self.assign_c_vector = []215 234 216 235 def write_python(self, x): … … 248 267 self.add_unique_var(arg.id) 249 268 250 def write_print(self, node):251 self.write_c ("printf (")252 c_str = self.current_statement253 self.current_statement = ""254 self.visit(node.args[0])255 print_params = self.current_statement # save print agruments256 self.current_statement = c_str257 print_params = print_params[1:(len(print_params) - 1)] # remove parentheses258 prcnt = print_params.rfind('%')259 if prcnt >= 0:260 format_string = print_params[0:prcnt-1].strip()261 format_string = format_string[1:len(format_string) - 1]262 var_string = print_params[prcnt+1:len(print_params)].strip()263 else:264 format_string = print_params265 var_string = ""266 format_string= format_string[:len(format_string)]267 self.write_c('"' + format_string + '\\n"')268 if (len(var_string) > 0):269 self.write_c(', ' + var_string)270 self.write_c(");")271 self.add_current_line()272 273 274 275 269 def newline(self, node=None, extra=0): 276 270 self.new_lines = max(self.new_lines, 1 + extra) 277 271 if node is not None and self.add_line_information: 278 self.write_c(' #line: %s' % node.lineno)272 self.write_c('// line: %s' % node.lineno) 279 273 self.new_lines = 1 280 274 if self.current_statement: … … 297 291 self.body(node.body) 298 292 if node.orelse: 293 self.unsupported(node, "for...else/while...else not supported") 294 299 295 self.newline() 300 296 self.write_c('else:') … … 326 322 except AttributeError: 327 323 arg_name = arg.id 328 w_str = (" Default Parameters are unknown to C: '%s = %s" \329 % (arg_name, str(default.n)))324 w_str = ("C does not support default parameters: %s=%s" 325 % (arg_name, str(default.n))) 330 326 self.warnings.append(w_str) 331 327 … … 366 362 367 363 def add_semi_colon(self): 368 semi_pos = self.current_statement.find(';')369 if semi_pos > 0.0:370 self.current_statement = self.current_statement.replace(';', '')364 #semi_pos = self.current_statement.find(';') 365 #if semi_pos >= 0: 366 # self.current_statement = self.current_statement.replace(';', '') 371 367 self.write_c(';') 372 368 373 369 def visit_Assign(self, node): 374 370 self.add_current_line() 371 self.in_expr = True 375 372 for idx, target in enumerate(node.targets): # multi assign, as in 'a = b = c = 7' 376 373 if idx: … … 378 375 self.define_c_vars(target) 379 376 self.visit(target) 380 if hasattr(target,'id'):381 self.assign_target = target.id382 377 # Capture assigned tuple names, if any 383 378 targets = self.tuples[:] … … 387 382 self.visited_args = False 388 383 self.visit(node.value) 389 if len(self.assign_c_vector): 390 for c_statement in self.assign_c_vector: 391 self.add_c_line (c_statement) 392 # self.c_proc.append(c_statement) 393 self.assign_c_vector.clear() 394 else: 384 self.add_semi_colon() 385 self.add_current_line() 386 # Assign tuples to tuples, if any 387 # TODO: doesn't handle swap: a,b = b,a 388 for target, item in zip(targets, self.tuples): 389 self.visit(target) 390 self.write_c(' = ') 391 self.visit(item) 395 392 self.add_semi_colon() 396 393 self.add_current_line() 397 # Assign tuples to tuples, if any 398 # TODO: doesn't handle swap: a,b = b,a 399 for target, item in zip(targets, self.tuples): 400 self.visit(target) 401 self.write_c(' = ') 402 self.visit(item) 403 self.add_semi_colon() 404 self.add_current_line() 405 if self.is_sequence and not self.visited_args: 406 for target in node.targets: 407 if hasattr(target, 'id'): 408 if target.id in self.c_vars and target.id not in self.c_dcl_pointers: 409 if target.id not in self.c_dcl_pointers: 410 self.c_dcl_pointers.append(target.id) 411 if target.id in self.c_vars: 412 self.c_vars.remove(target.id) 394 if self.is_sequence and not self.visited_args: 395 for target in node.targets: 396 if hasattr(target, 'id'): 397 if target.id in self.c_vars and target.id not in self.c_dcl_pointers: 398 if target.id not in self.c_dcl_pointers: 399 self.c_dcl_pointers.append(target.id) 400 if target.id in self.c_vars: 401 self.c_vars.remove(target.id) 413 402 self.current_statement = '' 414 self. assign_target = ''403 self.in_expr = False 415 404 416 405 def visit_AugAssign(self, node): … … 418 407 if node.target.id not in self.arguments: 419 408 self.c_vars.append(node.target.id) 409 self.in_expr = True 420 410 self.visit(node.target) 421 411 self.write_c(' ' + BINOP_SYMBOLS[type(node.op)] + '= ') 422 412 self.visit(node.value) 423 413 self.add_semi_colon() 414 self.in_expr = False 424 415 self.add_current_line() 425 416 … … 441 432 442 433 def visit_Expr(self, node): 434 #self.in_expr = True 443 435 self.newline(node) 444 436 self.generic_visit(node) 437 #self.in_expr = False 445 438 446 439 def write_c_pointers(self, start_var): … … 469 462 have_decls = True 470 463 start_var += 1 471 if len(self.C_Vectors) > 0:472 vec_declare = []473 for c_vec in self.C_Vectors:474 item_declare = c_vec.item_declare_string()475 if item_declare not in vec_declare:476 vec_declare.append (item_declare)477 if c_vec.name in self.c_vars:478 self.c_vars.remove(c_vec.name)479 all_declare = " double " + ", ".join (vec_declare) + ";\n"480 self.c_proc.insert(start_var, all_declare)481 start_var += 1482 464 483 465 if self.c_vars: … … 519 501 self.current_function = node.name 520 502 503 # remember the location of the next warning that will be inserted 504 # so that we can stuff the function name ahead of the warning list 505 # if any warnings are generated by the function. 521 506 warning_index = len(self.warnings) 507 522 508 self.newline(extra=1) 523 509 self.decorators(node) … … 533 519 self.insert_signature() 534 520 self.insert_c_vars(start_vars) 535 # self.c_pointers = [] 536 self.c_pointers.clear() 537 self.C_Vectors.clear() 521 del self.c_pointers[:] 538 522 self.current_function = "" 539 if (warning_index != len(self.warnings)):523 if warning_index != len(self.warnings): 540 524 self.warnings.insert(warning_index, "Warning in function '" + node.name + "':") 541 self.warnings.append("Note: C compilation will fail")542 543 525 544 526 def visit_ClassDef(self, node): … … 576 558 577 559 def visit_If(self, node): 578 self.add_current_line() 579 self.inside_if = True 560 580 561 self.write_c('if ') 562 self.in_expr = True 581 563 self.visit(node.test) 564 self.in_expr = False 582 565 self.write_c(' {') 583 566 self.body(node.body) … … 592 575 #self.newline() 593 576 self.write_c('else if ') 577 self.in_expr = True 594 578 self.visit(node.test) 579 self.in_expr = False 595 580 self.write_c(' {') 596 581 self.body(node.body) … … 601 586 self.newline() 602 587 self.write_c('else {') 603 self.body( node.orelse)588 self.body(else_) 604 589 self.add_c_line('}') 605 590 break 606 self.inside_if = False607 591 608 592 def get_for_range(self, node): … … 631 615 return start, stop, step 632 616 633 def add_c_int_var (self, iterator):634 if iteratornot in self.c_int_vars:635 self.c_int_vars.append( iterator)617 def add_c_int_var(self, name): 618 if name not in self.c_int_vars: 619 self.c_int_vars.append(name) 636 620 637 621 def visit_For(self, node): … … 647 631 iterator = self.current_statement 648 632 self.current_statement = '' 649 self.add_c_int_var 633 self.add_c_int_var(iterator) 650 634 start, stop, step = self.get_for_range(node) 651 self.write_c("for (" + iterator + "=" + str(start) +635 self.write_c("for (" + iterator + "=" + str(start) + 652 636 " ; " + iterator + " < " + str(stop) + 653 637 " ; " + iterator + " += " + str(step) + ") {") … … 664 648 self.write_c(':') 665 649 # report the error 666 self.unsupported( "unsupported " + self.current_statement)650 self.unsupported(node, "unsupported " + self.current_statement) 667 651 668 652 def visit_While(self, node): … … 670 654 self.write_c('while ') 671 655 self.visit(node.test) 672 self.write_c(' { 656 self.write_c(' {') 673 657 self.body_or_else(node) 674 658 self.write_c('}') … … 677 661 def visit_With(self, node): 678 662 self.unsupported(node) 663 679 664 self.newline(node) 680 665 self.write_python('with ') … … 691 676 692 677 def visit_Print(self, node): 693 # TODO: print support would be nice, though hard to do694 678 self.unsupported(node) 679 695 680 # CRUFT: python 2.6 only 696 681 self.newline(node) … … 711 696 def visit_Delete(self, node): 712 697 self.unsupported(node) 698 713 699 self.newline(node) 714 700 self.write_python('del ') … … 720 706 def visit_TryExcept(self, node): 721 707 self.unsupported(node) 708 722 709 self.newline(node) 723 710 self.write_python('try:') … … 728 715 def visit_TryFinally(self, node): 729 716 self.unsupported(node) 717 730 718 self.newline(node) 731 719 self.write_python('try:') … … 737 725 def visit_Global(self, node): 738 726 self.unsupported(node) 727 739 728 self.newline(node) 740 729 self.write_python('global ' + ', '.join(node.names)) … … 746 735 def visit_Return(self, node): 747 736 self.add_current_line() 737 self.in_expr = True 748 738 if node.value is None: 749 739 self.write_c('return') 750 740 else: 751 self.write_c('return (')741 self.write_c('return ') 752 742 self.visit(node.value) 753 self.write_c(')')754 743 self.add_semi_colon() 744 self.in_expr = False 755 745 self.add_c_line(self.current_statement) 756 746 self.current_statement = '' … … 766 756 def visit_Raise(self, node): 767 757 self.unsupported(node) 758 768 759 # CRUFT: Python 2.6 / 3.0 compatibility 769 760 self.newline(node) … … 788 779 def visit_Attribute(self, node): 789 780 self.unsupported(node, "attribute reference a.b not supported") 781 790 782 self.visit(node.value) 791 783 self.write_python('.' + node.attr) … … 807 799 elif node.func.id == "SINCOS": 808 800 self.write_sincos(node) 809 return810 elif node.func.id == "print":811 self.write_print(node)812 801 return 813 802 else: … … 835 824 self.visit(node.kwargs) 836 825 self.write_c(')') 837 if (self.inside_if == False): 838 self.write_c(';') 826 if not self.in_expr: 827 self.add_semi_colon() 828 829 TRANSLATE_CONSTANTS = { 830 # python 2 uses normal name references through vist_Name 831 'True': 'true', 832 'False': 'false', 833 'None': 'NULL', # "None" will probably fail for other reasons 834 # python 3 uses NameConstant 835 True: 'true', 836 False: 'false', 837 None: 'NULL', # "None" will probably fail for other reasons 838 } 839 839 840 840 def visit_Name(self, node): 841 translation = self.TRANSLATE_CONSTANTS.get(node.id, None) 842 if translation: 843 self.write_c(translation) 844 return 841 845 self.write_c(node.id) 842 846 if node.id in self.c_pointers and not self.in_subref: … … 853 857 name not in self.c_constants and not name.isdigit()): 854 858 if self.in_subscript: 855 self.add_c_int_var (self,node.id)859 self.add_c_int_var(node.id) 856 860 else: 857 861 self.c_vars.append(node.id) 858 862 863 def visit_NameConstant(self, node): 864 translation = self.TRANSLATE_CONSTANTS.get(node.value, None) 865 if translation is not None: 866 self.write_c(translation) 867 else: 868 self.unsupported(node, "don't know how to translate %r"%node.value) 869 859 870 def visit_Str(self, node): 860 self.write_c(repr(node.s)) 871 s = node.s 872 s = s.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n') 873 self.write_c('"') 874 self.write_c(s) 875 self.write_c('"') 861 876 862 877 def visit_Bytes(self, node): 863 self.write_c(repr(node.s)) 878 s = node.s 879 s = s.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n') 880 self.write_c('"') 881 self.write_c(s) 882 self.write_c('"') 864 883 865 884 def visit_Num(self, node): … … 876 895 def visit(self, node): 877 896 self.is_sequence = True 878 c_vec = C_Vector()879 897 s = "" 880 898 for idx, item in enumerate(node.elts): … … 882 900 s += ', ' 883 901 if hasattr(item, 'id'): 884 c_vec.values.append(item.id) 885 # s += item.id 902 s += item.id 886 903 elif hasattr(item, 'n'): 887 c_vec.values.append(item.n) 888 # s += str(item.n) 889 else: 890 temp = self.current_statement 891 self.current_statement = "" 892 self.visit(item) 893 c_vec.values.append(self.current_statement) 894 # s += self.current_statement 895 self.current_statement = temp 896 if (self.assign_target): 897 self.add_c_int_var ('n') 898 c_vec.name = self.assign_target 899 self.C_Vectors.append(c_vec) 900 assign_strings = c_vec.get_assign() 901 for assign_str in assign_strings: 902 self.assign_c_vector.append(assign_str) 903 # self.add_c_line(assign_str) 904 # if s: 905 # self.c_vectors.append(s) 906 # vec_name = "vec" + str(len(self.c_vectors)) 907 # self.write_c(vec_name) 904 s += str(item.n) 905 if s: 906 self.c_vectors.append(s) 907 vec_name = "vec" + str(len(self.c_vectors)) 908 self.write_c(vec_name) 908 909 return visit 909 910 … … 914 915 def visit_Dict(self, node): 915 916 self.unsupported(node) 917 916 918 self.write_python('{') 917 919 for idx, (key, value) in enumerate(zip(node.keys, node.values)): … … 964 966 if is_negative_exp: 965 967 self.write_c(")") 966 self.write_c(" ")968 #self.write_c(" ") 967 969 968 970 def translate_integer_divide(self, node): 969 self.write_c("(int)( ")971 self.write_c("(int)((") 970 972 self.visit(node.left) 971 self.write_c(") /(int)(")973 self.write_c(")/(") 972 974 self.visit(node.right) 973 self.write_c(")") 975 self.write_c("))") 976 977 def translate_float_divide(self, node): 978 self.write_c("((double)(") 979 self.visit(node.left) 980 self.write_c(")/(double)(") 981 self.visit(node.right) 982 self.write_c("))") 974 983 975 984 def visit_BinOp(self, node): … … 979 988 elif '%s' % BINOP_SYMBOLS[type(node.op)] == BINOP_SYMBOLS[ast.FloorDiv]: 980 989 self.translate_integer_divide(node) 990 elif '%s' % BINOP_SYMBOLS[type(node.op)] == BINOP_SYMBOLS[ast.Div]: 991 self.translate_float_divide(node) 981 992 else: 982 993 self.visit(node.left) … … 1043 1054 def visit_Yield(self, node): 1044 1055 self.unsupported(node) 1056 1045 1057 self.write_python('yield ') 1046 1058 self.visit(node.value) … … 1048 1060 def visit_Lambda(self, node): 1049 1061 self.unsupported(node) 1062 1050 1063 self.write_python('lambda ') 1051 1064 self.visit(node.args) … … 1055 1068 def visit_Ellipsis(self, node): 1056 1069 self.unsupported(node) 1070 1057 1071 self.write_python('Ellipsis') 1058 1072 … … 1075 1089 def visit_DictComp(self, node): 1076 1090 self.unsupported(node) 1091 1077 1092 self.write_python('{') 1078 1093 self.visit(node.key) … … 1083 1098 self.write_python('}') 1084 1099 1085 def visit_NameConstant (self, node):1086 if (hasattr (node, "value")):1087 if (node.value == True):1088 val = "1"1089 elif node.value == False:1090 val = "0"1091 else:1092 val = ""1093 if (len(val) > 0):1094 self.write_c("(" + val + ")")1095 1096 1100 def visit_IfExp(self, node): 1097 self.write_c('( ')1101 self.write_c('((') 1098 1102 self.visit(node.test) 1099 self.write_c(' ?')1103 self.write_c(')?(') 1100 1104 self.visit(node.body) 1101 self.write_c(' :')1105 self.write_c('):(') 1102 1106 self.visit(node.orelse) 1103 self.write_c(') ;')1107 self.write_c('))') 1104 1108 1105 1109 def visit_Starred(self, node): … … 1117 1121 def visit_alias(self, node): 1118 1122 self.unsupported(node) 1123 1119 1124 self.write_python(node.name) 1120 1125 if node.asname is not None: … … 1179 1184 const = "constant " # OpenCL needs globals to be constant 1180 1185 if isinstance(value, int): 1181 parts = [const + "int ", name, " = ", "%d"%value, "; "]1186 parts = [const + "int ", name, " = ", "%d"%value, ";\n"] 1182 1187 elif isinstance(value, float): 1183 parts = [const + "double ", name, " = ", "%.15g"%value, "; "]1188 parts = [const + "double ", name, " = ", "%.15g"%value, ";\n"] 1184 1189 else: 1185 1190 try: … … 1195 1200 elements = ["%.15g"%v for v in value] 1196 1201 parts = [const + "double ", name, "[]", " = ", 1197 "{\n ", ", ".join(elements), "\n}; "]1202 "{\n ", ", ".join(elements), "\n};\n"] 1198 1203 1199 1204 return "".join(parts) … … 1239 1244 % ", ".join(str(node) for node in dag.keys())) 1240 1245 1246 import re 1247 PRINT_ARGS = re.compile(r'print[(]"(?P<template>[^"]*)" *% *[(](?P<args>[^\n]*)[)] *[)] *\n') 1248 SUBST_ARGS = r'printf("\g<template>\\n", \g<args>)\n' 1249 PRINT_STR = re.compile(r'print[(]"(?P<template>[^"]*)" *[)] *\n') 1250 SUBST_STR = r'printf("\g<template>\n")' 1241 1251 def translate(functions, constants=None): 1242 # type: ( List[(str, str, int)], Dict[str, any]) -> List[str]1252 # type: (Sequence[(str, str, int)], Dict[str, any]) -> List[str] 1243 1253 """ 1244 Convert a set of functions 1254 Convert a list of functions to a list of C code strings. 1255 1256 Returns list of corresponding code snippets (with trailing lines in 1257 each block) and a list of warnings generated by the translator. 1258 1259 A function is given by the tuple (source, filename, line number). 1260 1261 Global constants are given in a dictionary of {name: value}. The 1262 constants are used for name space resolution and type inferencing. 1263 Constants are not translated by this code. Instead, call 1264 :func:`define_constant` with name and value, and maybe block_size 1265 if arrays need to be padded to the next block boundary. 1266 1267 Function prototypes are not generated. Use :func:`ordered_dag` 1268 to list the functions in reverse order of dependency before calling 1269 translate. [Maybe a future revision will return the function prototypes 1270 so that a suitable "*.h" file can be generated. 1245 1271 """ 1246 1272 snippets = [] 1247 snippets.append("#include <math.h>")1248 snippets.append("")1249 1273 warnings = [] 1250 1274 for source, fname, lineno in functions: 1251 line_directive = '#line %d "%s" '%(lineno, fname.replace('\\', '\\\\'))1275 line_directive = '#line %d "%s"\n'%(lineno, fname.replace('\\', '\\\\')) 1252 1276 snippets.append(line_directive) 1277 # Replace simple print function calls with printf statements 1278 source = PRINT_ARGS.sub(SUBST_ARGS, source) 1279 source = PRINT_STR.sub(SUBST_STR, source) 1253 1280 tree = ast.parse(source) 1254 c_code,w = to_source(tree,constants=constants, fname=fname, lineno=lineno)1255 if w:1256 warnings.append ("\n".join(w))1281 generator = SourceGenerator(constants=constants, fname=fname, lineno=lineno) 1282 generator.visit(tree) 1283 c_code = "".join(generator.c_proc) 1257 1284 snippets.append(c_code) 1258 return "\n".join(snippets),warnings 1259 # return snippets 1260 1261 def print_usage (): 1262 print("""\ 1263 Usage: python py2c.py <infile> [<outfile>] 1264 if outfile is omitted, output file is '<infile>.c' 1265 """) 1266 1267 def get_files_names (): 1268 import os 1269 fname_in = fname_out = "" 1270 valid_params = len(sys.argv) > 1 1271 if (valid_params == False): 1272 print_usage() 1273 else: 1274 fname_in = sys.argv[1] 1275 if len(sys.argv) == 2: 1276 fname_base = os.path.splitext(fname_in)[0] 1277 fname_out = str(fname_base) + '.c' 1278 else: 1279 fname_out = sys.argv[2] 1280 return valid_params, fname_in,fname_out 1285 warnings.extend(generator.warnings) 1286 return snippets, warnings 1287 1288 def to_source(tree, constants=None, fname=None, lineno=0): 1289 """ 1290 This function can convert a syntax tree into C sourcecode. 1291 """ 1292 c_code = "".join(generator.c_proc) 1293 return c_code 1294 1281 1295 1282 1296 C_HEADER = """ … … 1299 1313 } 1300 1314 """ 1315 1316 USAGE = """\ 1317 Usage: python py2c.py <infile> [<outfile>] 1318 1319 if outfile is omitted, output file is '<infile>.c' 1320 """ 1321 1301 1322 def main(): 1302 print("Parsing...using Python" + sys.version)1303 valid_params, fname_in, fname_out = get_files_names ()1304 if (valid_params == False):1305 print( "Input parameters error.\nExiting")1323 import os 1324 #print("Parsing...using Python" + sys.version) 1325 if len(sys.argv) == 1: 1326 print(USAGE) 1306 1327 return 1307 1328 1329 fname_in = sys.argv[1] 1330 if len(sys.argv) == 2: 1331 fname_base = os.path.splitext(fname_in)[0] 1332 fname_out = str(fname_base) + '.c' 1333 else: 1334 fname_out = sys.argv[2] 1335 1308 1336 with open(fname_in, "r") as python_file: 1309 1337 code = python_file.read() 1310 1338 name = "gauss" 1311 1339 code = (code … … 1315 1343 .replace('if __name__ == "__main__"', "def main()") 1316 1344 ) 1317 translation,warnings = translate([(code, fname_in, 1)]) 1318 translation = translation.replace("double main()", "int main(int argc, char *argv[])") 1345 1346 translation, warnings = translate([(code, fname_in, 1)]) 1347 c_code = "".join(translation) 1348 c_code = c_code.replace("double main()", "int main(int argc, char *argv[])") 1319 1349 1320 1350 with open(fname_out, "w") as file_out: 1321 1351 file_out.write(C_HEADER) 1322 file_out.write(str(translation)) 1352 file_out.write(c_code) 1353 1323 1354 if warnings: 1324 print(" ".join(str(warnings[0])))1325 print("...Done")1355 print("\n".join(warnings)) 1356 #print("...Done") 1326 1357 1327 1358 if __name__ == "__main__": 1328 try: 1329 main() 1330 except Exception as excp: 1331 print ("Error:\n" + str(excp.args)) 1359 main()
Note: See TracChangeset
for help on using the changeset viewer.