| Viewing file:  from_template.py (7.73 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
#!/usr/bin/env python3"""
 
 process_file(filename)
 
 takes templated file .xxx.src and produces .xxx file where .xxx
 is .pyf .f90 or .f using the following template rules:
 
 '<..>' denotes a template.
 
 All function and subroutine blocks in a source file with names that
 contain '<..>' will be replicated according to the rules in '<..>'.
 
 The number of comma-separated words in '<..>' will determine the number of
 replicates.
 
 '<..>' may have two different forms, named and short. For example,
 
 named:
 <p=d,s,z,c> where anywhere inside a block '<p>' will be replaced with
 'd', 's', 'z', and 'c' for each replicate of the block.
 
 <_c>  is already defined: <_c=s,d,c,z>
 <_t>  is already defined: <_t=real,double precision,complex,double complex>
 
 short:
 <s,d,c,z>, a short form of the named, useful when no <p> appears inside
 a block.
 
 In general, '<..>' contains a comma separated list of arbitrary
 expressions. If these expression must contain a comma|leftarrow|rightarrow,
 then prepend the comma|leftarrow|rightarrow with a backslash.
 
 If an expression matches '\\<index>' then it will be replaced
 by <index>-th expression.
 
 Note that all '<..>' forms in a block must have the same number of
 comma-separated entries.
 
 Predefined named template rules:
 <prefix=s,d,c,z>
 <ftype=real,double precision,complex,double complex>
 <ftypereal=real,double precision,\\0,\\1>
 <ctype=float,double,complex_float,complex_double>
 <ctypereal=float,double,\\0,\\1>
 
 """
 __all__ = ['process_str', 'process_file']
 
 import os
 import sys
 import re
 
 routine_start_re = re.compile(r'(\n|\A)((     (\$|\*))|)\s*(subroutine|function)\b', re.I)
 routine_end_re = re.compile(r'\n\s*end\s*(subroutine|function)\b.*(\n|\Z)', re.I)
 function_start_re = re.compile(r'\n     (\$|\*)\s*function\b', re.I)
 
 def parse_structure(astr):
 """ Return a list of tuples for each function or subroutine each
 tuple is the start and end of a subroutine or function to be
 expanded.
 """
 
 spanlist = []
 ind = 0
 while True:
 m = routine_start_re.search(astr, ind)
 if m is None:
 break
 start = m.start()
 if function_start_re.match(astr, start, m.end()):
 while True:
 i = astr.rfind('\n', ind, start)
 if i==-1:
 break
 start = i
 if astr[i:i+7]!='\n     $':
 break
 start += 1
 m = routine_end_re.search(astr, m.end())
 ind = end = m and m.end()-1 or len(astr)
 spanlist.append((start, end))
 return spanlist
 
 template_re = re.compile(r"<\s*(\w[\w\d]*)\s*>")
 named_re = re.compile(r"<\s*(\w[\w\d]*)\s*=\s*(.*?)\s*>")
 list_re = re.compile(r"<\s*((.*?))\s*>")
 
 def find_repl_patterns(astr):
 reps = named_re.findall(astr)
 names = {}
 for rep in reps:
 name = rep[0].strip() or unique_key(names)
 repl = rep[1].replace(r'\,', '@comma@')
 thelist = conv(repl)
 names[name] = thelist
 return names
 
 def find_and_remove_repl_patterns(astr):
 names = find_repl_patterns(astr)
 astr = re.subn(named_re, '', astr)[0]
 return astr, names
 
 item_re = re.compile(r"\A\\(?P<index>\d+)\Z")
 def conv(astr):
 b = astr.split(',')
 l = [x.strip() for x in b]
 for i in range(len(l)):
 m = item_re.match(l[i])
 if m:
 j = int(m.group('index'))
 l[i] = l[j]
 return ','.join(l)
 
 def unique_key(adict):
 """ Obtain a unique key given a dictionary."""
 allkeys = list(adict.keys())
 done = False
 n = 1
 while not done:
 newkey = '__l%s' % (n)
 if newkey in allkeys:
 n += 1
 else:
 done = True
 return newkey
 
 
 template_name_re = re.compile(r'\A\s*(\w[\w\d]*)\s*\Z')
 def expand_sub(substr, names):
 substr = substr.replace(r'\>', '@rightarrow@')
 substr = substr.replace(r'\<', '@leftarrow@')
 lnames = find_repl_patterns(substr)
 substr = named_re.sub(r"<\1>", substr)  # get rid of definition templates
 
 def listrepl(mobj):
 thelist = conv(mobj.group(1).replace(r'\,', '@comma@'))
 if template_name_re.match(thelist):
 return "<%s>" % (thelist)
 name = None
 for key in lnames.keys():    # see if list is already in dictionary
 if lnames[key] == thelist:
 name = key
 if name is None:      # this list is not in the dictionary yet
 name = unique_key(lnames)
 lnames[name] = thelist
 return "<%s>" % name
 
 substr = list_re.sub(listrepl, substr) # convert all lists to named templates
 # newnames are constructed as needed
 
 numsubs = None
 base_rule = None
 rules = {}
 for r in template_re.findall(substr):
 if r not in rules:
 thelist = lnames.get(r, names.get(r, None))
 if thelist is None:
 raise ValueError('No replicates found for <%s>' % (r))
 if r not in names and not thelist.startswith('_'):
 names[r] = thelist
 rule = [i.replace('@comma@', ',') for i in thelist.split(',')]
 num = len(rule)
 
 if numsubs is None:
 numsubs = num
 rules[r] = rule
 base_rule = r
 elif num == numsubs:
 rules[r] = rule
 else:
 print("Mismatch in number of replacements (base <%s=%s>)"
 " for <%s=%s>. Ignoring." %
 (base_rule, ','.join(rules[base_rule]), r, thelist))
 if not rules:
 return substr
 
 def namerepl(mobj):
 name = mobj.group(1)
 return rules.get(name, (k+1)*[name])[k]
 
 newstr = ''
 for k in range(numsubs):
 newstr += template_re.sub(namerepl, substr) + '\n\n'
 
 newstr = newstr.replace('@rightarrow@', '>')
 newstr = newstr.replace('@leftarrow@', '<')
 return newstr
 
 def process_str(allstr):
 newstr = allstr
 writestr = ''
 
 struct = parse_structure(newstr)
 
 oldend = 0
 names = {}
 names.update(_special_names)
 for sub in struct:
 cleanedstr, defs = find_and_remove_repl_patterns(newstr[oldend:sub[0]])
 writestr += cleanedstr
 names.update(defs)
 writestr += expand_sub(newstr[sub[0]:sub[1]], names)
 oldend =  sub[1]
 writestr += newstr[oldend:]
 
 return writestr
 
 include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+\.src)['\"]", re.I)
 
 def resolve_includes(source):
 d = os.path.dirname(source)
 with open(source) as fid:
 lines = []
 for line in fid:
 m = include_src_re.match(line)
 if m:
 fn = m.group('name')
 if not os.path.isabs(fn):
 fn = os.path.join(d, fn)
 if os.path.isfile(fn):
 lines.extend(resolve_includes(fn))
 else:
 lines.append(line)
 else:
 lines.append(line)
 return lines
 
 def process_file(source):
 lines = resolve_includes(source)
 return process_str(''.join(lines))
 
 _special_names = find_repl_patterns('''
 <_c=s,d,c,z>
 <_t=real,double precision,complex,double complex>
 <prefix=s,d,c,z>
 <ftype=real,double precision,complex,double complex>
 <ctype=float,double,complex_float,complex_double>
 <ftypereal=real,double precision,\\0,\\1>
 <ctypereal=float,double,\\0,\\1>
 ''')
 
 def main():
 try:
 file = sys.argv[1]
 except IndexError:
 fid = sys.stdin
 outfile = sys.stdout
 else:
 fid = open(file, 'r')
 (base, ext) = os.path.splitext(file)
 newname = base
 outfile = open(newname, 'w')
 
 allstr = fid.read()
 writestr = process_str(allstr)
 outfile.write(writestr)
 
 
 if __name__ == "__main__":
 main()
 
 |