1 /+ 2 + Copyright 2022 – 2023 Aya Partridge 3 + Distributed under the Boost Software License, Version 1.0. 4 + (See accompanying file LICENSE_1_0.txt or copy at 5 + http://www.boost.org/LICENSE_1_0.txt) 6 +/ 7 module bindbc.sdl.codegen; 8 9 import bindbc.sdl.config: staticBinding; 10 11 /*regex: function decl => makeFnBinds decl 12 ^[ \t]*([A-Za-z0-9_()*\[\]]+) (\w+) ?\(([A-Za-z0-9_()*, .=\[\]]*)\); 13 \t\t[q{$1}, q{$2}, q{$3}], 14 */ 15 enum makeFnBinds = (string[3][] fns) nothrow pure @safe{ 16 string makeFnBinds = ``; 17 string[] symbols; 18 static if(staticBinding){ 19 foreach(fn; fns){ 20 makeFnBinds ~= "\n\t"~fn[0]~` `~fn[1]~`(`~fn[2]~`);`; 21 } 22 }else{ 23 foreach(fn; fns){ 24 if(fn[2].length > 3 && fn[2][$-3..$] == "..."){ 25 //version(WebAssembly){ 26 makeFnBinds ~= "\n\t private "~fn[0]~` function(`~fn[2]~`) _`~fn[1]~`;`; 27 makeFnBinds ~= "\n\t alias "~fn[1]~` = _`~fn[1]~`;`; 28 /+}else{ 29 size_t lastCommaPos = size_t.max; //a guaranteed fail if it's never found 30 string params = ""; 31 bool capture = false; 32 bool firstArg = true; 33 foreach_reverse(i, c; fn[2]){ 34 if(capture){ 35 if(c == ' '){ 36 capture = false; 37 }else{ 38 params = c ~ params; 39 } 40 }else if(c == ','){ 41 capture = true; 42 if(!firstArg){ 43 params = ", " ~ params; 44 }else{ 45 lastCommaPos = i; 46 firstArg = false; 47 } 48 } 49 } 50 string namedParams = fn[2][0..lastCommaPos]; 51 makeFnBinds ~= "\n\tprivate "~fn[0]~` function(`~namedParams~`, void* argPtr) _`~fn[1]~`;`; 52 if(fn[0] == "void"){ 53 makeFnBinds ~= "\n\textern(D) "~fn[0]~` `~fn[1]~`(`~fn[2]~`){ _`~fn[1]~`(`~params~`, _argptr); }`; 54 }else{ 55 makeFnBinds ~= "\n\textern(D) "~fn[0]~` `~fn[1]~`(`~fn[2]~`){ return _`~fn[1]~`(`~params~`, _argptr); }`; 56 } 57 }+/ 58 }else{ 59 makeFnBinds ~= "\n\tprivate "~fn[0]~` function(`~fn[2]~`) _`~fn[1]~`;`; 60 if(fn[0] == "void"){ 61 makeFnBinds ~= "\n\t"~fn[0]~` `~fn[1]~`(`~fn[2]~`){ _`~fn[1]~`(__traits(parameters)); }`; 62 }else{ 63 makeFnBinds ~= "\n\t"~fn[0]~` `~fn[1]~`(`~fn[2]~`){ return _`~fn[1]~`(__traits(parameters)); }`; 64 } 65 } 66 symbols ~= fn[1]; 67 } 68 } 69 return [makeFnBinds] ~ symbols; 70 }; 71 72 enum joinFnBinds = (string[][] list) nothrow pure @safe{ 73 string joined = `extern(C) @nogc nothrow`; 74 string[] symbols; 75 76 static if(staticBinding){ 77 joined ~= `{`; 78 foreach(item; list){ 79 joined ~= item[0]; 80 } 81 }else{ 82 joined ~= ` __gshared{`; 83 foreach(item; list){ 84 joined ~= item[0]; 85 symbols ~= item[1..$]; 86 } 87 } 88 joined ~= "\n}"; 89 90 static if(!staticBinding){ 91 joined ~= "\n\nimport bindbc.loader: SharedLib, bindSymbol;\nvoid bindModuleSymbols(SharedLib lib) @nogc nothrow{"; 92 foreach(symbol; symbols){ 93 joined ~= "\n\tlib.bindSymbol(cast(void**)&_"~symbol~`, "`~symbol~`");`; 94 //joined ~= "\n\tassert("~symbol~` != null);`; 95 } 96 joined ~= "\n}"; 97 } 98 99 return joined; 100 }; 101 102 enum makeDynloadFns = (string name, string[] bindModules) nothrow pure @safe{ 103 string dynloadFns = ` 104 void unloadSDL`~name~`(){ if(lib != invalidHandle) lib.unload(); } 105 106 bool isSDL`~name~`Loaded(){ return lib != invalidHandle; } 107 108 SDL`~name~`Support loadSDL`~name~`(){ 109 const(char)[][libNamesCT.length] libNames = libNamesCT; 110 111 SDL`~name~`Support ret; 112 foreach(name; libNames){ 113 ret = loadSDL`~name~`(name.ptr); 114 //TODO: keep trying until we get the version we want, otherwise default to the highest one? 115 if(ret != SDL`~name~`Support.noLibrary && ret != SDL`~name~`Support.badLibrary) break; 116 } 117 return ret; 118 } 119 120 SDL`~name~`Support loadSDL`~name~`(const(char)* libName){ 121 lib = bindbc.loader.load(libName); 122 if(lib == invalidHandle){ 123 return SDL`~name~`Support.noLibrary; 124 } 125 126 auto errCount = errorCount(); 127 loadedVersion = SDL`~name~`Support.badLibrary; 128 `; 129 130 foreach(mod; bindModules){ 131 dynloadFns ~= "\n\t"~mod~".bindModuleSymbols(lib);"; 132 } 133 134 dynloadFns ~= ` 135 136 if(errCount == errorCount()) loadedVersion = sdl`~name~`Support; //this is a white-lie in order to maintain backwards-compatibility :( 137 return loadedVersion; 138 }`; 139 140 return dynloadFns; 141 };