PyForTool
Python-fortran-tool
Loading...
Searching...
No Matches
cpp.py
1"""
2This module implements the Cpp class containing the methods for dealing with cpp directives
3"""
4
5from pyfortool.util import debugDecor, alltext, PYFTError, tag, noParallel
6from pyfortool.tree import updateTree
7from pyfortool.variables import updateVarList
8
9
10class Cpp:
11 """
12 Methods to deal with cpp directives
13 """
14 @debugDecor
15 @noParallel
16 @updateVarList
17 @updateTree('signal')
18 def applyCPPifdef(self, keys):
19 """
20 Apply #ifdef / #ifndef only on some keys
21 :param keys: list of defined and undefined keys. The latter are preceded by a
22 percentage sign '%'.
23 E.g. if K is in keys, "#ifdef K "will be evaluated to True
24 if %K is in keys, "#ifdef K" will be evaluated to False
25
26 Warning: only #ifdef and #ifndef are treated and not "#if defined ..."
27
28 If key K is in keys
29 #ifdef K
30 A
31 #else
32 B
33 #endif
34 is reduced to A
35 If %K is in keys, code snippet is reduced to B.
36 If neither K nor %K are present in keys, code is kept untouched.
37 """
38
39 # We make the hypothesis that #ifdef, #else and #endif have the same parent
40 # Get all nodes containing #ifdef or #ifndef
41 parents = set(self.getParent(cppNode) for cppNode in self.findall('.//{*}cpp')
42 if (cppNode.text.startswith('#ifdef ') or
43 cppNode.text.startswith('#ifndef ')))
44
45 # Iteration over nodes contained in each parent
46 toRemove = []
47 for par in parents:
48 # we deal with nested #ifdef #ifndef and #if
49 # We need to track #if cpp directives to discard #else and #endif related to these #if
50 # Each time we enter an #ifdef, #ifndef or #if, we add a value to the keep list
51 # True or False to keep or discard it, None not to touch it
52 keep = [True]
53 for node in par:
54 if tag(node) == 'cpp':
55 if node.text.startswith('#ifdef '):
56 k = alltext(node).split(' ')[1].strip()
57 if k in keys:
58 toRemove.append((node, par))
59 keep.append(True)
60 elif '%' + k in keys:
61 toRemove.append((node, par))
62 keep.append(False)
63 else:
64 keep.append(None)
65 if False in keep:
66 toRemove.append((node, par))
67 elif node.text.startswith('#ifndef '):
68 k = alltext(node).split(' ')[1].strip()
69 if k in keys:
70 toRemove.append((node, par))
71 keep.append(False)
72 elif '%' + k in keys:
73 toRemove.append((node, par))
74 keep.append(True)
75 else:
76 keep.append(None)
77 if False in keep:
78 toRemove.append((node, par))
79 elif node.text.startswith('#if '):
80 if False in keep:
81 toRemove.append((node, par))
82 # We are in a #if,following #else / #endif is associated to this #if
83 keep.append(None)
84 elif node.text.startswith('#else'):
85 if keep[-1] is not None:
86 toRemove.append((node, par))
87 keep[-1] = not keep[-1]
88 elif False in keep:
89 toRemove.append((node, par))
90 elif node.text.startswith('#endif'):
91 if keep[-1] is not None or False in keep:
92 toRemove.append((node, par))
93 keep.pop()
94 elif node.text.startswith('#elifdef') or node.text.startswith('#elifndef'):
95 raise NotImplementedError("#elifdef and #elifndef not (yet?) implemented")
96 else:
97 if False in keep:
98 toRemove.append((node, par))
99 else:
100 if False in keep:
101 toRemove.append((node, par))
102 if len(keep) != 1:
103 # We check the hypothesis done at the beginning
104 raise PYFTError("#else or #endif hasn't the same parent as #ifdef " +
105 "or #ifndef in {f}".format(f=self.getFileName()))
106 # Suppress node in reverse order to attach tail to previous node
107 if len(toRemove) != 0:
108 self.tree.signal(self) # Tree may need to be updated
109 for node, par in toRemove[::-1]:
110 index = list(par).index(node)
111 if index != 0:
112 if node.tail is not None:
113 if par[index - 1].tail is None:
114 par[index - 1].tail = ""
115 # We only keep '\n' and spaces at the end (indentation)
116 par[index - 1].tail += (node.tail.count('\n') * '\n' +
117 (len(node.tail) - len(node.tail.rstrip(' '))) * ' ')
118 par.remove(node)
applyCPPifdef(self, keys)
Definition cpp.py:18