math/mpgen, symm/multigen: Fix the various build scripts for Python 3.
authorMark Wooding <mdw@distorted.org.uk>
Fri, 4 Oct 2019 14:43:27 +0000 (15:43 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 9 May 2020 19:57:33 +0000 (20:57 +0100)
  * Put parentheses around `print' arguments.

  * Write `raise EXC(VALUE)' rather than `raise EXC, VALUE' to raise
    exceptions.

  * Cope with `xrange' being renamed to `range' in Python 3.

  * Cope with `long' not being a type name in Python 3.

  * Cope with `execfile' not being available in Python 3.

  * Cope with function-object attribute names being renamed in Python 3.

  * Cope with `StringIO' being in `io' rather than `cStringIO' in Python
    3, and with `StringIO.reset' not being available any more.

  * Cope with `itertools.izip' not being available in Python 3.

  * Cope with `OBJ.next' not being available in Python 3.

  * Use an unpleasant hack to inject metaclasses, because the official
    syntax is so different between the two versions.

math/mpgen
symm/multigen

index 7d11bfd..c7dbf88 100644 (file)
@@ -28,10 +28,9 @@ from __future__ import with_statement
 
 import re as RX
 import optparse as OP
 
 import re as RX
 import optparse as OP
+import sys as SYS; stdout = SYS.stdout
 import types as TY
 
 import types as TY
 
-from sys import stdout
-
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
@@ -65,6 +64,22 @@ def fix_name(name):
   """Replace non-alphanumeric characters in NAME with underscores."""
   return R_IDBAD.sub('_', name)
 
   """Replace non-alphanumeric characters in NAME with underscores."""
   return R_IDBAD.sub('_', name)
 
+if SYS.version_info >= (3,):
+  def func_name(func): return func.__name__
+  xrange = range
+  long = int
+else:
+  def func_name(func): return func.func_name
+
+def with_metaclass(meta, *supers):
+  return meta('#<anonymous base %s>' % meta.__name__,
+              supers or (object,), dict())
+
+def load(file, mod):
+  with open(file) as f: text = f.read()
+  code = compile(text, file, 'exec')
+  exec(code, mod)
+
 ###--------------------------------------------------------------------------
 ### Determining the appropriate types.
 
 ###--------------------------------------------------------------------------
 ### Determining the appropriate types.
 
@@ -83,7 +98,7 @@ class IntClass (type):
     except AttributeError: pass
     return c
 
     except AttributeError: pass
     return c
 
-class BasicIntType (object):
+class BasicIntType (with_metaclass(IntClass)):
   """
   A base class for integer-type classes, providing defaults and protocol.
 
   """
   A base class for integer-type classes, providing defaults and protocol.
 
@@ -195,7 +210,7 @@ class TypeChoice (object):
 
     ## Load the captured type information.
     me.ti = TY.ModuleType('typeinfo')
 
     ## Load the captured type information.
     me.ti = TY.ModuleType('typeinfo')
-    execfile(opts.typeinfo, me.ti.__dict__)
+    load(opts.typeinfo, me.ti.__dict__)
 
     ## Build a map of the available types.
     tymap = {}
 
     ## Build a map of the available types.
     tymap = {}
@@ -226,14 +241,14 @@ class TypeChoice (object):
 
     ## Make sure we've not ended up somewhere really silly.
     if mpwbits < 16:
 
     ## Make sure we've not ended up somewhere really silly.
     if mpwbits < 16:
-      raise Exception, "`mpw' type is too small: your C environment is weird"
+      raise Exception("`mpw' type is too small: your C environment is weird")
 
     ## Now figure out suitable types for `mpw' and `mpd'.
     def find_type(bits, what):
       for ty in byrank:
         if ty.bits >= bits: return ty
 
     ## Now figure out suitable types for `mpw' and `mpd'.
     def find_type(bits, what):
       for ty in byrank:
         if ty.bits >= bits: return ty
-      raise Exception, \
-          "failed to find suitable %d-bit type, for %s" % (bits, what)
+      raise Exception \
+        ("failed to find suitable %d-bit type, for %s" % (bits, what))
 
     ## Store our decisions.
     me.mpwbits = mpwbits
 
     ## Store our decisions.
     me.mpwbits = mpwbits
@@ -327,7 +342,7 @@ def defmode(func):
   arguments from the command and are expected to write their output to
   stdout.
   """
   arguments from the command and are expected to write their output to
   stdout.
   """
-  name = func.func_name
+  name = func_name(func)
   if name.startswith('m_'): name = name[2:]
   MODEMAP[name] = func
   return func
   if name.startswith('m_'): name = name[2:]
   MODEMAP[name] = func
   return func
@@ -483,7 +498,7 @@ class GroupTableClass (type):
     else: MODEMAP[c.mode] = c.run
     return c
 
     else: MODEMAP[c.mode] = c.run
     return c
 
-class GroupTable (object):
+class GroupTable (with_metaclass(GroupTableClass)):
   """
   Base class for group tables objects.
 
   """
   Base class for group tables objects.
 
@@ -568,8 +583,6 @@ class GroupTable (object):
   ## _slotmap           A dictionary mapping slot names to their
   ##                    descriptions.
 
   ## _slotmap           A dictionary mapping slot names to their
   ##                    descriptions.
 
-  __metaclass__ = GroupTableClass
-
   ## Default values.
   keyword = 'group'
   slots = []
   ## Default values.
   keyword = 'group'
   slots = []
@@ -644,7 +657,7 @@ class GroupTable (object):
 
         if ff[0] == 'alias':
           ## An alias.  Just remember this.
 
         if ff[0] == 'alias':
           ## An alias.  Just remember this.
-          if len(ff) != 3: raise Exception, "wrong number of alias arguments"
+          if len(ff) != 3: raise Exception("wrong number of alias arguments")
           me._flush()
           me._names.append((ff[1], ff[2]))
 
           me._flush()
           me._names.append((ff[1], ff[2]))
 
@@ -654,7 +667,7 @@ class GroupTable (object):
           ## Check the headline syntax.  Headline slots may be set here, or
           ## later by name.
           if len(ff) < 2 or len(ff) > 2 + len(me._headslots):
           ## Check the headline syntax.  Headline slots may be set here, or
           ## later by name.
           if len(ff) < 2 or len(ff) > 2 + len(me._headslots):
-            raise Exception, "bad number of headline arguments"
+            raise Exception("bad number of headline arguments")
 
           ## Flush out the previous stanza.
           me._flush()
 
           ## Flush out the previous stanza.
           me._flush()
@@ -670,14 +683,14 @@ class GroupTable (object):
         elif ff[0] in me._slotmap:
           ## A slot assignment.  Get the slot to store a value.
           if me.st.name is None:
         elif ff[0] in me._slotmap:
           ## A slot assignment.  Get the slot to store a value.
           if me.st.name is None:
-            raise Exception, "no group currently being defined"
+            raise Exception("no group currently being defined")
           if len(ff) != 2:
           if len(ff) != 2:
-            raise Exception, "bad number of values for slot `%s'" % ff[0]
+            raise Exception("bad number of values for slot `%s'" % ff[0])
           me._slotmap[ff[0]].set(me.st, ff[1])
 
         else:
           ## Something incomprehensible.
           me._slotmap[ff[0]].set(me.st, ff[1])
 
         else:
           ## Something incomprehensible.
-          raise Exception, "unknown keyword `%s'" % ff[0]
+          raise Exception("unknown keyword `%s'" % ff[0])
 
     ## End of the input.  Write out the final stanza.
     me._flush()
 
     ## End of the input.  Write out the final stanza.
     me._flush()
@@ -687,7 +700,7 @@ class GroupTable (object):
     stdout.write("\nconst %s %s[] = {\n" % (me.entry_t, me.tabname))
     for a, n in me._names:
       if n not in me._defs:
     stdout.write("\nconst %s %s[] = {\n" % (me.entry_t, me.tabname))
     for a, n in me._names:
       if n not in me._defs:
-        raise Exception, "alias `%s' refers to unknown group `%s'" % (a, n)
+        raise Exception("alias `%s' refers to unknown group `%s'" % (a, n))
       stdout.write('  { "%s", &c_%s },\n' % (a, fix_name(n)))
     stdout.write("  { 0, 0 }\n};\n\n")
 
       stdout.write('  { "%s", &c_%s },\n' % (a, fix_name(n)))
     stdout.write("  { 0, 0 }\n};\n\n")
 
@@ -747,7 +760,7 @@ class BaseSlot (object):
     Store a VALUE for the slot.
     """
     if me._allowp and not me._allowp(st, value):
     Store a VALUE for the slot.
     """
     if me._allowp and not me._allowp(st, value):
-      raise Exception, "slot `%s' not allowed here" % me.name
+      raise Exception("slot `%s' not allowed here" % me.name)
     st.d[me] = value
 
   def setup(me, st):
     st.d[me] = value
 
   def setup(me, st):
@@ -755,7 +768,7 @@ class BaseSlot (object):
     Prepare the slot for output, checking its value and so on.
     """
     if me not in st.d and (not me._omitp or not me._omitp(st)):
     Prepare the slot for output, checking its value and so on.
     """
     if me not in st.d and (not me._omitp or not me._omitp(st)):
-      raise Exception, "missing slot `%s'" % me.name
+      raise Exception("missing slot `%s'" % me.name)
 
 class EnumSlot (BaseSlot):
   """
 
 class EnumSlot (BaseSlot):
   """
@@ -781,7 +794,7 @@ class EnumSlot (BaseSlot):
     Check that the VALUE is one of the ones we know.
     """
     if value not in me._values:
     Check that the VALUE is one of the ones we know.
     """
     if value not in me._values:
-      raise Exception, "invalid %s value `%s'" % (me.name, value)
+      raise Exception("invalid %s value `%s'" % (me.name, value))
     super(EnumSlot, me).set(st, value)
 
   def write(me, st):
     super(EnumSlot, me).set(st, value)
 
   def write(me, st):
index 4115eb4..8af0f10 100755 (executable)
@@ -30,7 +30,9 @@ import itertools as IT
 import optparse as OP
 import os as OS
 import re as RX
 import optparse as OP
 import os as OS
 import re as RX
-from cStringIO import StringIO
+import sys as SYS
+if SYS.version_info >= (3,): from io import StringIO
+else: from cStringIO import StringIO
 from sys import argv, exit, stderr
 
 ###--------------------------------------------------------------------------
 from sys import argv, exit, stderr
 
 ###--------------------------------------------------------------------------
@@ -49,6 +51,16 @@ def indexed(seq):
   """
   return IT.izip(IT.count(), seq)
 
   """
   return IT.izip(IT.count(), seq)
 
+if SYS.version_info >= (3,):
+  def func_name(func): return func.__name__
+  IT.izip = zip
+else:
+  def func_name(func): return func.func_name
+
+try: next
+except NameError:
+  def next(obj): return obj.next()
+
 ###--------------------------------------------------------------------------
 ### Reading the input values.
 
 ###--------------------------------------------------------------------------
 ### Reading the input values.
 
@@ -461,7 +473,7 @@ class ParseState (object):
 
     Sets `curr' to the next line, or to None if the input is exhausted.
     """
 
     Sets `curr' to the next line, or to None if the input is exhausted.
     """
-    try: me.curr = me._it.next()
+    try: me.curr = next(me._it)
     except StopIteration: me.curr = None
     else: me._i += 1
 
     except StopIteration: me.curr = None
     else: me._i += 1
 
@@ -504,7 +516,7 @@ def defop(func):
   An operator function is given the raw value as an argument and should
   return the transformed value.
   """
   An operator function is given the raw value as an argument and should
   return the transformed value.
   """
-  name = func.func_name
+  name = func_name(func)
   if name.startswith('op_'): name = name[3:]
   OPMAP[name] = func
   return func
   if name.startswith('op_'): name = name[3:]
   OPMAP[name] = func
   return func
@@ -576,7 +588,7 @@ def parse_text(ps):
     ## object.
     l = lit.getvalue()
     if l: tt.append(LiteralTemplate(l))
     ## object.
     l = lit.getvalue()
     if l: tt.append(LiteralTemplate(l))
-    lit.reset()
+    lit.seek(0)
     lit.truncate()
 
   ## Iterate over the lines of input.
     lit.truncate()
 
   ## Iterate over the lines of input.
@@ -786,7 +798,7 @@ def filenames(filetempl):
 
 ## Main dispatch.
 if opts.mode == 'list':
 
 ## Main dispatch.
 if opts.mode == 'list':
-  for file, cs in filenames(filetempl): print file
+  for file, cs in filenames(filetempl): print(file)
 elif opts.mode == 'gen':
   with open(opts.input) as f:
     templ = RepeatTemplate(compile_template(opts.input, f.read()))
 elif opts.mode == 'gen':
   with open(opts.input) as f:
     templ = RepeatTemplate(compile_template(opts.input, f.read()))