1import re 2 3import inflection 4 5 6class NamedElement(object): 7 def __init__(self, **kwargs): 8 super(NamedElement, self).__init__() 9 self.name = kwargs.pop("name", "unnamed") 10 self.description = kwargs.pop("description", "") 11 12 if not isinstance(self.name, str): 13 raise AttributeError( 14 "Element interpreted by YAML parser as non-string; likely " 15 "missing quotes around original name." 16 ) 17 18 self.old_namespaces = self.name.split(".") 19 self.old_classname = self.old_namespaces.pop() 20 self.namespaces = [ 21 inflection.underscore(x) for x in self.old_namespaces 22 ] 23 self.classname = inflection.camelize(self.old_classname) 24 25 def __getattribute__(self, name): 26 lam = { 27 "CamelCase": lambda: inflection.camelize(self.name), 28 "camelCase": lambda: NamedElement.lower_camel_case(self.name), 29 "snake_case": lambda: inflection.underscore(self.name), 30 "SNAKE_CASE": lambda: inflection.underscore(self.name).upper(), 31 }.get(name) 32 33 if lam: 34 return NamedElement.__fixup_name(lam()) 35 try: 36 return super(NamedElement, self).__getattribute__(name) 37 except Exception: 38 raise AttributeError( 39 "Attribute '%s' not found in %s.NamedElement" 40 % (name, self.__module__) 41 ) 42 43 def old_cppNamespace(self, typename="server"): 44 return "::".join(self.old_namespaces) + "::" + typename 45 46 def old_cppNamespacedClass(self, typename="server"): 47 return self.old_cppNamespace(typename) + "::" + self.old_classname 48 49 def headerFile(self, typename="common"): 50 return self.name.replace(".", "/") + f"/{typename}.hpp" 51 52 def cppNamespace(self): 53 return "::".join(self.namespaces) 54 55 def cppNamespacedClass(self): 56 return self.cppNamespace() + "::" + self.classname 57 58 def registryPrefix(self, root_prefix): 59 name = inflection.camelize( 60 self.name.replace("xyz.openbmc_project.", "") 61 .replace("com.", "") 62 .replace(".", "_"), 63 uppercase_first_letter=True, 64 ) 65 return root_prefix + "_" + name 66 67 """ Some names are reserved in some languages. Fixup names to avoid using 68 reserved words. 69 """ 70 71 @staticmethod 72 def __fixup_name(name): 73 # List of reserved words from http://en.cppreference.com/w/cpp/keyword 74 cppReserved = frozenset( 75 { 76 "alignas", 77 "alignof", 78 "and", 79 "and_eq", 80 "asm", 81 "auto", 82 "bitand", 83 "bitor", 84 "bool", 85 "break", 86 "case", 87 "catch", 88 "char", 89 "char8_t", 90 "char16_t", 91 "char32_t", 92 "class", 93 "compl", 94 "concept", 95 "const", 96 "consteval", 97 "constexpr", 98 "constinit", 99 "const_cast", 100 "continue", 101 "co_await", 102 "co_return", 103 "co_yield", 104 "decltype", 105 "default", 106 "delete", 107 "do", 108 "double", 109 "dynamic_cast", 110 "else", 111 "enum", 112 "explicit", 113 "export", 114 "extern", 115 "false", 116 "float", 117 "for", 118 "friend", 119 "goto", 120 "if", 121 "inline", 122 "int", 123 "long", 124 "mutable", 125 "namespace", 126 "new", 127 "noexcept", 128 "not", 129 "not_eq", 130 "nullptr", 131 "operator", 132 "or", 133 "or_eq", 134 "private", 135 "protected", 136 "public", 137 "register", 138 "reinterpret_cast", 139 "requires", 140 "return", 141 "short", 142 "signed", 143 "sizeof", 144 "static", 145 "static_assert", 146 "static_cast", 147 "struct", 148 "switch", 149 "template", 150 "this", 151 "thread_local", 152 "throw", 153 "true", 154 "try", 155 "typedef", 156 "typeid", 157 "typename", 158 "union", 159 "unsigned", 160 "using", 161 "virtual", 162 "void", 163 "volatile", 164 "wchar_t", 165 "while", 166 "xor", 167 "xor_eq", 168 } 169 ) 170 171 while name in cppReserved: 172 name = name + "_" 173 174 return name 175 176 # Normally you can use inflection.camelize(string, False) to return 177 # a lowerCamelCase string, but it doesn't work well for acronyms. 178 # An input like "MACAddress" will become "mACAddress". Try to handle 179 # acronyms in a better way. 180 @staticmethod 181 def lower_camel_case(name): 182 # Get the UpperCamelCase variation of the name first. 183 upper_name = inflection.camelize(name) 184 185 # If it is all upper case, return as all lower. ex. "MAC" 186 if re.match(r"^[A-Z0-9]*$", upper_name): 187 return upper_name.lower() 188 189 # If it doesn't start with 2 upper case, it isn't an acronym. 190 if not re.match(r"^[A-Z]{2}", upper_name): 191 return upper_name[0].lower() + upper_name[1:] 192 193 # If it is upper case followed by 'v[0-9]', treat it all as one word. 194 # ex. "IPv6Address" -> "ipv6Address" 195 if re.match(r"^[A-Z]+v[0-9]", upper_name): 196 return re.sub( 197 r"^([A-Z]+)(.*)$", 198 lambda m: m.group(1).lower() + m.group(2), 199 upper_name, 200 ) 201 202 # Anything left has at least two sequential upper-case, so it is an 203 # acronym. Switch all but the last upper-case (which starts the next 204 # word) to lower-case. 205 # ex. "MACAddress" -> "macAddress". 206 return re.sub( 207 r"^([A-Z]+)([A-Z].*)$", 208 lambda m: m.group(1).lower() + m.group(2), 209 upper_name, 210 ) 211