fortran_parser.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. from sympy.external import import_module
  2. lfortran = import_module('lfortran')
  3. if lfortran:
  4. from sympy.codegen.ast import (Variable, IntBaseType, FloatBaseType, String,
  5. Return, FunctionDefinition, Assignment)
  6. from sympy.core import Add, Mul, Integer, Float
  7. from sympy.core.symbol import Symbol
  8. asr_mod = lfortran.asr
  9. asr = lfortran.asr.asr
  10. src_to_ast = lfortran.ast.src_to_ast
  11. ast_to_asr = lfortran.semantic.ast_to_asr.ast_to_asr
  12. """
  13. This module contains all the necessary Classes and Function used to Parse
  14. Fortran code into SymPy expression
  15. The module and its API are currently under development and experimental.
  16. It is also dependent on LFortran for the ASR that is converted to SymPy syntax
  17. which is also under development.
  18. The module only supports the features currently supported by the LFortran ASR
  19. which will be updated as the development of LFortran and this module progresses
  20. You might find unexpected bugs and exceptions while using the module, feel free
  21. to report them to the SymPy Issue Tracker
  22. The API for the module might also change while in development if better and
  23. more effective ways are discovered for the process
  24. Features Supported
  25. ==================
  26. - Variable Declarations (integers and reals)
  27. - Function Definitions
  28. - Assignments and Basic Binary Operations
  29. Notes
  30. =====
  31. The module depends on an external dependency
  32. LFortran : Required to parse Fortran source code into ASR
  33. References
  34. ==========
  35. .. [1] https://github.com/sympy/sympy/issues
  36. .. [2] https://gitlab.com/lfortran/lfortran
  37. .. [3] https://docs.lfortran.org/
  38. """
  39. class ASR2PyVisitor(asr.ASTVisitor): # type: ignore
  40. """
  41. Visitor Class for LFortran ASR
  42. It is a Visitor class derived from asr.ASRVisitor which visits all the
  43. nodes of the LFortran ASR and creates corresponding AST node for each
  44. ASR node
  45. """
  46. def __init__(self):
  47. """Initialize the Parser"""
  48. self._py_ast = []
  49. def visit_TranslationUnit(self, node):
  50. """
  51. Function to visit all the elements of the Translation Unit
  52. created by LFortran ASR
  53. """
  54. for s in node.global_scope.symbols:
  55. sym = node.global_scope.symbols[s]
  56. self.visit(sym)
  57. for item in node.items:
  58. self.visit(item)
  59. def visit_Assignment(self, node):
  60. """Visitor Function for Assignment
  61. Visits each Assignment is the LFortran ASR and creates corresponding
  62. assignment for SymPy.
  63. Notes
  64. =====
  65. The function currently only supports variable assignment and binary
  66. operation assignments of varying multitudes. Any type of numberS or
  67. array is not supported.
  68. Raises
  69. ======
  70. NotImplementedError() when called for Numeric assignments or Arrays
  71. """
  72. # TODO: Arithmetic Assignment
  73. if isinstance(node.target, asr.Variable):
  74. target = node.target
  75. value = node.value
  76. if isinstance(value, asr.Variable):
  77. new_node = Assignment(
  78. Variable(
  79. target.name
  80. ),
  81. Variable(
  82. value.name
  83. )
  84. )
  85. elif (type(value) == asr.BinOp):
  86. exp_ast = call_visitor(value)
  87. for expr in exp_ast:
  88. new_node = Assignment(
  89. Variable(target.name),
  90. expr
  91. )
  92. else:
  93. raise NotImplementedError("Numeric assignments not supported")
  94. else:
  95. raise NotImplementedError("Arrays not supported")
  96. self._py_ast.append(new_node)
  97. def visit_BinOp(self, node):
  98. """Visitor Function for Binary Operations
  99. Visits each binary operation present in the LFortran ASR like addition,
  100. subtraction, multiplication, division and creates the corresponding
  101. operation node in SymPy's AST
  102. In case of more than one binary operations, the function calls the
  103. call_visitor() function on the child nodes of the binary operations
  104. recursively until all the operations have been processed.
  105. Notes
  106. =====
  107. The function currently only supports binary operations with Variables
  108. or other binary operations. Numerics are not supported as of yet.
  109. Raises
  110. ======
  111. NotImplementedError() when called for Numeric assignments
  112. """
  113. # TODO: Integer Binary Operations
  114. op = node.op
  115. lhs = node.left
  116. rhs = node.right
  117. if (type(lhs) == asr.Variable):
  118. left_value = Symbol(lhs.name)
  119. elif(type(lhs) == asr.BinOp):
  120. l_exp_ast = call_visitor(lhs)
  121. for exp in l_exp_ast:
  122. left_value = exp
  123. else:
  124. raise NotImplementedError("Numbers Currently not supported")
  125. if (type(rhs) == asr.Variable):
  126. right_value = Symbol(rhs.name)
  127. elif(type(rhs) == asr.BinOp):
  128. r_exp_ast = call_visitor(rhs)
  129. for exp in r_exp_ast:
  130. right_value = exp
  131. else:
  132. raise NotImplementedError("Numbers Currently not supported")
  133. if isinstance(op, asr.Add):
  134. new_node = Add(left_value, right_value)
  135. elif isinstance(op, asr.Sub):
  136. new_node = Add(left_value, -right_value)
  137. elif isinstance(op, asr.Div):
  138. new_node = Mul(left_value, 1/right_value)
  139. elif isinstance(op, asr.Mul):
  140. new_node = Mul(left_value, right_value)
  141. self._py_ast.append(new_node)
  142. def visit_Variable(self, node):
  143. """Visitor Function for Variable Declaration
  144. Visits each variable declaration present in the ASR and creates a
  145. Symbol declaration for each variable
  146. Notes
  147. =====
  148. The functions currently only support declaration of integer and
  149. real variables. Other data types are still under development.
  150. Raises
  151. ======
  152. NotImplementedError() when called for unsupported data types
  153. """
  154. if isinstance(node.type, asr.Integer):
  155. var_type = IntBaseType(String('integer'))
  156. value = Integer(0)
  157. elif isinstance(node.type, asr.Real):
  158. var_type = FloatBaseType(String('real'))
  159. value = Float(0.0)
  160. else:
  161. raise NotImplementedError("Data type not supported")
  162. if not (node.intent == 'in'):
  163. new_node = Variable(
  164. node.name
  165. ).as_Declaration(
  166. type = var_type,
  167. value = value
  168. )
  169. self._py_ast.append(new_node)
  170. def visit_Sequence(self, seq):
  171. """Visitor Function for code sequence
  172. Visits a code sequence/ block and calls the visitor function on all the
  173. children of the code block to create corresponding code in python
  174. """
  175. if seq is not None:
  176. for node in seq:
  177. self._py_ast.append(call_visitor(node))
  178. def visit_Num(self, node):
  179. """Visitor Function for Numbers in ASR
  180. This function is currently under development and will be updated
  181. with improvements in the LFortran ASR
  182. """
  183. # TODO:Numbers when the LFortran ASR is updated
  184. # self._py_ast.append(Integer(node.n))
  185. pass
  186. def visit_Function(self, node):
  187. """Visitor Function for function Definitions
  188. Visits each function definition present in the ASR and creates a
  189. function definition node in the Python AST with all the elements of the
  190. given function
  191. The functions declare all the variables required as SymPy symbols in
  192. the function before the function definition
  193. This function also the call_visior_function to parse the contents of
  194. the function body
  195. """
  196. # TODO: Return statement, variable declaration
  197. fn_args = [Variable(arg_iter.name) for arg_iter in node.args]
  198. fn_body = []
  199. fn_name = node.name
  200. for i in node.body:
  201. fn_ast = call_visitor(i)
  202. try:
  203. fn_body_expr = fn_ast
  204. except UnboundLocalError:
  205. fn_body_expr = []
  206. for sym in node.symtab.symbols:
  207. decl = call_visitor(node.symtab.symbols[sym])
  208. for symbols in decl:
  209. fn_body.append(symbols)
  210. for elem in fn_body_expr:
  211. fn_body.append(elem)
  212. fn_body.append(
  213. Return(
  214. Variable(
  215. node.return_var.name
  216. )
  217. )
  218. )
  219. if isinstance(node.return_var.type, asr.Integer):
  220. ret_type = IntBaseType(String('integer'))
  221. elif isinstance(node.return_var.type, asr.Real):
  222. ret_type = FloatBaseType(String('real'))
  223. else:
  224. raise NotImplementedError("Data type not supported")
  225. new_node = FunctionDefinition(
  226. return_type = ret_type,
  227. name = fn_name,
  228. parameters = fn_args,
  229. body = fn_body
  230. )
  231. self._py_ast.append(new_node)
  232. def ret_ast(self):
  233. """Returns the AST nodes"""
  234. return self._py_ast
  235. else:
  236. class ASR2PyVisitor(): # type: ignore
  237. def __init__(self, *args, **kwargs):
  238. raise ImportError('lfortran not available')
  239. def call_visitor(fort_node):
  240. """Calls the AST Visitor on the Module
  241. This function is used to call the AST visitor for a program or module
  242. It imports all the required modules and calls the visit() function
  243. on the given node
  244. Parameters
  245. ==========
  246. fort_node : LFortran ASR object
  247. Node for the operation for which the NodeVisitor is called
  248. Returns
  249. =======
  250. res_ast : list
  251. list of SymPy AST Nodes
  252. """
  253. v = ASR2PyVisitor()
  254. v.visit(fort_node)
  255. res_ast = v.ret_ast()
  256. return res_ast
  257. def src_to_sympy(src):
  258. """Wrapper function to convert the given Fortran source code to SymPy Expressions
  259. Parameters
  260. ==========
  261. src : string
  262. A string with the Fortran source code
  263. Returns
  264. =======
  265. py_src : string
  266. A string with the Python source code compatible with SymPy
  267. """
  268. a_ast = src_to_ast(src, translation_unit=False)
  269. a = ast_to_asr(a_ast)
  270. py_src = call_visitor(a)
  271. return py_src