_src_pyf.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import re
  2. # START OF CODE VENDORED FROM `numpy.distutils.from_template`
  3. #############################################################
  4. """
  5. process_file(filename)
  6. takes templated file .xxx.src and produces .xxx file where .xxx
  7. is .pyf .f90 or .f using the following template rules:
  8. '<..>' denotes a template.
  9. All function and subroutine blocks in a source file with names that
  10. contain '<..>' will be replicated according to the rules in '<..>'.
  11. The number of comma-separated words in '<..>' will determine the number of
  12. replicates.
  13. '<..>' may have two different forms, named and short. For example,
  14. named:
  15. <p=d,s,z,c> where anywhere inside a block '<p>' will be replaced with
  16. 'd', 's', 'z', and 'c' for each replicate of the block.
  17. <_c> is already defined: <_c=s,d,c,z>
  18. <_t> is already defined: <_t=real,double precision,complex,double complex>
  19. short:
  20. <s,d,c,z>, a short form of the named, useful when no <p> appears inside
  21. a block.
  22. In general, '<..>' contains a comma separated list of arbitrary
  23. expressions. If these expression must contain a comma|leftarrow|rightarrow,
  24. then prepend the comma|leftarrow|rightarrow with a backslash.
  25. If an expression matches '\\<index>' then it will be replaced
  26. by <index>-th expression.
  27. Note that all '<..>' forms in a block must have the same number of
  28. comma-separated entries.
  29. Predefined named template rules:
  30. <prefix=s,d,c,z>
  31. <ftype=real,double precision,complex,double complex>
  32. <ftypereal=real,double precision,\\0,\\1>
  33. <ctype=float,double,complex_float,complex_double>
  34. <ctypereal=float,double,\\0,\\1>
  35. """
  36. routine_start_re = re.compile(r'(\n|\A)(( (\$|\*))|)\s*(subroutine|function)\b', re.I)
  37. routine_end_re = re.compile(r'\n\s*end\s*(subroutine|function)\b.*(\n|\Z)', re.I)
  38. function_start_re = re.compile(r'\n (\$|\*)\s*function\b', re.I)
  39. def parse_structure(astr):
  40. """ Return a list of tuples for each function or subroutine each
  41. tuple is the start and end of a subroutine or function to be
  42. expanded.
  43. """
  44. spanlist = []
  45. ind = 0
  46. while True:
  47. m = routine_start_re.search(astr, ind)
  48. if m is None:
  49. break
  50. start = m.start()
  51. if function_start_re.match(astr, start, m.end()):
  52. while True:
  53. i = astr.rfind('\n', ind, start)
  54. if i==-1:
  55. break
  56. start = i
  57. if astr[i:i+7]!='\n $':
  58. break
  59. start += 1
  60. m = routine_end_re.search(astr, m.end())
  61. ind = end = m and m.end()-1 or len(astr)
  62. spanlist.append((start, end))
  63. return spanlist
  64. template_re = re.compile(r"<\s*(\w[\w\d]*)\s*>")
  65. named_re = re.compile(r"<\s*(\w[\w\d]*)\s*=\s*(.*?)\s*>")
  66. list_re = re.compile(r"<\s*((.*?))\s*>")
  67. def find_repl_patterns(astr):
  68. reps = named_re.findall(astr)
  69. names = {}
  70. for rep in reps:
  71. name = rep[0].strip() or unique_key(names)
  72. repl = rep[1].replace(r'\,', '@comma@')
  73. thelist = conv(repl)
  74. names[name] = thelist
  75. return names
  76. def find_and_remove_repl_patterns(astr):
  77. names = find_repl_patterns(astr)
  78. astr = re.subn(named_re, '', astr)[0]
  79. return astr, names
  80. item_re = re.compile(r"\A\\(?P<index>\d+)\Z")
  81. def conv(astr):
  82. b = astr.split(',')
  83. l = [x.strip() for x in b]
  84. for i in range(len(l)):
  85. m = item_re.match(l[i])
  86. if m:
  87. j = int(m.group('index'))
  88. l[i] = l[j]
  89. return ','.join(l)
  90. def unique_key(adict):
  91. """ Obtain a unique key given a dictionary."""
  92. allkeys = list(adict.keys())
  93. done = False
  94. n = 1
  95. while not done:
  96. newkey = '__l%s' % (n)
  97. if newkey in allkeys:
  98. n += 1
  99. else:
  100. done = True
  101. return newkey
  102. template_name_re = re.compile(r'\A\s*(\w[\w\d]*)\s*\Z')
  103. def expand_sub(substr, names):
  104. substr = substr.replace(r'\>', '@rightarrow@')
  105. substr = substr.replace(r'\<', '@leftarrow@')
  106. lnames = find_repl_patterns(substr)
  107. substr = named_re.sub(r"<\1>", substr) # get rid of definition templates
  108. def listrepl(mobj):
  109. thelist = conv(mobj.group(1).replace(r'\,', '@comma@'))
  110. if template_name_re.match(thelist):
  111. return "<%s>" % (thelist)
  112. name = None
  113. for key in lnames.keys(): # see if list is already in dictionary
  114. if lnames[key] == thelist:
  115. name = key
  116. if name is None: # this list is not in the dictionary yet
  117. name = unique_key(lnames)
  118. lnames[name] = thelist
  119. return "<%s>" % name
  120. substr = list_re.sub(listrepl, substr) # convert all lists to named templates
  121. # newnames are constructed as needed
  122. numsubs = None
  123. base_rule = None
  124. rules = {}
  125. for r in template_re.findall(substr):
  126. if r not in rules:
  127. thelist = lnames.get(r, names.get(r, None))
  128. if thelist is None:
  129. raise ValueError('No replicates found for <%s>' % (r))
  130. if r not in names and not thelist.startswith('_'):
  131. names[r] = thelist
  132. rule = [i.replace('@comma@', ',') for i in thelist.split(',')]
  133. num = len(rule)
  134. if numsubs is None:
  135. numsubs = num
  136. rules[r] = rule
  137. base_rule = r
  138. elif num == numsubs:
  139. rules[r] = rule
  140. else:
  141. print("Mismatch in number of replacements (base <{}={}>) "
  142. "for <{}={}>. Ignoring.".format(base_rule, ','.join(rules[base_rule]), r, thelist))
  143. if not rules:
  144. return substr
  145. def namerepl(mobj):
  146. name = mobj.group(1)
  147. return rules.get(name, (k+1)*[name])[k]
  148. newstr = ''
  149. for k in range(numsubs):
  150. newstr += template_re.sub(namerepl, substr) + '\n\n'
  151. newstr = newstr.replace('@rightarrow@', '>')
  152. newstr = newstr.replace('@leftarrow@', '<')
  153. return newstr
  154. def process_str(allstr):
  155. newstr = allstr
  156. writestr = ''
  157. struct = parse_structure(newstr)
  158. oldend = 0
  159. names = {}
  160. names.update(_special_names)
  161. for sub in struct:
  162. cleanedstr, defs = find_and_remove_repl_patterns(newstr[oldend:sub[0]])
  163. writestr += cleanedstr
  164. names.update(defs)
  165. writestr += expand_sub(newstr[sub[0]:sub[1]], names)
  166. oldend = sub[1]
  167. writestr += newstr[oldend:]
  168. return writestr
  169. include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+\.src)['\"]", re.I)
  170. def resolve_includes(source):
  171. d = os.path.dirname(source)
  172. with open(source) as fid:
  173. lines = []
  174. for line in fid:
  175. m = include_src_re.match(line)
  176. if m:
  177. fn = m.group('name')
  178. if not os.path.isabs(fn):
  179. fn = os.path.join(d, fn)
  180. if os.path.isfile(fn):
  181. lines.extend(resolve_includes(fn))
  182. else:
  183. lines.append(line)
  184. else:
  185. lines.append(line)
  186. return lines
  187. def process_file(source):
  188. lines = resolve_includes(source)
  189. return process_str(''.join(lines))
  190. _special_names = find_repl_patterns('''
  191. <_c=s,d,c,z>
  192. <_t=real,double precision,complex,double complex>
  193. <prefix=s,d,c,z>
  194. <ftype=real,double precision,complex,double complex>
  195. <ctype=float,double,complex_float,complex_double>
  196. <ftypereal=real,double precision,\\0,\\1>
  197. <ctypereal=float,double,\\0,\\1>
  198. ''')
  199. # END OF CODE VENDORED FROM `numpy.distutils.from_template`
  200. ###########################################################