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):