6
takes templated file .xxx.src and produces .xxx file where .xxx
7
is .pyf .f90 or .f using the following template rules:
9
'<..>' denotes a template.
11
All function and subroutine blocks in a source file with names that
12
contain '<..>' will be replicated according to the rules in '<..>'.
14
The number of comma-separeted words in '<..>' will determine the number of
17
'<..>' may have two different forms, named and short. For example,
20
<p=d,s,z,c> where anywhere inside a block '<p>' will be replaced with
21
'd', 's', 'z', and 'c' for each replicate of the block.
23
<_c> is already defined: <_c=s,d,c,z>
24
<_t> is already defined: <_t=real,double precision,complex,double complex>
27
<s,d,c,z>, a short form of the named, useful when no <p> appears inside
30
In general, '<..>' contains a comma separated list of arbitrary
31
expressions. If these expression must contain a comma|leftarrow|rightarrow,
32
then prepend the comma|leftarrow|rightarrow with a backslash.
34
If an expression matches '\\<index>' then it will be replaced
35
by <index>-th expression.
37
Note that all '<..>' forms in a block must have the same number of
38
comma-separated entries.
40
Predefined named template rules:
42
<ftype=real,double precision,complex,double complex>
43
<ftypereal=real,double precision,\\0,\\1>
44
<ctype=float,double,complex_float,complex_double>
45
<ctypereal=float,double,\\0,\\1>
49
__all__ = ['process_str','process_file']
52
if sys.version[:3]>='2.3':
58
if sys.version[:5]=='2.2.1':
61
routine_start_re = re.compile(r'(\n|\A)(( (\$|\*))|)\s*(subroutine|function)\b',re.I)
62
routine_end_re = re.compile(r'\n\s*end\s*(subroutine|function)\b.*(\n|\Z)',re.I)
63
function_start_re = re.compile(r'\n (\$|\*)\s*function\b',re.I)
65
def parse_structure(astr):
66
""" Return a list of tuples for each function or subroutine each
67
tuple is the start and end of a subroutine or function to be
74
m = routine_start_re.search(astr,ind)
78
if function_start_re.match(astr,start,m.end()):
80
i = astr.rfind('\n',ind,start)
84
if astr[i:i+7]!='\n $':
87
m = routine_end_re.search(astr,m.end())
88
ind = end = m and m.end()-1 or len(astr)
89
spanlist.append((start,end))
92
template_re = re.compile(r"<\s*(\w[\w\d]*)\s*>")
93
named_re = re.compile(r"<\s*(\w[\w\d]*)\s*=\s*(.*?)\s*>")
94
list_re = re.compile(r"<\s*((.*?))\s*>")
96
def find_repl_patterns(astr):
97
reps = named_re.findall(astr)
100
name = rep[0].strip() or unique_key(names)
101
repl = rep[1].replace('\,','@comma@')
103
names[name] = thelist
106
item_re = re.compile(r"\A\\(?P<index>\d+)\Z")
109
l = [x.strip() for x in b]
110
for i in range(len(l)):
111
m = item_re.match(l[i])
113
j = int(m.group('index'))
117
def unique_key(adict):
118
""" Obtain a unique key given a dictionary."""
119
allkeys = adict.keys()
123
newkey = '__l%s' % (n)
124
if newkey in allkeys:
131
template_name_re = re.compile(r'\A\s*(\w[\w\d]*)\s*\Z')
132
def expand_sub(substr,names):
133
substr = substr.replace('\>','@rightarrow@')
134
substr = substr.replace('\<','@leftarrow@')
135
lnames = find_repl_patterns(substr)
136
substr = named_re.sub(r"<\1>",substr) # get rid of definition templates
139
thelist = conv(mobj.group(1).replace('\,','@comma@'))
140
if template_name_re.match(thelist):
141
return "<%s>" % (thelist)
143
for key in lnames.keys(): # see if list is already in dictionary
144
if lnames[key] == thelist:
146
if name is None: # this list is not in the dictionary yet
147
name = unique_key(lnames)
148
lnames[name] = thelist
151
substr = list_re.sub(listrepl, substr) # convert all lists to named templates
152
# newnames are constructed as needed
157
for r in template_re.findall(substr):
158
if not rules.has_key(r):
159
thelist = lnames.get(r,names.get(r,None))
161
raise ValueError,'No replicates found for <%s>' % (r)
162
if not names.has_key(r) and not thelist.startswith('_'):
164
rule = [i.replace('@comma@',',') for i in thelist.split(',')]
174
print "Mismatch in number of replacements (base <%s=%s>)"\
175
" for <%s=%s>. Ignoring." % (base_rule,
176
','.join(rules[base_rule]),
183
return rules.get(name,(k+1)*[name])[k]
186
for k in range(numsubs):
187
newstr += template_re.sub(namerepl, substr) + '\n\n'
189
newstr = newstr.replace('@rightarrow@','>')
190
newstr = newstr.replace('@leftarrow@','<')
193
def process_str(allstr):
195
writestr = '' #_head # using _head will break free-format files
197
struct = parse_structure(newstr)
201
names.update(_special_names)
203
writestr += newstr[oldend:sub[0]]
204
names.update(find_repl_patterns(newstr[oldend:sub[0]]))
205
writestr += expand_sub(newstr[sub[0]:sub[1]],names)
207
writestr += newstr[oldend:]
211
include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+[.]src)['\"]",re.I)
213
def resolve_includes(source):
214
d = os.path.dirname(source)
217
for line in fid.readlines():
218
m = include_src_re.match(line)
221
if not os.path.isabs(fn):
222
fn = os.path.join(d,fn)
223
if os.path.isfile(fn):
224
print 'Including file',fn
225
lines.extend(resolve_includes(fn))
233
def process_file(source):
234
lines = resolve_includes(source)
235
return process_str(''.join(lines))
237
_special_names = find_repl_patterns('''
239
<_t=real,double precision,complex,double complex>
241
<ftype=real,double precision,complex,double complex>
242
<ctype=float,double,complex_float,complex_double>
243
<ftypereal=real,double precision,\\0,\\1>
244
<ctypereal=float,double,\\0,\\1>
247
if __name__ == "__main__":
256
(base, ext) = os.path.splitext(file)
258
outfile = open(newname,'w')
261
writestr = process_str(allstr)
262
outfile.write(writestr)