248 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			248 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# START OF CODE VENDORED FROM `numpy.distutils.from_template`
							 | 
						||
| 
								 | 
							
								#############################################################
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								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>
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 = f'__l{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 f"<{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 f"<{name}>"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # convert all lists to named templates
							 | 
						||
| 
								 | 
							
								    # new names are constructed as needed
							 | 
						||
| 
								 | 
							
								    substr = list_re.sub(listrepl, substr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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(f'No replicates found for <{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:
							 | 
						||
| 
								 | 
							
								                rules_base_rule = ','.join(rules[base_rule])
							 | 
						||
| 
								 | 
							
								                print("Mismatch in number of replacements "
							 | 
						||
| 
								 | 
							
								                      f"(base <{base_rule}={rules_base_rule}>) "
							 | 
						||
| 
								 | 
							
								                      f"for <{r}={thelist}>. Ignoring.")
							 | 
						||
| 
								 | 
							
								    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>
							 | 
						||
| 
								 | 
							
								''')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# END OF CODE VENDORED FROM `numpy.distutils.from_template`
							 | 
						||
| 
								 | 
							
								###########################################################
							 |