[Numpy-svn] r5074 - trunk/numpy/distutils

numpy-svn at scipy.org numpy-svn at scipy.org
Wed Apr 23 19:16:55 EDT 2008


Author: charris
Date: 2008-04-23 18:16:53 -0500 (Wed, 23 Apr 2008)
New Revision: 5074

Modified:
   trunk/numpy/distutils/conv_template.py
Log:
Add true nesting of loops to the template processing. The previous attempt
wasn't very useful. Loops can now be nested within each other using
/**begin repeat1 and /**end repeat1**/ and cousins, starting with the current
tags for the outermost loops.


Modified: trunk/numpy/distutils/conv_template.py
===================================================================
--- trunk/numpy/distutils/conv_template.py	2008-04-23 20:49:48 UTC (rev 5073)
+++ trunk/numpy/distutils/conv_template.py	2008-04-23 23:16:53 UTC (rev 5074)
@@ -1,39 +1,119 @@
 #!/usr/bin/python
+"""
+takes templated file .xxx.src and produces .xxx file  where .xxx is
+.i or .c or .h, using the following template rules
 
-# takes templated file .xxx.src and produces .xxx file  where .xxx is .i or .c or .h
-#  using the following template rules
+/**begin repeat  -- on a line by itself marks the start of a repeated code
+                    segment
+/**end repeat**/ -- on a line by itself marks it's end
 
-# /**begin repeat     on a line by itself marks the beginning of a segment of code to be repeated
-# /**end repeat**/    on a line by itself marks it's end
+After the /**begin repeat and before the */, all the named templates are placed
+these should all have the same number of replacements
 
-# after the /**begin repeat and before the */
-#  all the named templates are placed
-#  these should all have the same number of replacements
+Repeat blocks can be nested, with each nested block labeled with its depth,
+i.e.
+/**begin repeat1
+ *....
+ */
+/**end repeat1**/
 
-#  in the main body, the names are used.
-#  Each replace will use one entry from the list of named replacements
+In the main body each replace will use one entry from the list of named replacements
 
-#  Note that all #..# forms in a block must have the same number of
-#    comma-separated entries.
+ Note that all #..# forms in a block must have the same number of
+   comma-separated entries.
 
+Example:
+
+    An input file containing
+
+        /**begin repeat
+         * #a = 1,2,3#
+         * #b = 1,2,3#
+         */
+
+        /**begin repeat1
+         * #c = ted, jim#
+         */
+        @a@, @b@, @c@
+        /**end repeat1**/
+
+        /**end repeat**/
+
+    produces
+
+        line 1 "template.c.src"
+
+        /*
+         *********************************************************************
+         **       This file was autogenerated from a template  DO NOT EDIT!!**
+         **       Changes should be made to the original source (.src) file **
+         *********************************************************************
+         */
+
+        #line 9
+        1, 1, ted
+
+        #line 9
+        1, 1, jim
+
+        #line 9
+        2, 2, ted
+
+        #line 9
+        2, 2, jim
+
+        #line 9
+        3, 3, ted
+
+        #line 9
+        3, 3, jim
+
+"""
+
 __all__ = ['process_str', 'process_file']
 
 import os
 import sys
 import re
 
-def parse_structure(astr):
+# names for replacement that are already global.
+global_names = {}
+
+# header placed at the front of head processed file
+header =\
+"""
+/*
+ *****************************************************************************
+ **       This file was autogenerated from a template  DO NOT EDIT!!!!      **
+ **       Changes should be made to the original source (.src) file         **
+ *****************************************************************************
+ */
+
+"""
+# Parse string for repeat loops
+def parse_structure(astr, level):
+    """
+    The returned line number is from the beginning of the string, starting
+    at zero. Returns an empty list if no loops found.
+
+    """
+    if level == 0 :
+        loopbeg = "/**begin repeat"
+        loopend = "/**end repeat**/"
+    else :
+        loopbeg = "/**begin repeat%d" % level
+        loopend = "/**end repeat%d**/" % level
+
+    ind = 0
+    line = 0
     spanlist = []
-    # subroutines
-    ind = 0
-    line = 1
     while 1:
-        start = astr.find("/**begin repeat", ind)
+        start = astr.find(loopbeg, ind)
         if start == -1:
             break
         start2 = astr.find("*/",start)
         start2 = astr.find("\n",start2)
-        fini1 = astr.find("/**end repeat**/",start2)
+        fini1 = astr.find(loopend,start2)
         fini2 = astr.find("\n",fini1)
         line += astr.count("\n", ind, start2+1)
         spanlist.append((start, start2+1, fini1, fini2+1, line))
@@ -42,18 +122,13 @@
     spanlist.sort()
     return spanlist
 
-# return n copies of substr with template replacement
-_special_names = {}
 
-template_re = re.compile(r"@([\w]+)@")
-named_re = re.compile(r"#\s*([\w]*)\s*=\s*([^#]*)#")
-
-parenrep = re.compile(r"[(]([^)]*?)[)]\*(\d+)")
 def paren_repl(obj):
     torep = obj.group(1)
     numrep = obj.group(2)
     return ','.join([torep]*int(numrep))
 
+parenrep = re.compile(r"[(]([^)]*?)[)]\*(\d+)")
 plainrep = re.compile(r"([^*]+)\*(\d+)")
 def conv(astr):
     # replaces all occurrences of '(a,b,c)*4' in astr
@@ -65,115 +140,76 @@
                      for x in astr.split(',')])
     return astr.split(',')
 
