Andreas Wundsam | d30c107 | 2013-11-15 13:36:57 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2013, Big Switch Networks, Inc. |
| 3 | # |
| 4 | # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with |
| 5 | # the following special exception: |
| 6 | # |
| 7 | # LOXI Exception |
| 8 | # |
| 9 | # As a special exception to the terms of the EPL, you may distribute libraries |
| 10 | # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided |
| 11 | # that copyright and licensing notices generated by LoxiGen are not altered or removed |
| 12 | # from the LoxiGen Libraries and the notice provided below is (i) included in |
| 13 | # the LoxiGen Libraries, if distributed in source code form and (ii) included in any |
| 14 | # documentation for the LoxiGen Libraries, if distributed in binary form. |
| 15 | # |
| 16 | # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler." |
| 17 | # |
| 18 | # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain |
| 19 | # a copy of the EPL at: |
| 20 | # |
| 21 | # http://www.eclipse.org/legal/epl-v10.html |
| 22 | # |
| 23 | # Unless required by applicable law or agreed to in writing, software |
| 24 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 25 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 26 | # EPL for the specific language governing permissions and limitations |
| 27 | # under the EPL. |
| 28 | |
| 29 | import copy |
| 30 | from collections import OrderedDict |
| 31 | from itertools import chain |
| 32 | import logging |
| 33 | |
| 34 | import ir |
| 35 | |
| 36 | def build_unified_ir(name_protocol_map): |
| 37 | class UnifiedClassSpec(object): |
| 38 | def __init__(self, name): |
| 39 | self.name = name |
| 40 | self.members = OrderedDict() |
| 41 | self.superclass_name = None |
| 42 | self.superclass_set = False |
| 43 | self.params = OrderedDict() |
| 44 | self.version_class = OrderedDict() |
| 45 | self.virtual = False |
| 46 | self.base_length = None |
| 47 | self.is_fixed_length = True |
| 48 | |
| 49 | def add_class(self, version, v_class): |
| 50 | for v_member in v_class.members: |
| 51 | if hasattr(v_member, "name"): |
| 52 | if not v_member.name in self.members: |
| 53 | self.members[v_member.name] = v_member |
| 54 | else: |
| 55 | if not type(self.members[v_member.name]) == type(v_member): |
Andreas Wundsam | 343abd9 | 2013-11-16 13:16:07 -0800 | [diff] [blame] | 56 | raise Exception("Error unifying ir class {} - adding version: {} - member_type {} <-> {}".format( |
| 57 | self.name, v_class.protocol.version, self.members[v_member.name], v_member)) |
Andreas Wundsam | d30c107 | 2013-11-15 13:36:57 -0800 | [diff] [blame] | 58 | |
| 59 | if not self.superclass_set: |
| 60 | self.superclass_name = v_class.superclass.name if v_class.superclass else None |
| 61 | else: |
| 62 | if self.superclass_name != v_class.superclass_name: |
Andreas Wundsam | 343abd9 | 2013-11-16 13:16:07 -0800 | [diff] [blame] | 63 | raise Exception("Error unifying ir class {} - adding version {} - superclass: param {} <-> {}".format( |
Andreas Wundsam | d30c107 | 2013-11-15 13:36:57 -0800 | [diff] [blame] | 64 | self.name, v_class.protocol.version, self.superclass_name, v_class.superclass_name)) |
| 65 | |
| 66 | for name, value in v_class.params.items(): |
| 67 | if not name in self.params: |
| 68 | self.params[name] = value |
| 69 | else: |
| 70 | if self.params[name] != value: |
Andreas Wundsam | 343abd9 | 2013-11-16 13:16:07 -0800 | [diff] [blame] | 71 | raise Exception("Error unifying ir class {} - adding version: {} - param {} <-> {}".format( |
Andreas Wundsam | d30c107 | 2013-11-15 13:36:57 -0800 | [diff] [blame] | 72 | self.name, v_class.protocol.version, self.params[name], value)) |
| 73 | |
| 74 | if v_class.virtual: |
| 75 | self.virtual = True |
| 76 | |
| 77 | if not v_class.is_fixed_length: |
| 78 | self.is_fixed_length = False |
| 79 | |
| 80 | if self.base_length is None: |
| 81 | self.base_length = v_class.base_length |
| 82 | elif self.base_length != v_class.base_length: |
| 83 | self.is_fixed_length = False |
| 84 | if self.base_length > v_class.base_length: |
| 85 | self.base_length = v_class.base_length |
| 86 | self.version_class[version] = v_class |
| 87 | |
| 88 | class UnifiedEnumSpec(object): |
| 89 | def __init__(self, name): |
| 90 | self.name = name |
| 91 | self.entries = {} |
| 92 | self.params = {} |
| 93 | self.version_enums = OrderedDict() |
| 94 | |
| 95 | def add_enum(self, version, v_enum): |
| 96 | for e in v_enum.entries: |
| 97 | if not e.name in self.entries: |
| 98 | self.entries[e.name] = ir.OFEnumEntry(e.name, e.value, copy.copy(e.params)) |
| 99 | else: |
| 100 | entry = self.entries[e.name] |
| 101 | for name, value in e.params.items(): |
| 102 | if not name in entry.params: |
| 103 | entry.params[name] = value |
| 104 | elif entry.params[name] != value: |
| 105 | raise Exception("Error unifying ir enum {} - adding version: param {} <-> {}".format( |
| 106 | self.name, entry.params[name], value)) |
| 107 | for name, value in v_enum.params.items(): |
| 108 | if not name in self.params: |
| 109 | self.params[name] = value |
| 110 | else: |
| 111 | if self.params[name] != value: |
| 112 | if name == "wire_type": |
| 113 | self.params[name] = None |
| 114 | else: |
| 115 | raise Exception("Error unifying ir enum {} - adding version: {} param {} <-> {}".format( |
| 116 | self.name, v_enum.protocol.version, self.params[name], value)) |
| 117 | |
| 118 | self.version_enums[version]=v_enum |
| 119 | |
| 120 | u_name_classes = OrderedDict() |
| 121 | u_name_enums = OrderedDict() |
| 122 | |
| 123 | for version, protocol in name_protocol_map.items(): |
| 124 | assert isinstance(version, ir.OFVersion) |
| 125 | for v_class in protocol.classes: |
| 126 | name = v_class.name |
| 127 | if not name in u_name_classes: |
| 128 | u_name_classes[name] = UnifiedClassSpec(name) |
| 129 | spec = u_name_classes[name] |
| 130 | spec.add_class(version, v_class) |
| 131 | |
| 132 | for v_enum in protocol.enums: |
| 133 | name = v_enum.name |
| 134 | if not name in u_name_enums: |
| 135 | u_name_enums[name] = UnifiedEnumSpec(name) |
| 136 | spec = u_name_enums[name] |
| 137 | spec.add_enum(version, v_enum) |
| 138 | |
| 139 | unified_enums = tuple(ir.OFEnum(name=s.name, entries=tuple(s.entries.values()), params=s.params) for s in u_name_enums.values()) |
| 140 | unified_classes = OrderedDict() |
| 141 | for name, spec in u_name_classes.items(): |
| 142 | u = ir.OFUnifiedClass( |
| 143 | name = spec.name, |
| 144 | version_classes=spec.version_class, |
| 145 | superclass=None if not spec.superclass_name else unified_classes[spec.superclass_name], |
| 146 | members=spec.members.values(), |
| 147 | virtual=spec.virtual, |
| 148 | params=spec.params, |
| 149 | base_length=spec.base_length, |
| 150 | is_fixed_length=spec.is_fixed_length) |
| 151 | unified_classes[name] = u |
| 152 | |
| 153 | unified = ir.OFProtocol(version=None, classes = tuple(unified_classes.values()), enums=unified_enums) |
| 154 | for e in chain(unified.classes, unified.enums): |
| 155 | e.protocol = unified |
Andreas Wundsam | a85acd7 | 2013-11-15 15:18:54 -0800 | [diff] [blame] | 156 | return unified |