frontend: add syntax for inheritance
diff --git a/loxi_front_end/frontend.py b/loxi_front_end/frontend.py
index ab98c26..d25c2eb 100644
--- a/loxi_front_end/frontend.py
+++ b/loxi_front_end/frontend.py
@@ -60,8 +60,8 @@
 
     for decl_ast in ast:
         if decl_ast[0] == 'struct':
-            members = [create_member(m_ast) for m_ast in decl_ast[2]]
-            ofclass = OFClass(name=decl_ast[1], members=members)
+            members = [create_member(m_ast) for m_ast in decl_ast[3]]
+            ofclass = OFClass(name=decl_ast[1], superclass=decl_ast[2], members=members)
             ofinput.classes.append(ofclass)
         if decl_ast[0] == 'enum':
             enum = OFEnum(name=decl_ast[1], values=[(x[0], x[1]) for x in decl_ast[2]])
diff --git a/loxi_front_end/parser.py b/loxi_front_end/parser.py
index 503a05a..7893008 100644
--- a/loxi_front_end/parser.py
+++ b/loxi_front_end/parser.py
@@ -54,7 +54,8 @@
 type_member = P.Group(tag('type') + any_type + identifier + s('==') + integer)
 data_member = P.Group(tag('data') + any_type - identifier)
 struct_member = pad_member | type_member | data_member;
-struct = kw('struct') - identifier - s('{') + \
+parent = (s(':') - identifier) | tag(None)
+struct = kw('struct') - identifier - parent - s('{') + \
          P.Group(P.ZeroOrMore(struct_member - s(';'))) + \
          s('}') - s(';')
 
diff --git a/loxi_ir.py b/loxi_ir.py
index 9a27272..824524b 100644
--- a/loxi_ir.py
+++ b/loxi_ir.py
@@ -72,7 +72,7 @@
 @param name
 @param members List of *Member objects
 """
-OFClass = namedtuple('OFClass', ['name', 'members'])
+OFClass = namedtuple('OFClass', ['name', 'superclass', 'members'])
 
 """
 Normal field
diff --git a/utest/test_frontend.py b/utest/test_frontend.py
index 01b8270..609a262 100755
--- a/utest/test_frontend.py
+++ b/utest/test_frontend.py
@@ -90,7 +90,7 @@
                 ['OFPPC_NO_FWD', 32],
                 ['OFPPC_NO_PACKET_IN', 64]]],
             ['metadata', 'version', '2'],
