1 /** 2 * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: May 10, 2012 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.Enum; 8 9 import clang.c.Index; 10 import clang.Cursor; 11 import clang.Visitor; 12 import clang.Util; 13 14 import dstep.translator.MacroParser; 15 import dstep.translator.MacroDefinition; 16 import dstep.translator.ConvertCase; 17 import dstep.translator.Context; 18 import dstep.translator.Declaration; 19 import dstep.translator.Output; 20 import dstep.translator.Translator; 21 import dstep.translator.Type; 22 23 void translateEnumConstantDecl( 24 Output output, 25 Context context, 26 Cursor cursor, 27 string spelling, 28 bool last) 29 { 30 import std.conv; 31 import std.format; 32 import std.stdio; 33 34 context.defineSpellingTranslation(cursor, spelling); 35 36 auto expression = parseEnumMember(cursor.tokens, context.typeNames()); 37 38 auto expressionContext = ExpressionContext.make(context); 39 40 expressionContext.scope_ = cursor.lexicalParent; 41 42 auto translated = expression.hasValue 43 ? expression.debraced.translate(expressionContext) 44 : cursor.enum_.value.to!string(); 45 46 output.singleLine( 47 cursor.extent, 48 "%s = %s%s", 49 spelling, 50 translated, 51 last ? "" : ","); 52 } 53 54 void generateEnumAliases(Output output, Context context, Cursor cursor, string spelling) 55 { 56 string subscope = cursorScopeString(context, cursor) ~ "."; 57 58 version (D1) 59 enum fmt = "alias %2$s %1$s;"; 60 else 61 enum fmt = "alias %1$s = %2$s;"; 62 63 foreach (item; cursor.all) 64 { 65 switch (item.kind) 66 { 67 case CXCursorKind.enumConstantDecl: 68 output.singleLine( 69 fmt, 70 item.spelling, 71 subscope ~ item.spelling); 72 break; 73 74 default: 75 break; 76 } 77 } 78 } 79 80 auto renameEnumMembers(MemberRange)(string enumSpelling, MemberRange memberSpellings) 81 { 82 import std.array; 83 import std.algorithm; 84 import std.ascii; 85 import std.range; 86 87 struct Component 88 { 89 string spelling; 90 size_t offset; 91 } 92 93 Component[] separateWords(string spelling) 94 { 95 Component[] result; 96 97 size_t begin = 0; 98 99 if (spelling.canFind!(a => a.isLower)) 100 { 101 for (size_t i = 1; i < spelling.length; ++i) 102 { 103 if (spelling[i].isUpper || 104 (spelling[i - 1] == '_' && spelling[i].isLower)) 105 { 106 if (!result.empty) 107 result.back.offset = begin; 108 109 result ~= Component(spelling[begin..i].stripRight('_'), i); 110 begin = i; 111 } 112 } 113 } 114 else 115 { 116 for (size_t i = 1; i < spelling.length; ++i) 117 { 118 if (spelling[i - 1] == '_') 119 { 120 if (!result.empty) 121 result.back.offset = begin; 122 123 result ~= Component(spelling[begin .. i].stripRight('_'), i); 124 begin = i; 125 } 126 } 127 } 128 129 if (begin != spelling.length) 130 { 131 result ~= Component( 132 spelling[begin .. $].stripRight('_'), 133 spelling.length); 134 } 135 136 return result; 137 } 138 139 size_t trimWhenEnumIsCamelAndMemberIsCapitals( 140 string enumSpelling, 141 string memberSpelling, 142 Component[] enumSeparated) 143 { 144 size_t numWords = 0; 145 size_t offset = 0; 146 string uppercasePrefix; 147 148 while (offset < memberSpelling.length && numWords < enumSeparated.length) 149 { 150 if (!enumSeparated[numWords].spelling.canFind!isLower && 151 memberSpelling[offset .. $] 152 .startsWith(enumSeparated[numWords].spelling)) 153 { 154 uppercasePrefix ~= enumSeparated[numWords].spelling; 155 offset += enumSeparated[numWords].spelling.length; 156 ++numWords; 157 } 158 else 159 { 160 break; 161 } 162 } 163 164 return commonPrefix(uppercasePrefix, enumSpelling).length; 165 } 166 167 size_t refactorSingleton(string enumSpelling, string memberSpelling) 168 { 169 auto enumSeparated = separateWords(enumSpelling); 170 auto memberSeparated = separateWords(memberSpelling); 171 172 alias predicate = (a, b) => 173 a.spelling.startsWith!((a, b) => a.toLower == b.toLower)(b.spelling); 174 175 auto prefix = commonPrefix!(predicate)(enumSeparated, memberSeparated); 176 177 auto tentative = !prefix.empty 178 ? memberSeparated[prefix.length - 1].offset 179 : 0; 180 181 if (!memberSeparated.canFind!((Component a) => a.spelling.canFind!isLower)) 182 { 183 auto special = trimWhenEnumIsCamelAndMemberIsCapitals( 184 enumSpelling, 185 memberSpelling, 186 enumSeparated); 187 188 return max(tentative, special); 189 } 190 else 191 { 192 auto candidate = findSplitBefore(memberSpelling.retro, "_"); 193 return !candidate[1].empty 194 ? max(candidate[1].walkLength, tentative) 195 : tentative; 196 } 197 } 198 199 struct Range 200 { 201 private MemberRange memberSpellings; 202 private size_t prefixSize; 203 204 this(string enumSpelling, MemberRange memberSpellings) 205 { 206 this.memberSpellings = memberSpellings; 207 208 auto candidate = findSplitAfter(enumSpelling, "_"); 209 auto minorPrefix = fold!commonPrefix(memberSpellings); 210 211 if (memberSpellings.walkLength(2) == 1) 212 { 213 prefixSize = refactorSingleton( 214 enumSpelling, 215 memberSpellings.front); 216 } 217 else if (minorPrefix.endsWith("_")) 218 { 219 prefixSize = minorPrefix.length; 220 } 221 else 222 { 223 auto underscoreSplit = findSplitBefore(minorPrefix.retro, "_"); 224 225 prefixSize = !underscoreSplit[1].empty 226 && underscoreSplit[1].canFind!(x => x != '_') 227 ? underscoreSplit[1].walkLength 228 : minorPrefix.length; 229 } 230 } 231 232 bool empty() 233 { 234 return memberSpellings.empty; 235 } 236 237 string front() 238 { 239 return memberSpellings.front[prefixSize .. $]; 240 } 241 242 void popFront() 243 { 244 memberSpellings.popFront(); 245 } 246 } 247 248 return Range(enumSpelling, memberSpellings); 249 } 250 251 void translateEnumDef(Output output, Context context, Cursor cursor) 252 { 253 import std.algorithm; 254 import std.format : format; 255 import std.range; 256 257 auto variables = cursor.variablesInParentScope(); 258 auto anonymous = context.shouldBeAnonymous(cursor); 259 auto spelling = "enum"; 260 261 if (!anonymous || variables || !cursor.isGlobal) 262 spelling = "enum " ~ translateIdentifier(context.translateTagSpelling(cursor)); 263 264 output.subscopeStrong(cursor.extent, "%s", spelling) in 265 { 266 auto members = cursor.children 267 .filter!(cursor => cursor.kind == CXCursorKind.enumConstantDecl); 268 269 size_t length = members.walkLength(); 270 271 if (context.options.renameEnumMembers) 272 { 273 auto renamed = renameEnumMembers( 274 context.translateTagSpelling(cursor), 275 map!(x => x.spelling)(members)); 276 277 foreach (member, spelling; zip(members, enumerate(renamed))) 278 { 279 translateEnumConstantDecl( 280 output, 281 context, 282 member, 283 spelling.value.toCamelCase.translateIdentifier, 284 length == spelling.index + 1); 285 } 286 } 287 else 288 { 289 foreach (index, member; enumerate(members)) 290 { 291 translateEnumConstantDecl( 292 output, 293 context, 294 member, 295 member.spelling, 296 length == index + 1); 297 } 298 } 299 }; 300 301 if ((anonymous && variables) || !cursor.isGlobal || context.options.aliasEnumMembers) 302 generateEnumAliases(context.globalScope, context, cursor, spelling); 303 } 304 305 void translateEnum(Output output, Context context, Cursor cursor) 306 { 307 auto canonical = cursor.canonical; 308 309 if (!context.alreadyDefined(cursor.canonical)) 310 { 311 translateEnumDef(output, context, canonical.definition); 312 context.markAsDefined(cursor); 313 } 314 }