fc_scan.py 2.8 KB

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