sym_expr.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. from sympy.printing import pycode, ccode, fcode
  2. from sympy.external import import_module
  3. from sympy.utilities.decorator import doctest_depends_on
  4. lfortran = import_module('lfortran')
  5. cin = import_module('clang.cindex', import_kwargs = {'fromlist': ['cindex']})
  6. if lfortran:
  7. from sympy.parsing.fortran.fortran_parser import src_to_sympy
  8. if cin:
  9. from sympy.parsing.c.c_parser import parse_c
  10. @doctest_depends_on(modules=['lfortran', 'clang.cindex'])
  11. class SymPyExpression: # type: ignore
  12. """Class to store and handle SymPy expressions
  13. This class will hold SymPy Expressions and handle the API for the
  14. conversion to and from different languages.
  15. It works with the C and the Fortran Parser to generate SymPy expressions
  16. which are stored here and which can be converted to multiple language's
  17. source code.
  18. Notes
  19. =====
  20. The module and its API are currently under development and experimental
  21. and can be changed during development.
  22. The Fortran parser does not support numeric assignments, so all the
  23. variables have been Initialized to zero.
  24. The module also depends on external dependencies:
  25. - LFortran which is required to use the Fortran parser
  26. - Clang which is required for the C parser
  27. Examples
  28. ========
  29. Example of parsing C code:
  30. >>> from sympy.parsing.sym_expr import SymPyExpression
  31. >>> src = '''
  32. ... int a,b;
  33. ... float c = 2, d =4;
  34. ... '''
  35. >>> a = SymPyExpression(src, 'c')
  36. >>> a.return_expr()
  37. [Declaration(Variable(a, type=intc)),
  38. Declaration(Variable(b, type=intc)),
  39. Declaration(Variable(c, type=float32, value=2.0)),
  40. Declaration(Variable(d, type=float32, value=4.0))]
  41. An example of variable definiton:
  42. >>> from sympy.parsing.sym_expr import SymPyExpression
  43. >>> src2 = '''
  44. ... integer :: a, b, c, d
  45. ... real :: p, q, r, s
  46. ... '''
  47. >>> p = SymPyExpression()
  48. >>> p.convert_to_expr(src2, 'f')
  49. >>> p.convert_to_c()
  50. ['int a = 0', 'int b = 0', 'int c = 0', 'int d = 0', 'double p = 0.0', 'double q = 0.0', 'double r = 0.0', 'double s = 0.0']
  51. An example of Assignment:
  52. >>> from sympy.parsing.sym_expr import SymPyExpression
  53. >>> src3 = '''
  54. ... integer :: a, b, c, d, e
  55. ... d = a + b - c
  56. ... e = b * d + c * e / a
  57. ... '''
  58. >>> p = SymPyExpression(src3, 'f')
  59. >>> p.convert_to_python()
  60. ['a = 0', 'b = 0', 'c = 0', 'd = 0', 'e = 0', 'd = a + b - c', 'e = b*d + c*e/a']
  61. An example of function definition:
  62. >>> from sympy.parsing.sym_expr import SymPyExpression
  63. >>> src = '''
  64. ... integer function f(a,b)
  65. ... integer, intent(in) :: a, b
  66. ... integer :: r
  67. ... end function
  68. ... '''
  69. >>> a = SymPyExpression(src, 'f')
  70. >>> a.convert_to_python()
  71. ['def f(a, b):\\n f = 0\\n r = 0\\n return f']
  72. """
  73. def __init__(self, source_code = None, mode = None):
  74. """Constructor for SymPyExpression class"""
  75. super().__init__()
  76. if not(mode or source_code):
  77. self._expr = []
  78. elif mode:
  79. if source_code:
  80. if mode.lower() == 'f':
  81. if lfortran:
  82. self._expr = src_to_sympy(source_code)
  83. else:
  84. raise ImportError("LFortran is not installed, cannot parse Fortran code")
  85. elif mode.lower() == 'c':
  86. if cin:
  87. self._expr = parse_c(source_code)
  88. else:
  89. raise ImportError("Clang is not installed, cannot parse C code")
  90. else:
  91. raise NotImplementedError(
  92. 'Parser for specified language is not implemented'
  93. )
  94. else:
  95. raise ValueError('Source code not present')
  96. else:
  97. raise ValueError('Please specify a mode for conversion')
  98. def convert_to_expr(self, src_code, mode):
  99. """Converts the given source code to SymPy Expressions
  100. Attributes
  101. ==========
  102. src_code : String
  103. the source code or filename of the source code that is to be
  104. converted
  105. mode: String
  106. the mode to determine which parser is to be used according to
  107. the language of the source code
  108. f or F for Fortran
  109. c or C for C/C++
  110. Examples
  111. ========
  112. >>> from sympy.parsing.sym_expr import SymPyExpression
  113. >>> src3 = '''
  114. ... integer function f(a,b) result(r)
  115. ... integer, intent(in) :: a, b
  116. ... integer :: x
  117. ... r = a + b -x
  118. ... end function
  119. ... '''
  120. >>> p = SymPyExpression()
  121. >>> p.convert_to_expr(src3, 'f')
  122. >>> p.return_expr()
  123. [FunctionDefinition(integer, name=f, parameters=(Variable(a), Variable(b)), body=CodeBlock(
  124. Declaration(Variable(r, type=integer, value=0)),
  125. Declaration(Variable(x, type=integer, value=0)),
  126. Assignment(Variable(r), a + b - x),
  127. Return(Variable(r))
  128. ))]
  129. """
  130. if mode.lower() == 'f':
  131. if lfortran:
  132. self._expr = src_to_sympy(src_code)
  133. else:
  134. raise ImportError("LFortran is not installed, cannot parse Fortran code")
  135. elif mode.lower() == 'c':
  136. if cin:
  137. self._expr = parse_c(src_code)
  138. else:
  139. raise ImportError("Clang is not installed, cannot parse C code")
  140. else:
  141. raise NotImplementedError(
  142. "Parser for specified language has not been implemented"
  143. )
  144. def convert_to_python(self):
  145. """Returns a list with Python code for the SymPy expressions
  146. Examples
  147. ========
  148. >>> from sympy.parsing.sym_expr import SymPyExpression
  149. >>> src2 = '''
  150. ... integer :: a, b, c, d
  151. ... real :: p, q, r, s
  152. ... c = a/b
  153. ... d = c/a
  154. ... s = p/q
  155. ... r = q/p
  156. ... '''
  157. >>> p = SymPyExpression(src2, 'f')
  158. >>> p.convert_to_python()
  159. ['a = 0', 'b = 0', 'c = 0', 'd = 0', 'p = 0.0', 'q = 0.0', 'r = 0.0', 's = 0.0', 'c = a/b', 'd = c/a', 's = p/q', 'r = q/p']
  160. """
  161. self._pycode = []
  162. for iter in self._expr:
  163. self._pycode.append(pycode(iter))
  164. return self._pycode
  165. def convert_to_c(self):
  166. """Returns a list with the c source code for the SymPy expressions
  167. Examples
  168. ========
  169. >>> from sympy.parsing.sym_expr import SymPyExpression
  170. >>> src2 = '''
  171. ... integer :: a, b, c, d
  172. ... real :: p, q, r, s
  173. ... c = a/b
  174. ... d = c/a
  175. ... s = p/q
  176. ... r = q/p
  177. ... '''
  178. >>> p = SymPyExpression()
  179. >>> p.convert_to_expr(src2, 'f')
  180. >>> p.convert_to_c()
  181. ['int a = 0', 'int b = 0', 'int c = 0', 'int d = 0', 'double p = 0.0', 'double q = 0.0', 'double r = 0.0', 'double s = 0.0', 'c = a/b;', 'd = c/a;', 's = p/q;', 'r = q/p;']
  182. """
  183. self._ccode = []
  184. for iter in self._expr:
  185. self._ccode.append(ccode(iter))
  186. return self._ccode
  187. def convert_to_fortran(self):
  188. """Returns a list with the fortran source code for the SymPy expressions
  189. Examples
  190. ========
  191. >>> from sympy.parsing.sym_expr import SymPyExpression
  192. >>> src2 = '''
  193. ... integer :: a, b, c, d
  194. ... real :: p, q, r, s
  195. ... c = a/b
  196. ... d = c/a
  197. ... s = p/q
  198. ... r = q/p
  199. ... '''
  200. >>> p = SymPyExpression(src2, 'f')
  201. >>> p.convert_to_fortran()
  202. [' integer*4 a', ' integer*4 b', ' integer*4 c', ' integer*4 d', ' real*8 p', ' real*8 q', ' real*8 r', ' real*8 s', ' c = a/b', ' d = c/a', ' s = p/q', ' r = q/p']
  203. """
  204. self._fcode = []
  205. for iter in self._expr:
  206. self._fcode.append(fcode(iter))
  207. return self._fcode
  208. def return_expr(self):
  209. """Returns the expression list
  210. Examples
  211. ========
  212. >>> from sympy.parsing.sym_expr import SymPyExpression
  213. >>> src3 = '''
  214. ... integer function f(a,b)
  215. ... integer, intent(in) :: a, b
  216. ... integer :: r
  217. ... r = a+b
  218. ... f = r
  219. ... end function
  220. ... '''
  221. >>> p = SymPyExpression()
  222. >>> p.convert_to_expr(src3, 'f')
  223. >>> p.return_expr()
  224. [FunctionDefinition(integer, name=f, parameters=(Variable(a), Variable(b)), body=CodeBlock(
  225. Declaration(Variable(f, type=integer, value=0)),
  226. Declaration(Variable(r, type=integer, value=0)),
  227. Assignment(Variable(f), Variable(r)),
  228. Return(Variable(f))
  229. ))]
  230. """
  231. return self._expr