1 module api.util; 2 3 import api.classes; 4 5 import std.range; 6 import std.algorithm.searching, std.algorithm.iteration; 7 import std.path; 8 import std.conv : text; 9 import std..string; 10 11 import asdf; 12 13 class Type 14 { 15 static Type[string] typesByGodotName; 16 static Type[string] typesByDName; 17 18 static Type[] enums; 19 20 GodotClass objectClass; 21 string d; 22 string godot; 23 24 @property string dRef() const { return isRef ? ("Ref!"~d) : d; } 25 26 Type enumParent; 27 28 //alias d this; 29 30 string moduleName() const 31 { 32 if(isPrimitive || isCoreType) return null; 33 return godot.chompPrefix("_").toLower; 34 } 35 36 bool isEnum() const 37 { 38 return godot.startsWith("enum."); 39 } 40 41 bool isPrimitive() const 42 { 43 if(isEnum) return true; 44 return only("int", "bool", "real", "float", "void").canFind(godot); 45 } 46 47 bool isCoreType() const 48 { 49 auto coreTypes = only("AABB", 50 "Array", 51 "Basis", 52 "Color", 53 "Dictionary", 54 "GodotError", 55 "NodePath", 56 "Plane", 57 "PoolByteArray", 58 "PoolIntArray", 59 "PoolRealArray", 60 "PoolStringArray", 61 "PoolVector2Array", 62 "PoolVector3Array", 63 "PoolColorArray", 64 "Quat", 65 "Rect2", 66 "RID", 67 "String", 68 "Transform", 69 "Transform2D", 70 "Variant", 71 "Vector2", 72 "Vector3"); 73 return coreTypes.canFind(godot); 74 } 75 76 bool isRef() const 77 { 78 return objectClass && objectClass.is_reference; 79 } 80 81 /// type should be taken as template arg by methods to allow implicit conversion in ptrcall 82 bool acceptImplicit() const 83 { 84 auto accept = only("Variant", "NodePath"); 85 return accept.canFind(godot); 86 } 87 88 /// prefix for function parameter of this type 89 string dCallParamPrefix() const 90 { 91 if(isRef) return ""; 92 else if(objectClass) return ""; 93 else return "in "; 94 } 95 /// how to pass parameters of this type into ptrcall void** arg 96 string ptrCallArgPrefix() const 97 { 98 if(isPrimitive || isCoreType) return "&"; 99 return ""; 100 //return "cast(godot_object)"; // for both base classes and D classes (through alias this) 101 } 102 103 104 this(string godotName) 105 { 106 godot = godotName; 107 d = godotName.escapeType; 108 } 109 static Type get(string godotName) 110 { 111 if(!godotName.length) return null; // no type (used in base_class) 112 if(Type* ptr = godotName in typesByGodotName) return *ptr; 113 Type ret = new Type(godotName); 114 115 static import api.enums; 116 if(ret.isEnum) 117 { 118 ret.enumParent = get(api.enums.enumParent(godotName)); 119 enums ~= ret; 120 } 121 122 typesByGodotName[godotName] = ret; 123 typesByDName[ret.d] = ret; 124 125 return ret; 126 } 127 static Type deserialize(ref Asdf asdf) 128 { 129 string gn = asdf.get!string(null); 130 Type ret = get(gn); 131 return ret; 132 } 133 } 134 135 /// the default value to use for an argument if none is provided 136 string emptyDefault(in Type type) 137 { 138 import std..string; 139 import std.conv : text; 140 141 switch(type.d) 142 { 143 case "String": 144 return `gs!""`; 145 case "Dictionary": 146 return type.d~".make()"; 147 case "Array": 148 return type.d~".make()"; 149 default: // all default-blittable types 150 { 151 return type.d~".init"; // D's default initializer 152 ///return "null"; 153 } 154 } 155 } 156 157 /++ 158 PoolVector2Array 159 PoolColorArray 160 Array 161 Vector2 162 float 163 Color 164 bool 165 Object 166 PoolVector3Array 167 Vector3 168 Transform2D 169 RID 170 int 171 Transform 172 Rect2 173 String 174 Variant 175 PoolStringArray 176 +/ 177 string escapeDefault(in Type type, string arg) 178 { 179 import std..string; 180 import std.conv : text; 181 182 if(!arg || arg.length == 0) return emptyDefault(type); 183 184 // parse the defaults in api.json 185 switch(type.d) 186 { 187 case "Color": // "1,1,1,1" 188 return "Color("~arg~")"; 189 case "bool": // True, False 190 return arg.toLower; 191 case "Array": // "[]", "Null" - just use the empty one 192 case "Dictionary": 193 case "PoolByteArray": 194 case "PoolIntArray": 195 case "PoolRealArray": 196 case "PoolVector2Array": 197 case "PoolVector3Array": 198 case "PoolStringArray": 199 case "PoolColorArray": // "[PoolColorArray]" - wat? 200 return emptyDefault(type); 201 case "Transform": // "1, 0, 0, 0, 1, 0, 0, 0, 1 - 0, 0, 0" TODO: parse this 202 case "Transform2D": 203 case "RID": // always empty? 204 return emptyDefault(type); // D's default initializer 205 case "Vector2": // "(0, 0)" 206 case "Vector3": 207 case "Rect2": // "(0, 0, 0, 0)" 208 case "AABB": 209 return type.d~arg; 210 case "Variant": 211 if(arg == "Null") return "Variant.nil"; 212 else return arg; 213 case "String": 214 return "gs!\""~arg~"\""; 215 default: // all Object types 216 { 217 if(arg == "Null") return emptyDefault(type); 218 if(arg == "[Object:null]") return emptyDefault(type); 219 return arg; 220 } 221 } 222 } 223 224 string escapeType(string t) 225 { 226 import api.enums : qualifyEnumName; 227 228 t = t.chompPrefix("_"); 229 230 if(t == "Object") return "GodotObject"; 231 if(t == "Error") return "GodotError"; 232 if(t == "float") return "double"; 233 if(t == "int") return "long"; 234 if(t.startsWith("enum.")) return t.qualifyEnumName; 235 return t; 236 } 237 238 string escapeD(string s) 239 { 240 import std.meta; 241 import std.uni, std.utf; 242 /// TODO: there must be a better way of doing this... 243 /// maybe one of the D parser libraries has a list of keywords and basic types 244 245 if(s.toUTF32[0].isNumber) s = "_"~s; // can't start with a number 246 247 alias keywords = AliasSeq!( 248 "class", 249 "interface", 250 "struct", 251 "enum", 252 "bool", 253 "ubyte", 254 "byte", 255 "ushort", 256 "short", 257 "uint", 258 "int", 259 "ulong", 260 "long", 261 "cent", // really? 262 "ucent", 263 "float", 264 "double", 265 "real", 266 "char", 267 "wchar", 268 "dchar", 269 "function", 270 "delegate", 271 "override", 272 "default", 273 "case", 274 "switch", 275 "export", 276 "import", 277 "template", 278 "new", 279 "delete", 280 "return", 281 "with", 282 "align", 283 "in", 284 "out", 285 "ref", 286 "scope", 287 "auto", 288 "init", 289 "body", // for now at least... 290 ); 291 switch(s) 292 { 293 case "Object": return "GodotObject"; 294 case "Error": return "GodotError"; 295 foreach(kw; keywords) 296 { 297 case kw: 298 } 299 return "_"~s; 300 default: 301 return s; 302 } 303 } 304