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