pyloxi: use OFClass directly

It now includes everything we need except pyname.
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 7cd068f..4eafa48 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -36,10 +36,6 @@
 
 ofclasses_by_version = {}
 
-PyOFClass = namedtuple('PyOFClass', ['name', 'pyname', 'members', 'type_members',
-                                     'min_length', 'is_fixed_length',
-                                     'has_internal_alignment', 'has_external_alignment'])
-
 # Return the name for the generated Python class
 def generate_pyname(cls):
     if utils.class_is_action(cls):
@@ -54,50 +50,15 @@
         return cls[3:]
 
 # Create intermediate representation, extended from the LOXI IR
-# HACK the oftype member attribute is replaced with an OFType instance
 def build_ofclasses(version):
     ofclasses = []
     for ofclass in loxi_globals.ir[version].classes:
-        cls = ofclass.name
         if ofclass.virtual:
             continue
 
-        members = []
-        type_members = []
+        ofclass.pyname = generate_pyname(ofclass.name)
+        ofclasses.append(ofclass)
 
-        for m in ofclass.members:
-            if type(m) == OFTypeMember:
-                members.append(m)
-                type_members.append(members[-1])
-            elif type(m) == OFLengthMember:
-                members.append(m)
-            elif type(m) == OFFieldLengthMember:
-                members.append(m)
-            elif type(m) == OFPadMember:
-                members.append(m)
-            elif type(m) == OFDataMember:
-                if utils.class_is_message(ofclass.name) and m.name == 'version':
-                    # HACK move to frontend
-                    members.append(OFTypeMember(
-                        name=m.name,
-                        oftype=m.oftype,
-                        value=version.wire_version,
-                        base_length=m.base_length,
-                        is_fixed_length=m.is_fixed_length,
-                        offset=m.offset))
-                    type_members.append(members[-1])
-                else:
-                    members.append(m)
-
-        ofclasses.append(
-            PyOFClass(name=cls,
-                      pyname=generate_pyname(cls),
-                      members=members,
-                      type_members=type_members,
-                      min_length=ofclass.base_length,
-                      is_fixed_length=ofclass.is_fixed_length,
-                      has_internal_alignment=cls == 'of_action_set_field',
-                      has_external_alignment=cls == 'of_match_v3'))
     return ofclasses
 
 def generate_init(out, name, version):
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index 6116c78..53d6d7a 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,8 +1,9 @@
 :: from loxi_ir import *
 :: import py_gen.oftype
+:: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
 :: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
 class ${ofclass.pyname}(${superclass}):
-:: for m in ofclass.type_members:
+:: for m in type_members:
     ${m.name} = ${m.value}
 :: #endfor
 
diff --git a/py_gen/templates/_pack.py b/py_gen/templates/_pack.py
index 0525ef2..c292525 100644
--- a/py_gen/templates/_pack.py
+++ b/py_gen/templates/_pack.py
@@ -56,12 +56,12 @@
 :: #endfor
 :: if length_member_index != None:
         length = sum([len(x) for x in packed])
-:: if ofclass.has_internal_alignment:
+:: if ofclass.params.get('length_includes_align') == 'True':
         packed.append(loxi.generic_util.pad_to(8, length))
         length += len(packed[-1])
 :: #endif
         packed[${length_member_index}] = ${gen_pack_expr(length_member.oftype, 'length', version=version)}
 :: #endif
-:: if ofclass.has_external_alignment:
+:: if ofclass.params.get('length_includes_align') == 'False':
         packed.append(loxi.generic_util.pad_to(8, length))
 :: #endif
diff --git a/py_gen/templates/_unpack.py b/py_gen/templates/_unpack.py
index 81efb21..c3d3fd3 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -53,6 +53,6 @@
         obj.${m.name} = ${gen_unpack_expr(m.oftype, reader_expr, version=version)}
 ::     #endif
 :: #endfor
-:: if ofclass.has_external_alignment or ofclass.has_internal_alignment:
+:: if ofclass.params.get('length_includes_align') != None:
         reader.skip_align()
 :: #endif
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index 92c4e26..77a1d9f 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -72,7 +72,7 @@
         raise loxi.ProtocolError("unexpected BSN experimenter subtype %#x" % subtype)
 
 parsers = {
-:: sort_key = lambda x: x.type_members[0].value
+:: sort_key = lambda x: x.member_by_name('type').value
 :: msgtype_groups = itertools.groupby(sorted(ofclasses, key=sort_key), sort_key)
 :: for (k, v) in msgtype_groups:
 :: k = util.constant_for_value(version, "ofp_action_type", k)
@@ -85,15 +85,15 @@
 :: #endfor
 }
 
-:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[0].value == 0xffff]
-:: sort_key = lambda x: x.type_members[1].value
+:: experimenter_ofclasses = [x for x in ofclasses if x.member_by_name('type').value == 0xffff]
+:: sort_key = lambda x: x.member_by_name('experimenter').value
 :: experimenter_ofclasses.sort(key=sort_key)
 :: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
 experimenter_parsers = {
 :: for (experimenter, v) in grouped:
     ${experimenter} : {
 :: for ofclass in v:
-        ${ofclass.type_members[2].value}: ${ofclass.pyname}.unpack,
+        ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
 :: #endfor
     },
 :: #endfor
diff --git a/py_gen/templates/instruction.py b/py_gen/templates/instruction.py
index 2817625..5dfafab 100644
--- a/py_gen/templates/instruction.py
+++ b/py_gen/templates/instruction.py
@@ -67,7 +67,7 @@
         raise loxi.ProtocolError("unexpected experimenter id %#x subtype %#x" % (experimenter, subtype))
 
 parsers = {
-:: sort_key = lambda x: x.type_members[0].value
+:: sort_key = lambda x: x.member_by_name('type').value
 :: msgtype_groups = itertools.groupby(sorted(ofclasses, key=sort_key), sort_key)
 :: for (k, v) in msgtype_groups:
 :: k = util.constant_for_value(version, "ofp_instruction_type", k)
@@ -80,15 +80,15 @@
 :: #endfor
 }
 
-:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[0].value == 0xffff]
-:: sort_key = lambda x: x.type_members[1].value
+:: experimenter_ofclasses = [x for x in ofclasses if x.member_by_name('type').value == 0xffff]
+:: sort_key = lambda x: x.member_by_name('experimenter').value
 :: experimenter_ofclasses.sort(key=sort_key)
 :: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
 experimenter_parsers = {
 :: for (experimenter, v) in grouped:
     ${experimenter} : {
 :: for ofclass in v:
-        ${ofclass.type_members[2].value}: ${ofclass.pyname}.unpack,
+        ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
 :: #endfor
     },
 :: #endfor
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index b781bc1..2106ce7 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -170,7 +170,7 @@
         raise loxi.ProtocolError("unexpected experimenter %#x subtype %#x" % (experimenter, subtype))
 
 parsers = {
-:: sort_key = lambda x: x.type_members[1].value
+:: sort_key = lambda x: x.member_by_name('type').value
 :: msgtype_groups = itertools.groupby(sorted(ofclasses, key=sort_key), sort_key)
 :: for (k, v) in msgtype_groups:
 :: k = util.constant_for_value(version, "ofp_type", k)
@@ -271,15 +271,15 @@
 :: #endif
 }
 
-:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[1].value == 4]
-:: sort_key = lambda x: x.type_members[2].value
+:: experimenter_ofclasses = [x for x in ofclasses if x.member_by_name('type').value == 4]
+:: sort_key = lambda x: x.member_by_name('experimenter').value
 :: experimenter_ofclasses.sort(key=sort_key)
 :: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
 experimenter_parsers = {
 :: for (experimenter, v) in grouped:
     ${experimenter} : {
 :: for ofclass in v:
-        ${ofclass.type_members[3].value}: ${ofclass.pyname}.unpack,
+        ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
 :: #endfor
     },
 :: #endfor
diff --git a/py_gen/templates/meter_band.py b/py_gen/templates/meter_band.py
index 37cc6d9..3273314 100644
--- a/py_gen/templates/meter_band.py
+++ b/py_gen/templates/meter_band.py
@@ -54,7 +54,7 @@
 :: #endfor
 
 parsers = {
-:: sort_key = lambda x: x.type_members[0].value
+:: sort_key = lambda x: x.member_by_name('type').value
 :: msgtype_groups = itertools.groupby(sorted(ofclasses, key=sort_key), sort_key)
 :: for (k, v) in msgtype_groups:
 :: k = util.constant_for_value(version, "ofp_meter_band_type", k)
diff --git a/py_gen/templates/oxm.py b/py_gen/templates/oxm.py
index 3d888a7..176a74c 100644
--- a/py_gen/templates/oxm.py
+++ b/py_gen/templates/oxm.py
@@ -58,7 +58,7 @@
 :: #endfor
 
 parsers = {
-:: key = lambda x: x.type_members[0].value
+:: key = lambda x: x.member_by_name('type_len').value
 :: for ofclass in sorted(ofclasses, key=key):
     ${key(ofclass)} : ${ofclass.pyname}.unpack,
 :: #endfor