Merge into master from pull request #161:
List reading (https://github.com/floodlight/loxigen/pull/161)
diff --git a/loxi_ir/ir.py b/loxi_ir/ir.py
index 6fae92b..6a053c6 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -176,6 +176,10 @@
             raise Exception("Not a fixed length class: {}".format(self.name))
 
     @property
+    def length_member(self):
+        return find(lambda m: type(m) == OFLengthMember, self.members)
+
+    @property
     def has_internal_alignment(self):
         return self.params.get('length_includes_align') == 'True'
 
diff --git a/wireshark_gen/field_info.py b/wireshark_gen/field_info.py
index cf3192c..91c6aba 100644
--- a/wireshark_gen/field_info.py
+++ b/wireshark_gen/field_info.py
@@ -104,6 +104,8 @@
     ('of_barrier_reply', 'type'): 'ofp_type',
     ('of_echo_request', 'type'): 'ofp_type',
     ('of_echo_reply', 'type'): 'ofp_type',
+    ('of_flow_delete', 'type'): 'ofp_type',
+    ('of_flow_add', 'type'): 'ofp_type',
     ('of_match_v3', 'type'): 'ofp_match_type',
     ('of_action_set_nw_ttl', 'type'): 'ofp_action_type',
     ('of_action_set_field', 'type'): 'ofp_action_type',
diff --git a/wireshark_gen/templates/_ofclass_dissector.lua b/wireshark_gen/templates/_ofclass_dissector.lua
index 1e1520f..d327b49 100644
--- a/wireshark_gen/templates/_ofclass_dissector.lua
+++ b/wireshark_gen/templates/_ofclass_dissector.lua
@@ -39,17 +39,39 @@
 -- Discriminator is ${ofclass.discriminator.name}
 :: #endif
 function ${name}(reader, subtree)
+:: field_length_members = []
 :: if ofclass.virtual:
     return ${ofclass.name}_v${version.wire_version}_dissectors[reader.peek(${ofclass.discriminator.offset},${ofclass.discriminator.length}):uint()](reader, subtree)
 :: else:
+:: if not ofclass.is_fixed_length:
+    local _length = reader.peek(${ofclass.length_member.offset}, ${ofclass.length_member.base_length}):uint()
+    local orig_reader = reader
+    reader = orig_reader.slice(_length)
+:: #endif
 :: for m in ofclass.members:
 :: if isinstance(m, OFPadMember):
     reader.skip(${m.length})
 :: continue
 :: #endif
+:: if isinstance(m, OFFieldLengthMember):
+    local _${m.field_name}_length = reader.peek(0, ${m.base_length}):uint()
+:: field_length_members.append(m.field_name)
+:: #endif
+:: if m.oftype.startswith("list"):
+:: class_name = m.oftype.replace('_t)', '').replace('(', '').replace('list', '')
+:: if m.name in field_length_members:
+    read_list(reader.slice(_${m.name}_length), dissect_${class_name}_v${version.wire_version}, subtree, '${class_name}')
+:: else:
+    read_list(reader, dissect_${class_name}_v${version.wire_version}, subtree, '${class_name}')
+:: #endif
+:: if ofclass.has_external_alignment:
+    orig_reader.skip_align()
+:: #endif
+:: else:
 :: field_name = make_field_name(version, ofclass.name, m.name)
 :: reader_name = get_reader(version, ofclass, m)
     ${reader_name}(reader, ${version.wire_version}, subtree, '${field_name}')
+:: #endif
 :: #endfor
     return '${ofclass.name}'
 :: #endif
diff --git a/wireshark_gen/templates/_oftype_readers.lua b/wireshark_gen/templates/_oftype_readers.lua
index 10f13ca..586bbbf 100644
--- a/wireshark_gen/templates/_oftype_readers.lua
+++ b/wireshark_gen/templates/_oftype_readers.lua
@@ -57,11 +57,11 @@
 
 function read_of_match_t(reader, version, subtree, field_name)
     if version == 1 then
-        dissect_of_match_v1_v1(reader, subtree:add(fields[field_name]))
+        dissect_of_match_v1_v1(reader, subtree:add("of_match"))
     elseif version == 2 then
-        dissect_of_match_v2_v2(reader, subtree:add(fields[field_name]))
+        dissect_of_match_v2_v2(reader, subtree:add("of_match"))
     elseif version >= 3 then
-        dissect_of_match_v3_v3(reader, subtree:add(fields[field_name]))
+        dissect_of_match_v3_v3(reader, subtree:add("of_match"))
     end
 end
 
@@ -105,122 +105,6 @@
     end
 end
 
-function read_list_of_action_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-
-    local list_len = 0
-
-    if string.find(field_name,'packet_out') then
-        if version == 1 then
-            list_len = reader.peek(-2,2):uint()
-        else
-            list_len = reader.peek(-8,2):uint()
-        end
-    end
-
-    local list = nil
-    local reader2 = nil
-
-    if list_len == 0 then
-        list = subtree:add(fields[field_name], reader.peek_all(0))
-        reader2 = reader
-    else
-        list = subtree:add(fields[field_name], reader.peek(0, list_len))
-        reader2 = reader.slice(list_len)
-    end
-
-    while not reader2.is_empty() do
-        local action_len = reader2.peek(2, 2):uint()
-        local child_reader = reader2.slice(action_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_action_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-    list:set_text("List of actions")
-end
-
-function read_list_of_port_desc_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    list:set_text("List of port descriptions")
-    while not reader.is_empty() do
-        local port_desc_len = 64
-        local child_reader = reader.slice(port_desc_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_port_desc_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
-function read_list_of_flow_stats_entry_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    list:set_text("List of flow stats entries")
-    while not reader.is_empty() do
-        local stats_len = reader.peek(0,2):uint()
-        local child_reader = reader.slice(stats_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_flow_stats_entry_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
-function read_list_of_port_stats_entry_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    list:set_text("List of port stats entries")
-    while not reader.is_empty() do
-        local stats_len = 112
-        local child_reader = reader.slice(stats_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_port_stats_entry_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
-function read_list_of_table_stats_entry_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    list:set_text("List of table stats entries")
-    while not reader.is_empty() do
-        local stats_len = 24
-        local child_reader = reader.slice(stats_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_table_stats_entry_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
-function read_list_of_queue_stats_entry_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list = subtree:add(fields[field_name], reader.peek_all(0))
-    list:set_text("List of flow stats entries")
-    while not reader.is_empty() do
-        local stats_len = 40
-        local child_reader = reader.slice(stats_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_queue_stats_entry_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
-function read_list_of_packet_queue_t(reader, version, subtree, field_name)
-    -- TODO
-    read_of_octets_t()
-end
-
 function read_of_desc_str_t(reader, version, subtree, field_name)
     read_scalar(reader, subtree, field_name, 256)
 end
@@ -229,52 +113,6 @@
     read_scalar(reader, subtree, field_name, 32)
 end
 
-function read_list_of_oxm_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local list_len = reader.peek(-2,2):uint()
-    local reader2 = reader.slice(list_len - 4)
-    local list = nil
-    if not reader2.is_empty() then
-        list = subtree:add(fields[field_name], reader2.peek_all(0))
-        list:set_text("List of matches")
-    end
-    while not reader2.is_empty() do
-        local match_len = 4 + reader2.peek(3,1):uint()
-        local child_reader = reader2.slice(match_len)
-        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_oxm_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-    subtree:set_text("OXM")
-    reader.skip_align()
-end
-
-function read_list_of_instruction_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local child_subtree = subtree:add(fields[field_name], reader.peek_all(0))
-    local info = of_instruction_dissectors[version](reader, child_subtree)
-    child_subtree:set_text("Instructions")
-end
-
-function read_list_of_bucket_t(reader, version, subtree, field_name)
-    if reader.is_empty() then
-        return
-    end
-    local bucket_list_subtree = subtree:add(fields[field_name], reader.peek_all(0))
-    bucket_list_subtree:set_text("List of buckets")
-    while not reader.is_empty() do
-        local bucket_len = reader.peek(0,2):uint()
-        local child_reader = reader.slice(bucket_len)
-        local child_subtree = bucket_list_subtree:add(fields[field_name], child_reader.peek_all(0))
-        local info = of_bucket_dissectors[version](child_reader, child_subtree)
-        child_subtree:set_text(info)
-    end
-end
-
 function read_of_oxm_t(reader, version, subtree, field_name)
     if reader.is_empty() then
         return
@@ -283,3 +121,16 @@
     local info = of_oxm_dissectors[version](reader, child_subtree)
     child_subtree:set_text(info)
 end
+
+function read_list(reader, dissector, subtree, field_name)
+    if not reader.is_empty() then
+        local list_subtree = subtree:add(field_name .. " list", reader.peek_all(0))
+        while not reader.is_empty() do
+            local atom_subtree = list_subtree:add(field_name, reader.peek_all(0))
+            local info = dissector(reader, atom_subtree)
+            atom_subtree:set_text(info)
+        end
+    else
+        return
+    end
+end
diff --git a/wireshark_gen/templates/openflow.lua b/wireshark_gen/templates/openflow.lua
index bdbe2ec..ea8bb95 100644
--- a/wireshark_gen/templates/openflow.lua
+++ b/wireshark_gen/templates/openflow.lua
@@ -112,66 +112,6 @@
 :: #endfor
 }
 
-local of_action_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_action_v${version.wire_version},
-:: #endfor
-}
-
-local of_instruction_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_instruction_v${version.wire_version},
-:: #endfor
-}
-
-local of_bucket_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_bucket_v${version.wire_version},
-:: #endfor
-}
-
-local of_port_desc_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_port_desc_v${version.wire_version},
-:: #endfor
-}
-
-local of_stats_reply_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_stats_reply_v${version.wire_version},
-:: #endfor
-}
-
-local of_stats_request_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_stats_request_v${version.wire_version},
-:: #endfor
-}
-
-local of_flow_stats_entry_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_flow_stats_entry_v${version.wire_version},
-:: #endfor
-}
-
-local of_port_stats_entry_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_port_stats_entry_v${version.wire_version},
-:: #endfor
-}
-
-local of_table_stats_entry_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_table_stats_entry_v${version.wire_version},
-:: #endfor
-}
-
-local of_queue_stats_entry_dissectors = {
-:: for version in ir:
-    [${version.wire_version}] = dissect_of_queue_stats_entry_v${version.wire_version},
-:: #endfor
-}
-
 :: include('_oftype_readers.lua')
 
 function dissect_of_message(buf, root)