-def unique_key(adict):
-    # this obtains a unique key given a dictionary
-    # currently it works by appending together n of the letters of the
-    #   current keys and increasing n until a unique key is found
-    # -- not particularly quick
-    allkeys = adict.keys()
-    done = False
-    n = 1
-    while not done:
-        newkey = "".join([x[:n] for x in allkeys])
-        if newkey in allkeys:
-            n += 1
-        else:
-            done = True
-    return newkey
+named_re = re.compile(r"#\s*([\w]*)\s*=\s*([^#]*)#")
+def parse_loop_header(loophead) :
+    """Find all named replacements in the header
 
-def expand_sub(substr, namestr, line):
-    # find all named replacements in the various loops.
-    reps = named_re.findall(namestr)
-    nsubs = None
-    loops = []
-    names = {}
-    names.update(_special_names)
+    Returns a list of dictionaries, one for each loop iteration,
+    where each key is a name to be substituted and the corresponding
+    value is the replacement string.
+
+    """
+    # parse out the names and lists of values
+    names = []
+    reps = named_re.findall(loophead)
+    nsub = None
     for rep in reps:
         name = rep[0].strip()
         vals = conv(rep[1])
         size = len(vals)
-        if name == "repeat" :
-            names[None] = nsubs
-            loops.append(names)
-            nsubs = None
-            names = {}
-            continue
-        if nsubs is None :
-            nsubs = size
-        elif nsubs != size :
+        if nsub is None :
+            nsub = size
+        elif nsub != size :
             print name
             print vals
             raise ValueError, "Mismatch in number to replace"
-        names[name] = vals
-    names[None] = nsubs
-    loops.append(names)
+        names.append((name,vals))
 
     # generate list of dictionaries, one for each template iteration
-    def merge(d1,d2) :
-        tmp = d1.copy()
-        tmp.update(d2)
-        return tmp
+    dlist = []
+    for i in range(nsub) :
+        tmp = {}
+        for name,vals in names :
+            tmp[name] = vals[i]
+        dlist.append(tmp)
+    return dlist
 
-    dlist = [{}]
-    for d in loops :
-        nsubs = d.pop(None)
-        tmp = [{} for i in range(nsubs)]
-        for name, item in d.items() :
-            for i in range(nsubs) :
-                tmp[i][name] = item[i]
-        dlist = [merge(d1,d2) for d1 in dlist for d2 in tmp]
-
-    # now replace all keys for each of the dictionaries
-    def namerepl(match):
+replace_re = re.compile(r"@([\w]+)@")
+def parse_string(astr, env, level, line) :
+    # local function for string replacement, uses env
+    def replace(match):
         name = match.group(1)
-        return d[name]
+        return env[name]
 
-    mystr = []
-    header = "#line %d\n"%line
-    for d in dlist :
-        code = ''.join((header, template_re.sub(namerepl, substr), '\n'))
-        mystr.append(code)
+    code = []
+    struct = parse_structure(astr, level)
+    if struct :
+        # recurse over inner loops
+        oldend = 0
+        newlevel = level + 1
+        for sub in struct:
+            pref = astr[oldend:sub[0]]
+            head = astr[sub[0]:sub[1]]
+            text = astr[sub[1]:sub[2]]
+            oldend = sub[3]
+            newline = line + sub[4]
+            envlist = parse_loop_header(head)
+            code.append(pref)
+            for newenv in envlist :
+                newenv.update(env)
+                newcode = parse_string(text, newenv, newlevel, newline)
+                code.extend(newcode)
+        code.append(astr[oldend:])       
+    else :
+        # replace keys
+        lineno = "#line %d\n" % line
+        newcode = replace_re.sub(replace, astr)
+        code.append(''.join((lineno, newcode , '\n')))
+    return code
 
-    return mystr
-
-
-header =\
-"""
-/*
- *****************************************************************************
- **       This file was autogenerated from a template  DO NOT EDIT!!!!      **
- **       Changes should be made to the original source (.src) file         **
- *****************************************************************************
- */
-
-"""
-
-def get_line_header(str,beg):
-    extra = []
-    ind = beg - 1
-    char = str[ind]
-    while (ind > 0) and (char != '\n'):
-        extra.insert(0,char)
-        ind = ind - 1
-        char = str[ind]
-    return ''.join(extra)
-
-def process_str(allstr):
+def process_str(astr):
     code = [header]
-    struct = parse_structure(allstr)
-
-    #  return a (sorted) list of tuples for each begin repeat section
-    #  each tuple is the start and end of a region to be template repeated
-    oldend = 0
-    for sub in struct:
-        pref = allstr[oldend:sub[0]]
-        head = allstr[sub[0]:sub[1]]
-        text = allstr[sub[1]:sub[2]]
-        line = sub[4]
-        oldend =  sub[3]
-        code.append(pref)
-        code.extend(expand_sub(text,head,line))
-    code.append(allstr[oldend:])
+    code.extend(parse_string(astr, global_names, 0, 1))
     return ''.join(code)
 
 
@@ -203,10 +239,27 @@
 def process_file(source):
     lines = resolve_includes(source)
     sourcefile = os.path.normcase(source).replace("\\","\\\\")
-    return ('#line 1 "%s"\n%s'
-            % (sourcefile, process_str(''.join(lines))))
+    code = process_str(''.join(lines))
+    return '#line 1 "%s"\n%s' % (sourcefile, code)
 
 
+def unique_key(adict):
+    # this obtains a unique key given a dictionary
+    # currently it works by appending together n of the letters of the
+    #   current keys and increasing n until a unique key is found
+    # -- not particularly quick
+    allkeys = adict.keys()
+    done = False
+    n = 1
+    while not done:
+        newkey = "".join([x[:n] for x in allkeys])
+        if newkey in allkeys:
+            n += 1
+        else:
+            done = True
+    return newkey
+
+
 if __name__ == "__main__":
 
     try:




More information about the Numpy-svn mailing list