pyloxi: move OFType interface functions to module level

This removes the need to replace the LOXI IR OFType object in members with our
own version. If/when the core OFType is upgraded to an inheritance hierachy we
won't need to duplicate it in pyloxi.
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index fb2e48c..4e93c4c 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -67,20 +67,12 @@
 
         for m in ofclass.members:
             if type(m) == OFTypeMember:
-                members.append(OFTypeMember(
-                    name=m.name,
-                    oftype=oftype.OFType(m.oftype, version),
-                    value=m.value))
+                members.append(m)
                 type_members.append(members[-1])
             elif type(m) == OFLengthMember:
-                members.append(OFLengthMember(
-                    name=m.name,
-                    oftype=oftype.OFType(m.oftype, version)))
+                members.append(m)
             elif type(m) == OFFieldLengthMember:
-                members.append(OFFieldLengthMember(
-                    name=m.name,
-                    oftype=oftype.OFType(m.oftype, version),
-                    field_name=m.field_name))
+                members.append(m)
             elif type(m) == OFPadMember:
                 members.append(m)
             elif type(m) == OFDataMember:
@@ -88,13 +80,11 @@
                     # HACK move to frontend
                     members.append(OFTypeMember(
                         name=m.name,
-                        oftype=oftype.OFType(m.oftype, version),
+                        oftype=m.oftype,
                         value=version))
                     type_members.append(members[-1])
                 else:
