fc_scan.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # DC 2008
  4. # Thomas Nagy 2016-2018 (ita)
  5. import re
  6. INC_REGEX = r"""(?:^|['">]\s*;)\s*(?:|#\s*)INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])"""
  7. USE_REGEX = r"""(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)"""
  8. MOD_REGEX = r"""(?:^|;)\s*MODULE(?!\s+(?:PROCEDURE|SUBROUTINE|FUNCTION))\s+(\w+)"""
  9. SMD_REGEX = r"""(?:^|;)\s*SUBMODULE\s*\(([\w:]+)\)\s*(\w+)"""
  10. re_inc = re.compile(INC_REGEX, re.I)
  11. re_use = re.compile(USE_REGEX, re.I)
  12. re_mod = re.compile(MOD_REGEX, re.I)
  13. re_smd = re.compile(SMD_REGEX, re.I)
  14. class fortran_parser(object):
  15. """
  16. This parser returns:
  17. * the nodes corresponding to the module names to produce
  18. * the nodes corresponding to the include files used
  19. * the module names used by the fortran files
  20. """
  21. def __init__(self, incpaths):
  22. self.seen = []
  23. """Files already parsed"""
  24. self.nodes = []
  25. """List of :py:class:`waflib.Node.Node` representing the dependencies to return"""
  26. self.names = []
  27. """List of module names to return"""
  28. self.incpaths = incpaths
  29. """List of :py:class:`waflib.Node.Node` representing the include paths"""
  30. def find_deps(self, node):
  31. """
  32. Parses a Fortran file to obtain the dependencies used/provided
  33. :param node: fortran file to read
  34. :type node: :py:class:`waflib.Node.Node`
  35. :return: lists representing the includes, the modules used, and the modules created by a fortran file
  36. :rtype: tuple of list of strings
  37. """
  38. txt = node.read()
  39. incs = []
  40. uses = []
  41. mods = []
  42. for line in txt.splitlines():
  43. # line by line regexp search? optimize?
  44. m = re_inc.search(line)
  45. if m:
  46. incs.append(m.group(1))
  47. m = re_use.search(line)
  48. if m:
  49. uses.append(m.group(1))
  50. m = re_mod.search(line)
  51. if m:
  52. mods.append(m.group(1))
  53. m = re_smd.search(line)
  54. if m:
  55. uses.append(m.group(1))
  56. mods.append('{0}:{1}'.format(m.group(1),m.group(2)))
  57. return (incs, uses, mods)
  58. def start(self, node):
  59. """
  60. Start parsing. Use the stack ``self.waiting`` to hold nodes to iterate on
  61. :param node: fortran file
  62. :type node: :py:class:`waflib.Node.Node`
  63. """
  64. self.waiting = [node]
  65. while self.waiting:
  66. nd = self.waiting.pop(0)
  67. self.iter(nd)
  68. def iter(self, node):
  69. """
  70. Processes a single file during dependency parsing. Extracts files used
  71. modules used and modules provided.
  72. """
  73. incs, uses, mods = self.find_deps(node)
  74. for x in incs:
  75. if x in self.seen:
  76. continue
  77. self.seen.append(x)
  78. self.tryfind_header(x)
  79. for x in uses:
  80. name = "USE@%s" % x
  81. if not name in self.names:
  82. self.names.append(name)
  83. for x in mods:
  84. name = "MOD@%s" % x
  85. if not name in self.names:
  86. self.names.append(name)
  87. def tryfind_header(self, filename):
  88. """
  89. Adds an include file to the list of nodes to process
  90. :param filename: file name
  91. :type filename: string
  92. """
  93. found = None
  94. for n in self.incpaths:
  95. found = n.find_resource(filename)
  96. if found:
  97. self.nodes.append(found)
  98. self.waiting.append(found)
  99. break
  100. if not found:
  101. if not filename in self.names:
  102. self.names.append(filename)