fortran.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. """
  2. Fortran code printer
  3. The FCodePrinter converts single SymPy expressions into single Fortran
  4. expressions, using the functions defined in the Fortran 77 standard where
  5. possible. Some useful pointers to Fortran can be found on wikipedia:
  6. https://en.wikipedia.org/wiki/Fortran
  7. Most of the code below is based on the "Professional Programmer\'s Guide to
  8. Fortran77" by Clive G. Page:
  9. https://www.star.le.ac.uk/~cgp/prof77.html
  10. Fortran is a case-insensitive language. This might cause trouble because
  11. SymPy is case sensitive. So, fcode adds underscores to variable names when
  12. it is necessary to make them different for Fortran.
  13. """
  14. from __future__ import annotations
  15. from typing import Any
  16. from collections import defaultdict
  17. from itertools import chain
  18. import string
  19. from sympy.codegen.ast import (
  20. Assignment, Declaration, Pointer, value_const,
  21. float32, float64, float80, complex64, complex128, int8, int16, int32,
  22. int64, intc, real, integer, bool_, complex_, none, stderr, stdout
  23. )
  24. from sympy.codegen.fnodes import (
  25. allocatable, isign, dsign, cmplx, merge, literal_dp, elemental, pure,
  26. intent_in, intent_out, intent_inout
  27. )
  28. from sympy.core import S, Add, N, Float, Symbol
  29. from sympy.core.function import Function
  30. from sympy.core.numbers import equal_valued
  31. from sympy.core.relational import Eq
  32. from sympy.sets import Range
  33. from sympy.printing.codeprinter import CodePrinter
  34. from sympy.printing.precedence import precedence, PRECEDENCE
  35. from sympy.printing.printer import printer_context
  36. # These are defined in the other file so we can avoid importing sympy.codegen
  37. # from the top-level 'import sympy'. Export them here as well.
  38. from sympy.printing.codeprinter import fcode, print_fcode # noqa:F401
  39. known_functions = {
  40. "sin": "sin",
  41. "cos": "cos",
  42. "tan": "tan",
  43. "asin": "asin",
  44. "acos": "acos",
  45. "atan": "atan",
  46. "atan2": "atan2",
  47. "sinh": "sinh",
  48. "cosh": "cosh",
  49. "tanh": "tanh",
  50. "log": "log",
  51. "exp": "exp",
  52. "erf": "erf",
  53. "Abs": "abs",
  54. "conjugate": "conjg",
  55. "Max": "max",
  56. "Min": "min",
  57. }
  58. class FCodePrinter(CodePrinter):
  59. """A printer to convert SymPy expressions to strings of Fortran code"""
  60. printmethod = "_fcode"
  61. language = "Fortran"
  62. type_aliases = {
  63. integer: int32,
  64. real: float64,
  65. complex_: complex128,
  66. }
  67. type_mappings = {
  68. intc: 'integer(c_int)',
  69. float32: 'real*4', # real(kind(0.e0))
  70. float64: 'real*8', # real(kind(0.d0))
  71. float80: 'real*10', # real(kind(????))
  72. complex64: 'complex*8',
  73. complex128: 'complex*16',
  74. int8: 'integer*1',
  75. int16: 'integer*2',
  76. int32: 'integer*4',
  77. int64: 'integer*8',
  78. bool_: 'logical'
  79. }
  80. type_modules = {
  81. intc: {'iso_c_binding': 'c_int'}
  82. }
  83. _default_settings: dict[str, Any] = dict(CodePrinter._default_settings, **{
  84. 'precision': 17,
  85. 'user_functions': {},
  86. 'source_format': 'fixed',
  87. 'contract': True,
  88. 'standard': 77,
  89. 'name_mangling': True,
  90. })
  91. _operators = {
  92. 'and': '.and.',
  93. 'or': '.or.',
  94. 'xor': '.neqv.',
  95. 'equivalent': '.eqv.',
  96. 'not': '.not. ',
  97. }
  98. _relationals = {
  99. '!=': '/=',
  100. }
  101. def __init__(self, settings=None):
  102. if not settings:
  103. settings = {}
  104. self.mangled_symbols = {} # Dict showing mapping of all words
  105. self.used_name = []
  106. self.type_aliases = dict(chain(self.type_aliases.items(),
  107. settings.pop('type_aliases', {}).items()))
  108. self.type_mappings = dict(chain(self.type_mappings.items(),
  109. settings.pop('type_mappings', {}).items()))
  110. super().__init__(settings)
  111. self.known_functions = dict(known_functions)
  112. userfuncs = settings.get('user_functions', {})
  113. self.known_functions.update(userfuncs)
  114. # leading columns depend on fixed or free format
  115. standards = {66, 77, 90, 95, 2003, 2008}
  116. if self._settings['standard'] not in standards:
  117. raise ValueError("Unknown Fortran standard: %s" % self._settings[
  118. 'standard'])
  119. self.module_uses = defaultdict(set) # e.g.: use iso_c_binding, only: c_int
  120. @property
  121. def _lead(self):
  122. if self._settings['source_format'] == 'fixed':
  123. return {'code': " ", 'cont': " @ ", 'comment': "C "}
  124. elif self._settings['source_format'] == 'free':
  125. return {'code': "", 'cont': " ", 'comment': "! "}
  126. else:
  127. raise ValueError("Unknown source format: %s" % self._settings['source_format'])
  128. def _print_Symbol(self, expr):
  129. if self._settings['name_mangling'] == True:
  130. if expr not in self.mangled_symbols:
  131. name = expr.name
  132. while name.lower() in self.used_name:
  133. name += '_'
  134. self.used_name.append(name.lower())
  135. if name == expr.name:
  136. self.mangled_symbols[expr] = expr
  137. else:
  138. self.mangled_symbols[expr] = Symbol(name)
  139. expr = expr.xreplace(self.mangled_symbols)
  140. name = super()._print_Symbol(expr)
  141. return name
  142. def _rate_index_position(self, p):
  143. return -p*5
  144. def _get_statement(self, codestring):
  145. return codestring
  146. def _get_comment(self, text):
  147. return "! {}".format(text)
  148. def _declare_number_const(self, name, value):
  149. return "parameter ({} = {})".format(name, self._print(value))
  150. def _print_NumberSymbol(self, expr):
  151. # A Number symbol that is not implemented here or with _printmethod
  152. # is registered and evaluated
  153. self._number_symbols.add((expr, Float(expr.evalf(self._settings['precision']))))
  154. return str(expr)
  155. def _format_code(self, lines):
  156. return self._wrap_fortran(self.indent_code(lines))
  157. def _traverse_matrix_indices(self, mat):
  158. rows, cols = mat.shape
  159. return ((i, j) for j in range(cols) for i in range(rows))
  160. def _get_loop_opening_ending(self, indices):
  161. open_lines = []
  162. close_lines = []
  163. for i in indices:
  164. # fortran arrays start at 1 and end at dimension
  165. var, start, stop = map(self._print,
  166. [i.label, i.lower + 1, i.upper + 1])
  167. open_lines.append("do %s = %s, %s" % (var, start, stop))
  168. close_lines.append("end do")
  169. return open_lines, close_lines
  170. def _print_sign(self, expr):
  171. from sympy.functions.elementary.complexes import Abs
  172. arg, = expr.args
  173. if arg.is_integer:
  174. new_expr = merge(0, isign(1, arg), Eq(arg, 0))
  175. elif (arg.is_complex or arg.is_infinite):
  176. new_expr = merge(cmplx(literal_dp(0), literal_dp(0)), arg/Abs(arg), Eq(Abs(arg), literal_dp(0)))
  177. else:
  178. new_expr = merge(literal_dp(0), dsign(literal_dp(1), arg), Eq(arg, literal_dp(0)))
  179. return self._print(new_expr)
  180. def _print_Piecewise(self, expr):
  181. if expr.args[-1].cond != True:
  182. # We need the last conditional to be a True, otherwise the resulting
  183. # function may not return a result.
  184. raise ValueError("All Piecewise expressions must contain an "
  185. "(expr, True) statement to be used as a default "
  186. "condition. Without one, the generated "
  187. "expression may not evaluate to anything under "
  188. "some condition.")
  189. lines = []
  190. if expr.has(Assignment):
  191. for i, (e, c) in enumerate(expr.args):
  192. if i == 0:
  193. lines.append("if (%s) then" % self._print(c))
  194. elif i == len(expr.args) - 1 and c == True:
  195. lines.append("else")
  196. else:
  197. lines.append("else if (%s) then" % self._print(c))
  198. lines.append(self._print(e))
  199. lines.append("end if")
  200. return "\n".join(lines)
  201. elif self._settings["standard"] >= 95:
  202. # Only supported in F95 and newer:
  203. # The piecewise was used in an expression, need to do inline
  204. # operators. This has the downside that inline operators will
  205. # not work for statements that span multiple lines (Matrix or
  206. # Indexed expressions).
  207. pattern = "merge({T}, {F}, {COND})"
  208. code = self._print(expr.args[-1].expr)
  209. terms = list(expr.args[:-1])
  210. while terms:
  211. e, c = terms.pop()
  212. expr = self._print(e)
  213. cond = self._print(c)
  214. code = pattern.format(T=expr, F=code, COND=cond)
  215. return code
  216. else:
  217. # `merge` is not supported prior to F95
  218. raise NotImplementedError("Using Piecewise as an expression using "
  219. "inline operators is not supported in "
  220. "standards earlier than Fortran95.")
  221. def _print_MatrixElement(self, expr):
  222. return "{}({}, {})".format(self.parenthesize(expr.parent,
  223. PRECEDENCE["Atom"], strict=True), expr.i + 1, expr.j + 1)
  224. def _print_Add(self, expr):
  225. # purpose: print complex numbers nicely in Fortran.
  226. # collect the purely real and purely imaginary parts:
  227. pure_real = []
  228. pure_imaginary = []
  229. mixed = []
  230. for arg in expr.args:
  231. if arg.is_number and arg.is_real:
  232. pure_real.append(arg)
  233. elif arg.is_number and arg.is_imaginary:
  234. pure_imaginary.append(arg)
  235. else:
  236. mixed.append(arg)
  237. if pure_imaginary:
  238. if mixed:
  239. PREC = precedence(expr)
  240. term = Add(*mixed)
  241. t = self._print(term)
  242. if t.startswith('-'):
  243. sign = "-"
  244. t = t[1:]
  245. else:
  246. sign = "+"
  247. if precedence(term) < PREC:
  248. t = "(%s)" % t
  249. return "cmplx(%s,%s) %s %s" % (
  250. self._print(Add(*pure_real)),
  251. self._print(-S.ImaginaryUnit*Add(*pure_imaginary)),
  252. sign, t,
  253. )
  254. else:
  255. return "cmplx(%s,%s)" % (
  256. self._print(Add(*pure_real)),
  257. self._print(-S.ImaginaryUnit*Add(*pure_imaginary)),
  258. )
  259. else:
  260. return CodePrinter._print_Add(self, expr)
  261. def _print_Function(self, expr):
  262. # All constant function args are evaluated as floats
  263. prec = self._settings['precision']
  264. args = [N(a, prec) for a in expr.args]
  265. eval_expr = expr.func(*args)
  266. if not isinstance(eval_expr, Function):
  267. return self._print(eval_expr)
  268. else:
  269. return CodePrinter._print_Function(self, expr.func(*args))
  270. def _print_Mod(self, expr):
  271. # NOTE : Fortran has the functions mod() and modulo(). modulo() behaves
  272. # the same wrt to the sign of the arguments as Python and SymPy's
  273. # modulus computations (% and Mod()) but is not available in Fortran 66
  274. # or Fortran 77, thus we raise an error.
  275. if self._settings['standard'] in [66, 77]:
  276. msg = ("Python % operator and SymPy's Mod() function are not "
  277. "supported by Fortran 66 or 77 standards.")
  278. raise NotImplementedError(msg)
  279. else:
  280. x, y = expr.args
  281. return " modulo({}, {})".format(self._print(x), self._print(y))
  282. def _print_ImaginaryUnit(self, expr):
  283. # purpose: print complex numbers nicely in Fortran.
  284. return "cmplx(0,1)"
  285. def _print_int(self, expr):
  286. return str(expr)
  287. def _print_Mul(self, expr):
  288. # purpose: print complex numbers nicely in Fortran.
  289. if expr.is_number and expr.is_imaginary:
  290. return "cmplx(0,%s)" % (
  291. self._print(-S.ImaginaryUnit*expr)
  292. )
  293. else:
  294. return CodePrinter._print_Mul(self, expr)
  295. def _print_Pow(self, expr):
  296. PREC = precedence(expr)
  297. if equal_valued(expr.exp, -1):
  298. return '%s/%s' % (
  299. self._print(literal_dp(1)),
  300. self.parenthesize(expr.base, PREC)
  301. )
  302. elif equal_valued(expr.exp, 0.5):
  303. if expr.base.is_integer:
  304. # Fortran intrinsic sqrt() does not accept integer argument
  305. if expr.base.is_Number:
  306. return 'sqrt(%s.0d0)' % self._print(expr.base)
  307. else:
  308. return 'sqrt(dble(%s))' % self._print(expr.base)
  309. else:
  310. return 'sqrt(%s)' % self._print(expr.base)
  311. else:
  312. return CodePrinter._print_Pow(self, expr)
  313. def _print_Rational(self, expr):
  314. p, q = int(expr.p), int(expr.q)
  315. return "%d.0d0/%d.0d0" % (p, q)
  316. def _print_Float(self, expr):
  317. printed = CodePrinter._print_Float(self, expr)
  318. e = printed.find('e')
  319. if e > -1:
  320. return "%sd%s" % (printed[:e], printed[e + 1:])
  321. return "%sd0" % printed
  322. def _print_Relational(self, expr):
  323. lhs_code = self._print(expr.lhs)
  324. rhs_code = self._print(expr.rhs)
  325. op = expr.rel_op
  326. op = op if op not in self._relationals else self._relationals[op]
  327. return "{} {} {}".format(lhs_code, op, rhs_code)
  328. def _print_Indexed(self, expr):
  329. inds = [ self._print(i) for i in expr.indices ]
  330. return "%s(%s)" % (self._print(expr.base.label), ", ".join(inds))
  331. def _print_AugmentedAssignment(self, expr):
  332. lhs_code = self._print(expr.lhs)
  333. rhs_code = self._print(expr.rhs)
  334. return self._get_statement("{0} = {0} {1} {2}".format(
  335. self._print(lhs_code), self._print(expr.binop), self._print(rhs_code)))
  336. def _print_sum_(self, sm):
  337. params = self._print(sm.array)
  338. if sm.dim != None: # Must use '!= None', cannot use 'is not None'
  339. params += ', ' + self._print(sm.dim)
  340. if sm.mask != None: # Must use '!= None', cannot use 'is not None'
  341. params += ', mask=' + self._print(sm.mask)
  342. return '%s(%s)' % (sm.__class__.__name__.rstrip('_'), params)
  343. def _print_product_(self, prod):
  344. return self._print_sum_(prod)
  345. def _print_Do(self, do):
  346. excl = ['concurrent']
  347. if do.step == 1:
  348. excl.append('step')
  349. step = ''
  350. else:
  351. step = ', {step}'
  352. return (
  353. 'do {concurrent}{counter} = {first}, {last}'+step+'\n'
  354. '{body}\n'
  355. 'end do\n'
  356. ).format(
  357. concurrent='concurrent ' if do.concurrent else '',
  358. **do.kwargs(apply=lambda arg: self._print(arg), exclude=excl)
  359. )
  360. def _print_ImpliedDoLoop(self, idl):
  361. step = '' if idl.step == 1 else ', {step}'
  362. return ('({expr}, {counter} = {first}, {last}'+step+')').format(
  363. **idl.kwargs(apply=lambda arg: self._print(arg))
  364. )
  365. def _print_For(self, expr):
  366. target = self._print(expr.target)
  367. if isinstance(expr.iterable, Range):
  368. start, stop, step = expr.iterable.args
  369. else:
  370. raise NotImplementedError("Only iterable currently supported is Range")
  371. body = self._print(expr.body)
  372. return ('do {target} = {start}, {stop}, {step}\n'
  373. '{body}\n'
  374. 'end do').format(target=target, start=start, stop=stop - 1,
  375. step=step, body=body)
  376. def _print_Type(self, type_):
  377. type_ = self.type_aliases.get(type_, type_)
  378. type_str = self.type_mappings.get(type_, type_.name)
  379. module_uses = self.type_modules.get(type_)
  380. if module_uses:
  381. for k, v in module_uses:
  382. self.module_uses[k].add(v)
  383. return type_str
  384. def _print_Element(self, elem):
  385. return '{symbol}({idxs})'.format(
  386. symbol=self._print(elem.symbol),
  387. idxs=', '.join((self._print(arg) for arg in elem.indices))
  388. )
  389. def _print_Extent(self, ext):
  390. return str(ext)
  391. def _print_Declaration(self, expr):
  392. var = expr.variable
  393. val = var.value
  394. dim = var.attr_params('dimension')
  395. intents = [intent in var.attrs for intent in (intent_in, intent_out, intent_inout)]
  396. if intents.count(True) == 0:
  397. intent = ''
  398. elif intents.count(True) == 1:
  399. intent = ', intent(%s)' % ['in', 'out', 'inout'][intents.index(True)]
  400. else:
  401. raise ValueError("Multiple intents specified for %s" % self)
  402. if isinstance(var, Pointer):
  403. raise NotImplementedError("Pointers are not available by default in Fortran.")
  404. if self._settings["standard"] >= 90:
  405. result = '{t}{vc}{dim}{intent}{alloc} :: {s}'.format(
  406. t=self._print(var.type),
  407. vc=', parameter' if value_const in var.attrs else '',
  408. dim=', dimension(%s)' % ', '.join((self._print(arg) for arg in dim)) if dim else '',
  409. intent=intent,
  410. alloc=', allocatable' if allocatable in var.attrs else '',
  411. s=self._print(var.symbol)
  412. )
  413. if val != None: # Must be "!= None", cannot be "is not None"
  414. result += ' = %s' % self._print(val)
  415. else:
  416. if value_const in var.attrs or val:
  417. raise NotImplementedError("F77 init./parameter statem. req. multiple lines.")
  418. result = ' '.join((self._print(arg) for arg in [var.type, var.symbol]))
  419. return result
  420. def _print_Infinity(self, expr):
  421. return '(huge(%s) + 1)' % self._print(literal_dp(0))
  422. def _print_While(self, expr):
  423. return 'do while ({condition})\n{body}\nend do'.format(**expr.kwargs(
  424. apply=lambda arg: self._print(arg)))
  425. def _print_BooleanTrue(self, expr):
  426. return '.true.'
  427. def _print_BooleanFalse(self, expr):
  428. return '.false.'
  429. def _pad_leading_columns(self, lines):
  430. result = []
  431. for line in lines:
  432. if line.startswith('!'):
  433. result.append(self._lead['comment'] + line[1:].lstrip())
  434. else:
  435. result.append(self._lead['code'] + line)
  436. return result
  437. def _wrap_fortran(self, lines):
  438. """Wrap long Fortran lines
  439. Argument:
  440. lines -- a list of lines (without \\n character)
  441. A comment line is split at white space. Code lines are split with a more
  442. complex rule to give nice results.
  443. """
  444. # routine to find split point in a code line
  445. my_alnum = set("_+-." + string.digits + string.ascii_letters)
  446. my_white = set(" \t()")
  447. def split_pos_code(line, endpos):
  448. if len(line) <= endpos:
  449. return len(line)
  450. pos = endpos
  451. split = lambda pos: \
  452. (line[pos] in my_alnum and line[pos - 1] not in my_alnum) or \
  453. (line[pos] not in my_alnum and line[pos - 1] in my_alnum) or \
  454. (line[pos] in my_white and line[pos - 1] not in my_white) or \
  455. (line[pos] not in my_white and line[pos - 1] in my_white)
  456. while not split(pos):
  457. pos -= 1
  458. if pos == 0:
  459. return endpos
  460. return pos
  461. # split line by line and add the split lines to result
  462. result = []
  463. if self._settings['source_format'] == 'free':
  464. trailing = ' &'
  465. else:
  466. trailing = ''
  467. for line in lines:
  468. if line.startswith(self._lead['comment']):
  469. # comment line
  470. if len(line) > 72:
  471. pos = line.rfind(" ", 6, 72)
  472. if pos == -1:
  473. pos = 72
  474. hunk = line[:pos]
  475. line = line[pos:].lstrip()
  476. result.append(hunk)
  477. while line:
  478. pos = line.rfind(" ", 0, 66)
  479. if pos == -1 or len(line) < 66:
  480. pos = 66
  481. hunk = line[:pos]
  482. line = line[pos:].lstrip()
  483. result.append("%s%s" % (self._lead['comment'], hunk))
  484. else:
  485. result.append(line)
  486. elif line.startswith(self._lead['code']):
  487. # code line
  488. pos = split_pos_code(line, 72)
  489. hunk = line[:pos].rstrip()
  490. line = line[pos:].lstrip()
  491. if line:
  492. hunk += trailing
  493. result.append(hunk)
  494. while line:
  495. pos = split_pos_code(line, 65)
  496. hunk = line[:pos].rstrip()
  497. line = line[pos:].lstrip()
  498. if line:
  499. hunk += trailing
  500. result.append("%s%s" % (self._lead['cont'], hunk))
  501. else:
  502. result.append(line)
  503. return result
  504. def indent_code(self, code):
  505. """Accepts a string of code or a list of code lines"""
  506. if isinstance(code, str):
  507. code_lines = self.indent_code(code.splitlines(True))
  508. return ''.join(code_lines)
  509. free = self._settings['source_format'] == 'free'
  510. code = [ line.lstrip(' \t') for line in code ]
  511. inc_keyword = ('do ', 'if(', 'if ', 'do\n', 'else', 'program', 'interface')
  512. dec_keyword = ('end do', 'enddo', 'end if', 'endif', 'else', 'end program', 'end interface')
  513. increase = [ int(any(map(line.startswith, inc_keyword)))
  514. for line in code ]
  515. decrease = [ int(any(map(line.startswith, dec_keyword)))
  516. for line in code ]
  517. continuation = [ int(any(map(line.endswith, ['&', '&\n'])))
  518. for line in code ]
  519. level = 0
  520. cont_padding = 0
  521. tabwidth = 3
  522. new_code = []
  523. for i, line in enumerate(code):
  524. if line in ('', '\n'):
  525. new_code.append(line)
  526. continue
  527. level -= decrease[i]
  528. if free:
  529. padding = " "*(level*tabwidth + cont_padding)
  530. else:
  531. padding = " "*level*tabwidth
  532. line = "%s%s" % (padding, line)
  533. if not free:
  534. line = self._pad_leading_columns([line])[0]
  535. new_code.append(line)
  536. if continuation[i]:
  537. cont_padding = 2*tabwidth
  538. else:
  539. cont_padding = 0
  540. level += increase[i]
  541. if not free:
  542. return self._wrap_fortran(new_code)
  543. return new_code
  544. def _print_GoTo(self, goto):
  545. if goto.expr: # computed goto
  546. return "go to ({labels}), {expr}".format(
  547. labels=', '.join((self._print(arg) for arg in goto.labels)),
  548. expr=self._print(goto.expr)
  549. )
  550. else:
  551. lbl, = goto.labels
  552. return "go to %s" % self._print(lbl)
  553. def _print_Program(self, prog):
  554. return (
  555. "program {name}\n"
  556. "{body}\n"
  557. "end program\n"
  558. ).format(**prog.kwargs(apply=lambda arg: self._print(arg)))
  559. def _print_Module(self, mod):
  560. return (
  561. "module {name}\n"
  562. "{declarations}\n"
  563. "\ncontains\n\n"
  564. "{definitions}\n"
  565. "end module\n"
  566. ).format(**mod.kwargs(apply=lambda arg: self._print(arg)))
  567. def _print_Stream(self, strm):
  568. if strm.name == 'stdout' and self._settings["standard"] >= 2003:
  569. self.module_uses['iso_c_binding'].add('stdint=>input_unit')
  570. return 'input_unit'
  571. elif strm.name == 'stderr' and self._settings["standard"] >= 2003:
  572. self.module_uses['iso_c_binding'].add('stdint=>error_unit')
  573. return 'error_unit'
  574. else:
  575. if strm.name == 'stdout':
  576. return '*'
  577. else:
  578. return strm.name
  579. def _print_Print(self, ps):
  580. if ps.format_string == none: # Must be '!= None', cannot be 'is not None'
  581. template = "print {fmt}, {iolist}"
  582. fmt = '*'
  583. else:
  584. template = 'write(%(out)s, fmt="{fmt}", advance="no"), {iolist}' % {
  585. 'out': {stderr: '0', stdout: '6'}.get(ps.file, '*')
  586. }
  587. fmt = self._print(ps.format_string)
  588. return template.format(fmt=fmt, iolist=', '.join(
  589. (self._print(arg) for arg in ps.print_args)))
  590. def _print_Return(self, rs):
  591. arg, = rs.args
  592. return "{result_name} = {arg}".format(
  593. result_name=self._context.get('result_name', 'sympy_result'),
  594. arg=self._print(arg)
  595. )
  596. def _print_FortranReturn(self, frs):
  597. arg, = frs.args
  598. if arg:
  599. return 'return %s' % self._print(arg)
  600. else:
  601. return 'return'
  602. def _head(self, entity, fp, **kwargs):
  603. bind_C_params = fp.attr_params('bind_C')
  604. if bind_C_params is None:
  605. bind = ''
  606. else:
  607. bind = ' bind(C, name="%s")' % bind_C_params[0] if bind_C_params else ' bind(C)'
  608. result_name = self._settings.get('result_name', None)
  609. return (
  610. "{entity}{name}({arg_names}){result}{bind}\n"
  611. "{arg_declarations}"
  612. ).format(
  613. entity=entity,
  614. name=self._print(fp.name),
  615. arg_names=', '.join([self._print(arg.symbol) for arg in fp.parameters]),
  616. result=(' result(%s)' % result_name) if result_name else '',
  617. bind=bind,
  618. arg_declarations='\n'.join((self._print(Declaration(arg)) for arg in fp.parameters))
  619. )
  620. def _print_FunctionPrototype(self, fp):
  621. entity = "{} function ".format(self._print(fp.return_type))
  622. return (
  623. "interface\n"
  624. "{function_head}\n"
  625. "end function\n"
  626. "end interface"
  627. ).format(function_head=self._head(entity, fp))
  628. def _print_FunctionDefinition(self, fd):
  629. if elemental in fd.attrs:
  630. prefix = 'elemental '
  631. elif pure in fd.attrs:
  632. prefix = 'pure '
  633. else:
  634. prefix = ''
  635. entity = "{} function ".format(self._print(fd.return_type))
  636. with printer_context(self, result_name=fd.name):
  637. return (
  638. "{prefix}{function_head}\n"
  639. "{body}\n"
  640. "end function\n"
  641. ).format(
  642. prefix=prefix,
  643. function_head=self._head(entity, fd),
  644. body=self._print(fd.body)
  645. )
  646. def _print_Subroutine(self, sub):
  647. return (
  648. '{subroutine_head}\n'
  649. '{body}\n'
  650. 'end subroutine\n'
  651. ).format(
  652. subroutine_head=self._head('subroutine ', sub),
  653. body=self._print(sub.body)
  654. )
  655. def _print_SubroutineCall(self, scall):
  656. return 'call {name}({args})'.format(
  657. name=self._print(scall.name),
  658. args=', '.join((self._print(arg) for arg in scall.subroutine_args))
  659. )
  660. def _print_use_rename(self, rnm):
  661. return "%s => %s" % tuple((self._print(arg) for arg in rnm.args))
  662. def _print_use(self, use):
  663. result = 'use %s' % self._print(use.namespace)
  664. if use.rename != None: # Must be '!= None', cannot be 'is not None'
  665. result += ', ' + ', '.join([self._print(rnm) for rnm in use.rename])
  666. if use.only != None: # Must be '!= None', cannot be 'is not None'
  667. result += ', only: ' + ', '.join([self._print(nly) for nly in use.only])
  668. return result
  669. def _print_BreakToken(self, _):
  670. return 'exit'
  671. def _print_ContinueToken(self, _):
  672. return 'cycle'
  673. def _print_ArrayConstructor(self, ac):
  674. fmtstr = "[%s]" if self._settings["standard"] >= 2003 else '(/%s/)'
  675. return fmtstr % ', '.join((self._print(arg) for arg in ac.elements))
  676. def _print_ArrayElement(self, elem):
  677. return '{symbol}({idxs})'.format(
  678. symbol=self._print(elem.name),
  679. idxs=', '.join((self._print(arg) for arg in elem.indices))
  680. )