-                    members.append(OFDataMember(
-                        name=m.name,
-                        oftype=oftype.OFType(m.oftype, version)))
+                    members.append(m)
 
         ofclasses.append(
             PyOFClass(name=cls,
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 2854e76..cf87ded 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -32,7 +32,7 @@
 # Map from LOXI type name to an object with templates for init, pack, and unpack
 # Most types are defined using the convenience code below. This dict should
 # only be used directly for special cases such as primitive types.
-type_data = {
+type_data_map = {
     'char': OFTypeData(
         init='0',
         pack='struct.pack("!B", %s)',
@@ -129,7 +129,7 @@
 }
 
 for (cls, length) in fixed_length_strings.items():
-    type_data[cls] = OFTypeData(
+    type_data_map[cls] = OFTypeData(
         init='""',
         pack='struct.pack("!%ds", %%s)' % length,
         unpack='%%s.read("!%ds")[0].rstrip("\\x00")' % length)
@@ -145,7 +145,7 @@
 }
 
 for (cls, pyclass) in embedded_structs.items():
-    type_data[cls] = OFTypeData(
+    type_data_map[cls] = OFTypeData(
         init='%s()' % pyclass,
         pack='%s.pack()',
         unpack='%s.unpack(%%s)' % pyclass)
@@ -168,7 +168,7 @@
 }
 
 for (cls, deserializer) in variable_elem_len_lists.items():
-    type_data[cls] = OFTypeData(
+    type_data_map[cls] = OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
         unpack='%s(%%s)' % deserializer)
@@ -189,39 +189,39 @@
 }
 
 for (cls, element_deserializer) in fixed_elem_len_lists.items():
-    type_data[cls] = OFTypeData(
+    type_data_map[cls] = OFTypeData(
         init='[]',
         pack='util.pack_list(%s)',
         unpack='loxi.generic_util.unpack_list(%%s, %s)' % element_deserializer)
 
+## Public interface
 
-class OFType(object):
-    """
-    Encapsulates knowledge about the OpenFlow type system.
-    """
+# Return an initializer expression for the given oftype
+def gen_init_expr(oftype):
+    type_data = type_data_map.get(oftype)
+    if type_data and type_data.init:
+        return type_data.init
+    else:
+        return "loxi.unimplemented('init %s')" % oftype
 
-    version = None
-    base = None
+# Return a pack expression for the given oftype
+#
+# 'value_expr' is a string of Python code which will evaluate to
+# the value to be packed.
+def gen_pack_expr(oftype, value_expr):
+    type_data = type_data_map.get(oftype)
+    if type_data and type_data.pack:
+        return type_data.pack % value_expr
+    else:
+        return "loxi.unimplemented('pack %s')" % oftype
 
-    def __init__(self, string, version):
-        self.version = version
-        self.base = string
-        self.type_data = type_data.get(self.base)
-
-    def gen_init_expr(self):
-        if self.type_data and self.type_data.init:
-            return self.type_data.init
-        else:
-            return "loxi.unimplemented('init %s')" % self.base
-
-    def gen_pack_expr(self, value_expr):
-        if self.type_data and self.type_data.pack:
-            return self.type_data.pack % value_expr
-        else:
-            "loxi.unimplemented('pack %s')" % self.base
-
-    def gen_unpack_expr(self, reader_expr):
-        if self.type_data and self.type_data.unpack:
-            return self.type_data.unpack % reader_expr
-        else:
-            "loxi.unimplemented('unpack %s')" % self.base
+# Return an unpack expression for the given oftype
+#
+# 'reader_expr' is a string of Python code which will evaluate to
+# the OFReader instance used for deserialization.
+def gen_unpack_expr(oftype, reader_expr):
+    type_data = type_data_map.get(oftype)
+    if type_data and type_data.unpack:
+        return type_data.unpack % reader_expr
+    else:
+        return "loxi.unimplemented('unpack %s')" % oftype
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index 4c5358e..ec6b04e 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,4 +1,5 @@
 :: from loxi_ir import *
+:: import py_gen.oftype
 :: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
 class ${ofclass.pyname}(${superclass}):
 :: for m in ofclass.type_members:
@@ -10,7 +11,7 @@
         if ${m.name} != None:
             self.${m.name} = ${m.name}
         else:
-            self.${m.name} = ${m.oftype.gen_init_expr()}
+            self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
 :: #endfor
         return
 
diff --git a/py_gen/templates/_pack.py b/py_gen/templates/_pack.py
index 9a481b2..4714e97 100644
--- a/py_gen/templates/_pack.py
+++ b/py_gen/templates/_pack.py
@@ -27,6 +27,7 @@
 ::
 :: # TODO coalesce format strings
 :: from loxi_ir import *
+:: from py_gen.oftype import gen_pack_expr
 :: length_member = None
 :: length_member_index = None
 :: field_length_members = {}
@@ -36,26 +37,26 @@
 ::     if type(m) == OFLengthMember:
 ::         length_member = m
 ::         length_member_index = index
-        packed.append(${m.oftype.gen_pack_expr('0')}) # placeholder for ${m.name} at index ${index}
+        packed.append(${gen_pack_expr(m.oftype, '0')}) # placeholder for ${m.name} at index ${index}
 ::     elif type(m) == OFFieldLengthMember:
 ::         field_length_members[m.field_name] = m
 ::         field_length_indexes[m.field_name] = index
-        packed.append(${m.oftype.gen_pack_expr('0')}) # placeholder for ${m.name} at index ${index}
+        packed.append(${gen_pack_expr(m.oftype, '0')}) # placeholder for ${m.name} at index ${index}
 ::     elif type(m) == OFPadMember:
         packed.append('\x00' * ${m.length})
 ::     else:
-        packed.append(${m.oftype.gen_pack_expr('self.' + m.name)})
+        packed.append(${gen_pack_expr(m.oftype, 'self.' + m.name)})
 ::         if m.name in field_length_members:
 ::             field_length_member = field_length_members[m.name]
 ::             field_length_index = field_length_indexes[m.name]
-        packed[${field_length_index}] = ${field_length_member.oftype.gen_pack_expr('len(packed[-1])')}
+        packed[${field_length_index}] = ${gen_pack_expr(field_length_member.oftype, 'len(packed[-1])')}
 ::         #endif
 ::     #endif
 ::     index += 1
 :: #endfor
 :: if length_member_index != None:
         length = sum([len(x) for x in packed])
-        packed[${length_member_index}] = ${length_member.oftype.gen_pack_expr('length')}
+        packed[${length_member_index}] = ${gen_pack_expr(length_member.oftype, 'length')}
 :: #endif
 :: if ofclass.name == 'of_match_v3':
         packed.append('\x00' * ((length + 7)/8*8 - length))
diff --git a/py_gen/templates/_pretty_print.py b/py_gen/templates/_pretty_print.py
index 65c5941..7be7a14 100644
--- a/py_gen/templates/_pretty_print.py
+++ b/py_gen/templates/_pretty_print.py
@@ -44,15 +44,15 @@
                     q.text("%#x" % self.${m.name})
                 else:
                     q.text('None')
-:: elif m.oftype.base == 'of_mac_addr_t':
+:: elif m.oftype == 'of_mac_addr_t':
                 q.text(util.pretty_mac(self.${m.name}))
-:: elif m.oftype.base == 'uint32_t' and m.name.startswith("ipv4"):
+:: elif m.oftype == 'uint32_t' and m.name.startswith("ipv4"):
                 q.text(util.pretty_ipv4(self.${m.name}))
-:: elif m.oftype.base == 'of_wc_bmap_t' and version in [1,2]:
+:: elif m.oftype == 'of_wc_bmap_t' and version in [1,2]:
                 q.text(util.pretty_wildcards(self.${m.name}))
-:: elif m.oftype.base == 'of_port_no_t':
+:: elif m.oftype == 'of_port_no_t':
                 q.text(util.pretty_port(self.${m.name}))
-:: elif m.oftype.base.startswith("uint"):
+:: elif m.oftype.startswith("uint"):
                 q.text("%#x" % self.${m.name})
 :: else:
                 q.pp(self.${m.name})
diff --git a/py_gen/templates/_unpack.py b/py_gen/templates/_unpack.py
index 211043e..133e831 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -27,6 +27,7 @@
 ::
 :: # TODO coalesce format strings
 :: from loxi_ir import *
+:: from py_gen.oftype import gen_unpack_expr
         if type(buf) == loxi.generic_util.OFReader:
             reader = buf
         else:
@@ -36,12 +37,12 @@
 ::     if type(m) == OFPadMember:
         reader.skip(${m.length})
 ::     elif type(m) == OFLengthMember:
-        _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
+        _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
 ::     elif type(m) == OFFieldLengthMember:
 ::         field_length_members[m.field_name] = m
-        _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
+        _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
 ::     elif type(m) == OFTypeMember:
-        _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
+        _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
         assert(_${m.name} == ${m.value})
 ::     elif type(m) == OFDataMember:
 ::         if m.name in field_length_members:
@@ -49,7 +50,7 @@
 ::         else:
 ::             reader_expr = 'reader'
 ::         #endif
-        obj.${m.name} = ${m.oftype.gen_unpack_expr(reader_expr)}
+        obj.${m.name} = ${gen_unpack_expr(m.oftype, reader_expr)}
 ::     #endif
 :: #endfor
 :: if ofclass.name == 'of_match_v3':
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index ffad6cf..8bf11f1 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -28,6 +28,7 @@
 :: import itertools
 :: import of_g
 :: import py_gen.util as util
+:: import py_gen.oftype
 :: include('_copyright.py')
 
 :: include('_autogen.py')
@@ -66,7 +67,7 @@
         if ${m.name} != None:
             self.${m.name} = ${m.name}
         else:
-            self.${m.name} = ${m.oftype.gen_init_expr()}
+            self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
 :: #endfor
 
     def pack(self):
diff --git a/py_gen/templates/oxm.py b/py_gen/templates/oxm.py
index 2a10a5f..d95dd8e 100644
--- a/py_gen/templates/oxm.py
+++ b/py_gen/templates/oxm.py
@@ -27,6 +27,7 @@
 ::
 :: import itertools
 :: import of_g
+:: import py_gen.oftype
 :: include('_copyright.py')
 
 :: include('_autogen.py')
@@ -65,7 +66,7 @@
         if ${m.name} != None:
             self.${m.name} = ${m.name}
         else:
-            self.${m.name} = ${m.oftype.gen_init_expr()}
+            self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
 :: #endfor
 
     def pack(self):