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 sys as SYS; stdout = SYS.stdout
 import types as TY
 
-from sys import stdout
-
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
@@ -65,6 +64,22 @@ def fix_name(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.
 
@@ -83,7 +98,7 @@ class IntClass (type):
     except AttributeError: pass
     return c
 
-class BasicIntType (object):
+class BasicIntType (with_metaclass(IntClass)):
   """
   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')
-    execfile(opts.typeinfo, me.ti.__dict__)
+    load(opts.typeinfo, me.ti.__dict__)
 
     ## 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:
-      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
-      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
@@ -327,7 +342,7 @@ def defmode(func):
   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
@@ -483,7 +498,7 @@ class GroupTableClass (type):
     else: MODEMAP[c.mode] = c.run
     return c
 
-class GroupTable (object):
+class GroupTable (with_metaclass(GroupTableClass)):
   """
   Base class for group tables objects.
 
@@ -568,8 +583,6 @@ class GroupTable (object):
   ## _slotmap           A dictionary mapping slot names to their
   ##                    descriptions.
 
-  __metaclass__ = GroupTableClass
-
   ## Default values.
   keyword = 'group'
   slots = []
@@ -644,7 +657,7 @@ class GroupTable (object):
 
         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]))
 
@@ -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):
-            raise Exception, "bad number of headline arguments"
+            raise Exception("bad number of headline arguments")
 
           ## 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:
-            raise Exception, "no group currently being defined"
+            raise Exception("no group currently being defined")
           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.
-          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()
@@ -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:
-        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")
 
@@ -747,7 +760,7 @@ class BaseSlot (object):
     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):
@@ -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)):
-      raise Exception, "missing slot `%s'" % me.name
+      raise Exception("missing slot `%s'" % me.name)
 
 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:
-      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):
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
-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
 
 ###--------------------------------------------------------------------------
@@ -49,6 +51,16 @@ def indexed(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.
 
@@ -461,7 +473,7 @@ class ParseState (object):
 
     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
 
@@ -504,7 +516,7 @@ def defop(func):
   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
@@ -576,7 +588,7 @@ def parse_text(ps):
     ## object.
     l = lit.getvalue()
     if l: tt.append(LiteralTemplate(l))
-    lit.reset()
+    lit.seek(0)
     lit.truncate()
 
   ## Iterate over the lines of input.
@@ -786,7 +798,7 @@ def filenames(filetempl):
 
 ## 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()))