-            ['struct', 'of_echo_reply', [
+            ['struct', 'of_echo_reply', None, [
                 ['data', 'uint8_t', 'version'],
                 ['type', 'uint8_t', 'type', 3],
                 ['data', 'uint16_t', 'length'],
@@ -100,7 +100,7 @@
                 ['OFPQOFC_BAD_PORT', 0],
                 ['OFPQOFC_BAD_QUEUE', 1],
                 ['OFPQOFC_EPERM', 2]]],
-            ['struct', 'of_packet_queue', [
+            ['struct', 'of_packet_queue', None, [
                 ['data', 'uint32_t', 'queue_id'],
                 ['data', 'uint16_t', 'len'],
                 ['pad', 2],
@@ -111,13 +111,13 @@
         ofinput = frontend.create_ofinput(ast)
         self.assertEquals(set([1, 2]), ofinput.wire_versions)
         expected_classes = [
-            OFClass('of_echo_reply', [
+            OFClass('of_echo_reply', None, [
                 OFDataMember('version', 'uint8_t'), # XXX
                 OFTypeMember('type', 'uint8_t', 3),
                 OFLengthMember('length', 'uint16_t'),
                 OFDataMember('xid', 'uint32_t'),
                 OFDataMember('data', 'of_octets_t')]),
-            OFClass('of_packet_queue', [
+            OFClass('of_packet_queue', None, [
                 OFDataMember('queue_id', 'uint32_t'),
                 OFLengthMember('len', 'uint16_t'),
                 OFPadMember(2),
@@ -140,5 +140,57 @@
         ]
         self.assertEquals(expected_enums, ofinput.enums)
 
+    def test_inheritance(self):
+        ast = parser.parse("""
+#version 1
+
+struct of_queue_prop {
+    uint16_t type;
+    uint16_t len;
+    pad(4);
+};
+
+struct of_queue_prop_min_rate : of_queue_prop {
+    uint16_t type == 1;
+    uint16_t len;
+    pad(4);
+    uint16_t rate;
+    pad(6);
+};
+""")
+
+        # Not testing the parser, just making sure the AST is what we expect
+        expected_ast = [
+            ['metadata', 'version', '1'],
+
+            ['struct', 'of_queue_prop', None, [
+                ['data', 'uint16_t', 'type'],
+                ['data', 'uint16_t', 'len'],
+                ['pad', 4]]],
+
+            ['struct', 'of_queue_prop_min_rate', 'of_queue_prop', [
+                ['type', 'uint16_t', 'type', 1],
+                ['data', 'uint16_t', 'len'],
+                ['pad', 4],
+                ['data', 'uint16_t', 'rate'],
+                ['pad', 6]]],
+        ]
+        self.assertEquals(expected_ast, ast)
+
+        ofinput = frontend.create_ofinput(ast)
+        expected_classes = [
+            OFClass('of_queue_prop', None, [
+                OFDataMember('type', 'uint16_t'),
+                OFLengthMember('len', 'uint16_t'),
+                OFPadMember(4)]),
+            OFClass('of_queue_prop_min_rate', 'of_queue_prop', [
+                OFTypeMember('type', 'uint16_t', 1),
+                OFLengthMember('len', 'uint16_t'),
+                OFPadMember(4),
+                OFDataMember('rate', 'uint16_t'),
+                OFPadMember(6)]),
+        ]
+        self.assertEquals(expected_classes, ofinput.classes)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/utest/test_parser.py b/utest/test_parser.py
index 40d2913..c24665c 100755
--- a/utest/test_parser.py
+++ b/utest/test_parser.py
@@ -42,7 +42,7 @@
 struct foo { };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['struct', 'foo', []]])
+        self.assertEquals(ast, [['struct', 'foo', None, []]])
 
     def test_one_field(self):
         src = """\
@@ -52,7 +52,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['data', 'uint32_t', 'bar']]]])
+            [['struct', 'foo', None, [['data', 'uint32_t', 'bar']]]])
 
     def test_multiple_fields(self):
         src = """\
@@ -64,7 +64,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo',
+            [['struct', 'foo', None,
                 [['data', 'uint32_t', 'bar'],
                  ['data', 'uint8_t', 'baz'],
                  ['data', 'uint64_t', 'abc']]]])
@@ -77,7 +77,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['data', 'uint32_t[4]', 'bar']]]])
+            [['struct', 'foo', None, [['data', 'uint32_t[4]', 'bar']]]])
 
     def test_list_type(self):
         src = """\
@@ -87,7 +87,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['data', 'list(of_action_t)', 'bar']]]])
+            [['struct', 'foo', None, [['data', 'list(of_action_t)', 'bar']]]])
 
     def test_pad_member(self):
         src = """\
@@ -97,7 +97,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['pad', 1]]]])
+            [['struct', 'foo', None, [['pad', 1]]]])
 
     def test_type_member(self):
         src = """\
@@ -107,7 +107,17 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['type', 'uint16_t', 'foo', 0x10]]]])
+            [['struct', 'foo', None, [['type', 'uint16_t', 'foo', 0x10]]]])
+
+    def test_inheritance(self):
+        src = """\
+struct foo : bar {
+    uint16_t foo == 0x10;
+};
+"""
+        ast = parser.parse(src)
+        self.assertEquals(ast,
+            [['struct', 'foo', 'bar', [['type', 'uint16_t', 'foo', 0x10]]]])
 
 class EnumTests(unittest.TestCase):
     def test_empty(self):
@@ -165,7 +175,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', []], ['struct', 'bar', []]])
+            [['struct', 'foo', None, []], ['struct', 'bar', None, []]])
 
     def test_comments(self):
         src = """\
@@ -179,7 +189,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', [['data', 'uint32_t', 'a']]]])
+            [['struct', 'foo', None, [['data', 'uint32_t', 'a']]]])
 
     def test_mixed(self):
         src = """\
@@ -191,9 +201,9 @@
         ast = parser.parse(src)
         self.assertEquals(ast,
             [['metadata', 'version', '1'],
-             ['struct', 'foo', []],
+             ['struct', 'foo', None, []],
              ['metadata', 'version', '2'],
-             ['struct', 'bar', []]])
+             ['struct', 'bar', None, []]])
 
 class TestErrors(unittest.TestCase):
     def syntax_error(self, src, regex):