1 module bamboo.codegen.field_declarations.atomic; 2 3 import bamboo.codegen.field_declarations; 4 5 string generateAtomic(AtomicField field, bool stub) 6 { 7 string generated; 8 string fieldType; 9 10 bool isComplex = field.parameters.length > 1; 11 bool isProperty = field.name.startsWith("set"); 12 13 string name = (() @trusted{ 14 if (!isProperty) 15 { 16 return field.name; 17 } 18 19 if (field.name[3 .. $].all!(x => isUpper(x))) 20 { 21 return field.name[3 .. $].toLower; 22 } 23 24 return cast(char)(field.name[3].toLower) ~ field.name[4 .. $]; 25 })(); 26 27 int counter = field.id; 28 29 foreach (parameter; field.parameters) 30 { 31 counter++; 32 parameter.symbol = generateName(parameter.symbol, parameter.parameterTypeName, counter); 33 } 34 35 string generateUnderlyingAtomicField() 36 { 37 string format; 38 39 if (isComplex) 40 { 41 format ~= "private Tuple!("; 42 foreach (parameter; field.parameters) 43 { 44 string[] parts = generateDefinition(parameter).split(' '); 45 string type = parts[0]; 46 string param = parts[1]; 47 48 format ~= type ~ "," ~ "`" ~ param ~ "`,"; 49 } 50 format ~= ") _" ~ name ~ "; "; 51 } 52 else if (field.parameters.length == 1) 53 { 54 auto parameter = field.parameters[0]; 55 format ~= "private " ~ generateDefinition(parameter).split(' ')[0] ~ " _" ~ name ~ ";"; 56 } 57 else 58 { 59 assert(0, name ~ " is a setter with no value!"); 60 } 61 62 return format; 63 } 64 65 string generateAtomicDeclaration(out string fieldType) 66 { 67 string format; 68 69 format ~= "@FieldId(" ~ field.id.to!string ~ ") "; 70 71 if (isProperty) 72 { 73 if (isComplex && stub) 74 { 75 fieldType = "typeof(_" ~ name ~ ")"; 76 } 77 else if (!isComplex) 78 { 79 fieldType = generateDefinition(field.parameters[0]).split(' ')[0]; 80 } 81 } 82 83 foreach (keyword; field.keywords) 84 { 85 format ~= "@" ~ keyword ~ " "; 86 } 87 88 if (!stub) 89 { 90 format ~= " abstract "; 91 } 92 format ~= " void "; 93 94 format ~= name; 95 96 format ~= "("; 97 format ~= generateParameterListFor(field); 98 format ~= ") "; 99 100 if (!isComplex && isProperty) 101 { 102 format ~= "@property"; 103 } 104 105 return format; 106 } 107 108 string generateComplexSetter() 109 { 110 return q{ 111 void %1$s(T)(T value) if(__traits(compiles, %1$s(value.expand))) 112 { 113 %1$s(value.expand); 114 } 115 }.format(name); 116 } 117 118 string generateContracts() 119 { 120 string contracts; 121 string format; 122 123 foreach (parameter; field.parameters) 124 { 125 contracts ~= generateContractFor(parameter); 126 } 127 128 if (contracts.length > 0) 129 { 130 format ~= " in {"; 131 format ~= contracts; 132 format ~= "}"; 133 if (stub) 134 { 135 format ~= "body"; 136 } 137 } 138 139 return format; 140 } 141 142 string generateBody() 143 { 144 string format; 145 146 if (stub) 147 { 148 format ~= "{"; 149 if (isProperty) 150 { 151 if (isComplex) 152 { 153 foreach (parameter; field.parameters) 154 { 155 format ~= "_" ~ name ~ "." ~ parameter.symbol; 156 format ~= "=" ~ parameter.symbol ~ ";"; 157 } 158 } 159 else if (field.parameters.length == 1) 160 { 161 auto parameter = field.parameters[0]; 162 format ~= "_" ~ name; 163 format ~= "=" ~ parameter.symbol ~ ";"; 164 } 165 } 166 else 167 { 168 format ~= `assert(0, "Override body not defined for ` ~ field.name ~ `!");`; 169 } 170 171 format ~= "}"; 172 } 173 else 174 { 175 format ~= ";"; 176 } 177 178 return format; 179 } 180 181 string generateGetter() 182 { 183 string format; 184 185 format ~= "@FieldId(" ~ field.id.to!string ~ ") "; 186 if (!isComplex) 187 { 188 format ~= "@property "; 189 } 190 191 if (!stub) 192 { 193 format ~= "abstract " ~ fieldType ~ " " ~ name ~ "();"; 194 } 195 else 196 { 197 format ~= fieldType ~ " " ~ name ~ "() { "; 198 format ~= "return _" ~ name ~ ";"; 199 format ~= "}"; 200 } 201 202 return format; 203 } 204 205 autogenParameterNames(field); 206 207 if (stub && isProperty) 208 { 209 generated ~= generateUnderlyingAtomicField(); 210 } 211 212 generated ~= generateAtomicDeclaration(fieldType); 213 generated ~= generateContracts(); 214 generated ~= generateBody(); 215 216 if (isProperty) 217 { 218 if (isComplex) 219 { 220 generated ~= generateComplexSetter(); 221 } 222 generated ~= generateGetter(); 223 generated ~= text("alias ", field.name, " = ", name, ";"); // setField 224 generated ~= text("alias get", field.name[3 .. $], " = ", name, ";"); // getField 225 } 226 227 return generated; 228 } 229 230 private: 231 232 /** 233 * This function generates parameter names from a field's name. 234 * 235 * Notes: 236 * Some fields follow the pattern of, e.g., `setXYZ(float32, float32, float32)`. 237 * Using the name generator, ugly parameter names will be generated when 238 * enough semantic information is already available to properly generate 239 * parameter names. 240 * 241 * This method would take `setXYZ(float32, float32, float32)` and transform 242 * it to `setXYZ(float32 x, float32, y, float32 z)`, for example. 243 */ 244 void autogenParameterNames(AtomicField field) 245 { 246 if (!field.symbol.startsWith("set")) 247 { 248 return; 249 } 250 251 if (field.parameters.length == 1) 252 { 253 string name = field.symbol[3 .. $]; 254 field.parameters[0].symbol = cast(char) name[0].toLower ~ name[1 .. $]; 255 } 256 257 foreach (parameter; field.parameters) 258 { 259 if (parameter.symbol.length > 0) 260 { 261 return; 262 } 263 } 264 265 string name = field.symbol[3 .. $]; 266 if (name.filter!(a => isUpper(cast(dchar) a))().array.length != field.parameters.length) 267 { 268 return; 269 } 270 271 int start; 272 int index = 1; 273 int param; 274 275 string[] names; 276 277 void appendName() 278 { 279 if (index - start == 1) 280 { 281 names ~= [cast(char)(name[start].toLower)]; 282 } 283 else 284 { 285 names ~= cast(char)(name[start].toLower) ~ name[start + 1 .. index]; 286 } 287 288 } 289 290 foreach (value; name[1 .. $]) 291 { 292 if (isUpper(cast(dchar) value)) 293 { 294 appendName(); 295 start = index; 296 } 297 index++; 298 } 299 if (names.length != field.parameters.length) 300 { 301 appendName(); 302 } 303 304 foreach (i, para; field.parameters) 305 { 306 para.symbol = names[i]; 307 } 308 }