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