1 module janet.func; 2 3 import janet.c; 4 5 import janet; 6 7 /** 8 Emulates the effects of janet_pcall using a thread-local fiber 9 and D-like varargs. 10 */ 11 @nogc Janet callJanet(T...)(JanetFunction* fun,T args) 12 { 13 Janet[T.length] wrappedArgs = []; 14 static foreach(i,v;args) 15 { 16 wrappedArgs[i]=janetWrap(v); 17 } 18 Janet j; 19 enum argc = T.length; 20 const(Janet*) argv = cast(const(Janet*))(wrappedArgs); 21 JanetFiber* fiber = janet_fiber_reset(janet_root_fiber().get,fun,argc,argv); 22 fiber.table = janet_table(0); 23 fiber.table.proto = coreEnv; 24 const int result = janet_continue(fiber,janet_wrap_nil(),&j); 25 debug assert(result==0,"Function errored! "~j.as!string); 26 return j; 27 } 28 29 import std.traits : functionAttributes, FunctionAttribute; 30 31 private @nogc Janet janetReturn(alias func,Args...)(Args args) 32 if(functionAttributes!func & FunctionAttribute.nogc) 33 { 34 import std.traits : ReturnType; 35 static if(is(ReturnType!func == void)) 36 { 37 func(args); 38 return janet_wrap_nil(); 39 } 40 else 41 { 42 return janetWrap!(ReturnType!func)(func(args)); 43 } 44 } 45 46 private Janet janetReturn(alias func,Args...)(Args args) 47 if(!(functionAttributes!func & FunctionAttribute.nogc)) 48 { 49 import std.traits : ReturnType; 50 static if(is(ReturnType!func == void)) 51 { 52 func(args); 53 return janet_wrap_nil(); 54 } 55 else 56 { 57 return janetWrap!(ReturnType!func)(func(args)); 58 } 59 } 60 61 62 /** 63 Wraps around a function, allowing it to be called in Janet. 64 */ 65 template makeJanetCFunc(alias func) 66 { 67 import std.traits : Parameters,isNestedFunction,arity,functionAttributes,SetFunctionAttributes,FunctionTypeOf; 68 import std.typecons : Tuple; 69 import std.meta; 70 extern(C) static Janet ourJanetFunc (int argc, Janet* argv) 71 { 72 static foreach(overload;__traits(getOverloads,__traits(parent,func),__traits(identifier,func))) 73 { 74 Tuple!(Parameters!overload) args; 75 static if(arity!overload == 0) 76 { 77 if(argc == 0) 78 { 79 return janetReturn!(overload)(); 80 } 81 } 82 else 83 { 84 if(argc == arity!overload) 85 { 86 bool argsCorrect = true; 87 static foreach(i;0..args.length) 88 { 89 argsCorrect = argsCorrect && argv[i].janetCompatible!(Parameters!overload[i]); 90 if(argsCorrect) 91 { 92 args[i] = (&argv[i]).as!(Parameters!overload[i]); 93 } 94 } 95 if(argsCorrect) 96 { 97 return janetReturn!(overload)(args.expand); 98 } 99 } 100 } 101 } 102 return janet_wrap_nil(); 103 } 104 alias JanetDFunction = SetFunctionAttributes!(JanetCFunction,"C",functionAttributes!func); 105 JanetDFunction makeJanetCFunc() 106 { 107 return cast(JanetDFunction)(&ourJanetFunc); 108 } 109 } 110 111 import std.traits : isPointer,PointerTarget; 112 113 /** 114 The same, but requires a type and an instance of that type as an argument. 115 This is due to many class methods requiring context pointers. 116 This is mostly only useful for class/struct registering. 117 */ 118 template makeJanetCFunc(alias func,T) 119 if(is(T == class) || isPointer!T && is(PointerTarget!T == struct)) 120 { 121 import std.traits : Parameters,isNestedFunction,arity,functionAttributes,SetFunctionAttributes,FunctionTypeOf,ReturnType; 122 import std.typecons : Tuple; 123 T obj; 124 alias JanetDFunction = SetFunctionAttributes!(JanetCFunction,"C",functionAttributes!func); 125 JanetDFunction makeJanetCFunc(T argObj) 126 { 127 obj = argObj; 128 return cast(JanetDFunction)(&ourJanetFunc); 129 } 130 extern(C) static Janet ourJanetFunc (int argc, Janet* argv) 131 { 132 foreach(overload;__traits(getOverloads,__traits(parent,func),__traits(identifier,func))) 133 { 134 Tuple!(Parameters!overload) args; 135 static if(arity!overload == 0) 136 { 137 if(argc == 0) 138 { 139 static if(is(ReturnType!overload == void)) 140 { 141 mixin("obj."~__traits(identifier,overload)~"();"); 142 return janet_wrap_nil(); 143 } 144 else 145 { 146 return janetWrap!(ReturnType!overload)(mixin("obj."~__traits(identifier,overload)~"()")); 147 } 148 149 } 150 } 151 else 152 { 153 if(argc == arity!overload) 154 { 155 bool argsCorrect = true; 156 static foreach(i;0..args.length) 157 { 158 argsCorrect = argsCorrect && argv[i].janetCompatible!(Parameters!overload[i]); 159 if(argsCorrect) 160 { 161 args[i] = (&argv[i]).as!(Parameters!overload[i]); 162 } 163 } 164 if(argsCorrect) 165 { 166 static if(is(ReturnType!overload == void)) 167 { 168 mixin("obj."~__traits(identifier,overload)~"(args.expand)"); 169 return janet_wrap_nil(); 170 } 171 else 172 { 173 return janetWrap!(ReturnType!overload)(mixin("obj."~__traits(identifier,overload)~"(args.expand)")); 174 } 175 } 176 } 177 } 178 } 179 return janet_wrap_nil(); 180 } 181 } 182 183 /** 184 Makes a function globally available with Janet. 185 */ 186 @nogc void registerFunctionWithJanet(alias func,string documentation = "")() 187 { 188 JanetReg[2] reg; 189 reg[0].name = cast(const(char)*)__traits(identifier,func); 190 reg[0].cfun = makeJanetCFunc!func; 191 reg[0].documentation = cast(const(char)*)(documentation); 192 janet_cfuns(coreEnv,"",®[0]); 193 } 194 195 version(unittest) 196 { 197 int foo(int x) 198 { 199 return x+1; 200 } 201 int bar(int y) 202 { 203 return y+2; 204 } 205 } 206 207 unittest 208 { 209 import std.stdio : writeln; 210 writeln("Performing CFunction register test."); 211 registerFunctionWithJanet!(foo,"Returns the input plus one."); 212 registerFunctionWithJanet!(bar,"Returns the input plus two."); 213 writeln("Functions registered."); 214 assert(doFile("./source/tests/dtests/function.janet") == 0,"Function failed!"); 215 }