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