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,"",&reg[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 }