1 module juliad.types;
2 
3 import std.traits : EnumMembers;
4 import std.array : array;
5 import std.traits : isSomeString, isArray;
6 import std.format : format;
7 import std.string : toLower;
8 import std.range : ElementEncodingType;
9 import std.typecons : nullable, Nullable;
10 import std.conv : to;
11 import std.stdio;
12 
13 import juliad.julia;
14 import juliad.shims;
15 
16 struct JuliaToDType(T) {
17 }
18 
19 enum JuliaType {
20 	@JuliaToDType!void Void,
21 	Uniontype,
22 	Unionall,
23 	Typename,
24 	@JuliaToDType!byte Int8,
25 	@JuliaToDType!short Int16,
26 	@JuliaToDType!int Int32,
27 	@JuliaToDType!long Int64,
28 	@JuliaToDType!ubyte UInt8,
29 	@JuliaToDType!ushort UInt16,
30 	@JuliaToDType!uint UInt32,
31 	@JuliaToDType!ulong UInt64,
32 	@JuliaToDType!bool Bool,
33 	Symbol,
34 	Ssavalue,
35 	Expr,
36 	Globalref,
37 	Gotonode,
38 	Pinode,
39 	Phinode,
40 	Phicnode,
41 	Upsilonnode,
42 	Quotenode,
43 	Newvarnode,
44 	Method_instance,
45 	Code_info,
46 	Method,
47 	Module,
48 	Task,
49 	@JuliaToDType!string String,
50 	@JuliaToDType!float Float16,
51 	@JuliaToDType!float Float32,
52 	@JuliaToDType!double Float64
53 }
54 
55 pragma(msg, __traits(getAttributes, __traits(getMember, JuliaType, "String")));
56 
57 string[] buildTypeStrings() pure @safe {
58 	import std.algorithm.iteration : map;
59 
60 	return [EnumMembers!JuliaType]
61 		.map!(t => to!string(t))
62 		.map!(t => t.toLower())
63 		.array;
64 }
65 
66 enum string[] typeStrings = buildTypeStrings();
67 
68 private string genJlIsType(string type) {
69 	string s = 
70 `bool jl_is_%1$s(_jl_value_t* v) {
71 	return jl_typeis(v, jl_%1$s_type);
72 }`;
73 	return format(s, type);
74 }
75 
76 static foreach(t; typeStrings) {
77 	mixin(genJlIsType(t));
78 }
79 
80 jl_function_t* jl_get_function(jl_module_t* m, string name) {
81 	import std.string : toStringz;
82 	return cast(jl_function_t*)jl_get_global(m, jl_symbol(toStringz(name)));
83 }
84 
85 Nullable!JuliaType getType(jl_value_t* v) {
86 	assert(v !is null, "Passed value must not be null");
87 	enum ems = [EnumMembers!JuliaType];
88 	static foreach(idx, t; typeStrings) {{
89 		enum s = format(`bool i = jl_is_%s(v);`, t);
90 		mixin(s);
91 		if(i) {
92 			return nullable(ems[idx]);
93 		}
94 	}}
95 	return Nullable!(JuliaType).init;
96 }
97 
98 //#define jl_astaggedvalue(v)                                             \
99 //    ((jl_taggedvalue_t*)((char*)(v) - sizeof(jl_taggedvalue_t)))
100 
101 jl_taggedvalue_t* jl_astaggedvalue(jl_value_t* v) {
102 	char* vc = cast(char*)v;
103 	char* mtv = vc - jl_taggedvalue_t.sizeof;
104 	return cast(jl_taggedvalue_t*)mtv;
105 }
106 
107 
108 //#define jl_valueof(v)                                           \
109 //    ((jl_value_t*)((char*)(v) + sizeof(jl_taggedvalue_t)))
110 
111 jl_value_t* jl_valueof(jl_taggedvalue_t* tv) {
112 	char* vc = cast(char*)tv;
113 	char* mtv = vc + jl_taggedvalue_t.sizeof;
114 	return cast(jl_value_t*)mtv;
115 }
116 
117 //#define jl_typeof(v)                                                    \
118 //    ((jl_value_t*)(jl_astaggedvalue(v)->header & ~(uintptr_t)15))
119 
120 jl_value_t* jl_typeof(jl_value_t* v) {
121 	auto h = jl_astaggedvalue(v).header;
122 	uintptr_t ft = ~cast(uintptr_t)15;
123 	uintptr_t and = h & ft;
124 	void* vp = cast(void*)and;
125 	return cast(jl_value_t*)vp;
126 }
127 
128 //#define jl_typeis(v,t) (jl_typeof(v)==(jl_value_t*)(t))
129 bool jl_typeis(jl_value_t* v, jl_datatype_t* dt) {
130 	return jl_typeof(v) is cast(jl_value_t*)dt;
131 }
132 
133 bool jl_is_nothing(jl_value_t* v) {
134 	return v == jl_nothing;
135 }
136 
137 JuliaType dTypeToJuliaType(T)() {
138 	JuliaType ret;
139 	static foreach(em; EnumMembers!JuliaType) {{
140 		enum emStr = to!string(em);
141 		alias emMem = __traits(getMember, JuliaType, emStr);
142 		static if(__traits(getAttributes, emMem).length > 0) {{
143 			alias uda = __traits(getAttributes, emMem)[0];
144 			static if(is(uda : JuliaToDType!F, F)) {
145 				static if(is(T == F)) {
146 					ret = em;
147 					goto retLabel;
148 				}
149 			}
150 		}}
151 	}}
152 	retLabel:
153 	return ret;
154 }
155 
156 unittest {
157 	enum JuliaType d = dTypeToJuliaType!(double);
158 	static assert(d == JuliaType.Float64, format("%s", d));
159 }
160 
161 unittest {
162 	enum JuliaType d = dTypeToJuliaType!(string);
163 	static assert(d == JuliaType.String, format("%s", d));
164 }
165 
166 Nullable!T fromJuliaTo(T)(jl_value_t* v) if(!isSomeString!T && isArray!T) {
167 	enum size_t dim = dimOfArray!T;
168 
169 	//if(!jl_is_array_type(v)) {
170 	//	writeln(__LINE__);
171 	//	return Nullable!(T).init;
172 	//}
173 
174 	jl_array_t* arr = cast(jl_array_t*)v;
175 
176 	const size_t arrDim = to!size_t(jl_array_ndims(v));
177 
178 //	if(arrDim == dim) {
179 //		writeln(__LINE__);
180 //		return Nullable!(T).init;
181 //	}
182 
183 	jl_value_t* elType = jl_tparam0(jl_typeof(cast(jl_value_t*)arr));
184 	Nullable!JuliaType elJType = getType(elType);
185 
186 //	if(elJType.isNull()) {
187 //		writeln(__LINE__);
188 //		return Nullable!(T).init;
189 //	}
190 
191 	alias RootType = ArrayRootType!T;
192 	enum JuliaType tType = dTypeToJuliaType!(RootType);
193 
194 //	if(elJType.get() != tType) {
195 //		writeln(__LINE__);
196 //		return Nullable!(T).init;
197 //	}
198 
199 	static if(dim == 1) {
200 		void* data = jl_array_data(arr);
201 		RootType* tData = cast(RootType*)data;
202 		const size_t len1 = jl_array_dim(arr, 1);
203 		RootType[] tSlice = tData[0 .. len1];
204 
205 		T ret = new T(len1);
206 		foreach(idx, val; tSlice) {
207 			ret[idx] = val;
208 		}
209 		writeln(ret);
210 		return nullable(ret);
211 	} else {
212 		static assert(false, T.stringof);
213 	}
214 }
215 
216 Nullable!T fromJuliaTo(T)(jl_value_t* v) if(!isSomeString!T && !isArray!T) {
217 	Nullable!JuliaType jt = getType(v);
218 	if(jt.isNull()) {
219 		return Nullable!(T).init;
220 	}
221 
222 	enum JuliaType djt = dTypeToJuliaType!(T);
223 	return jt.get() == djt 
224 		? fromJuliaImpl!(T, djt)(v)
225 		: Nullable!(T).init;
226 }
227 
228 Nullable!T fromJuliaTo(T)(jl_value_t* v) if(isSomeString!T) {
229 	import std.string : fromStringz;
230 
231 	Nullable!JuliaType jt = getType(v);
232 	if(jt.isNull()) {
233 		return Nullable!(T).init;
234 	}
235 
236 	enum JuliaType djt = dTypeToJuliaType!(T);
237 	return jt.get() == djt 
238 		? nullable(to!T(fromStringz(jl_string_ptr(v))).idup)
239 		: Nullable!(T).init;
240 }
241 
242 private Nullable!T fromJuliaImpl(T, JuliaType jt)(jl_value_t* v) {
243 	Nullable!T ret;
244 	enum emStr = to!string(jt);
245 	alias emMem = __traits(getMember, JuliaType, emStr);
246 	static if(__traits(getAttributes, emMem).length > 0) {{
247 		alias uda = __traits(getAttributes, emMem)[0];
248 		static if(is(uda : JuliaToDType!F, F)) {{
249 			static if(is(F == bool)) {
250 				enum s = format("ret = nullable(to!bool(jl_unbox_%s(v)));", 
251 						emStr.toLower());
252 			} else {
253 				enum s = format("ret = nullable(jl_unbox_%s(v));", 
254 						emStr.toLower());
255 			}
256 			mixin(s);
257 		}}
258 	}}
259 	return ret;	
260 }
261 
262 jl_value_t* toJulia(V)(V v) if(!isSomeString!V && !isArray!V) {
263 	jl_value_t* ret;
264 	enum JuliaType djt = dTypeToJuliaType!(V);
265 	enum s = format("ret = jl_box_%s(v);", 
266 			to!string(djt).toLower());
267 	mixin(s);
268 	return ret;
269 }
270 
271 jl_value_t* toJulia(V)(V v) if(isSomeString!V) {
272 	import std.string : toStringz;
273 	jl_value_t* ret;
274 	return jl_cstr_to_string(toStringz(to!string(v)));
275 }
276 
277 private template dimOfArray(Arr) {
278 	static if(isArray!Arr) {
279 		enum dimOfArray = 1 + dimOfArray!(ElementEncodingType!Arr);
280 	} else {
281 		enum dimOfArray = 0;
282 	}
283 }
284 
285 unittest {
286 	static assert(dimOfArray!(int) == 0);
287 	static assert(dimOfArray!(int[]) == 1);
288 	static assert(dimOfArray!(int[][]) == 2);
289 	static assert(dimOfArray!(int[][][][][]) == 5);
290 }
291 
292 private template ArrayRootType(Arr) {
293 	static if(isArray!Arr) {
294 		alias ArrayRootType = ArrayRootType!(ElementEncodingType!Arr);
295 	} else {
296 		alias ArrayRootType = Arr;
297 	}
298 }
299 
300 unittest {
301 	static assert(is(ArrayRootType!(int) == int));
302 	static assert(is(ArrayRootType!(int[]) == int));
303 	static assert(is(ArrayRootType!(int[][][][][]) == int));
304 	static assert(is(ArrayRootType!(void*[][][][][]) == void*));
305 }
306 
307 jl_value_t* getArrayType(Arr)() {
308 	alias RootType = ArrayRootType!Arr;
309 	enum size_t dim = dimOfArray!Arr;
310 	enum JuliaType jt = dTypeToJuliaType!RootType;
311 	enum string jtStr = to!string(jt).toLower();
312 	enum string gen = `
313 		jl_value_t* arrayType = jl_apply_array_type(cast(jl_value_t*)jl_%s_type, %s);
314 	`;
315 	mixin(format(gen, jtStr, dim));
316 	return arrayType;
317 }
318 
319 jl_array_t* toJulia(V)(V v) if(!isSomeString!V && isArray!V) {
320 	enum dim = dimOfArray!(V);
321 	alias RootType = ArrayRootType!V;
322 
323 	jl_value_t* arrayType = getArrayType!V;
324 	jl_array_t* ret;
325 
326 	static if(dim == 1) {
327 		ret = jl_alloc_array_1d(arrayType, v.length);
328 		RootType* data = cast(RootType*)jl_array_data(ret);
329 		foreach(idx, val; v) {
330 			data[idx] = val;
331 		}
332 	} else static if(dim == 2) {
333 		ret = jl_alloc_array_2d(arrayType, v.length, v[0].length);
334 		RootType* data = cast(RootType*)jl_array_data(ret);
335 		const size_t dim0 = jl_array_dim(ret, 0);
336 		foreach(idx, it; v) {
337 			foreach(jdx, jt; it) {
338 				data[jdx + dim0 * idx] = jt;
339 			}
340 		}
341 	} else static if(dim == 3) {
342 		ret = jl_alloc_array_3d(arrayType, v.length, v[0].length,
343 				v[0][0].length);
344 		RootType* data = cast(RootType*)jl_array_data(ret);
345 		const size_t dim0 = jl_array_dim(ret, 0);
346 		const size_t dim1 = jl_array_dim(ret, 1);
347 		foreach(idx, it; v) {
348 			foreach(jdx, jt; it) {
349 				foreach(kdx, kt; jt) {
350 					data[(kdx * dim0 * dim1) + (jdx * dim0) + idx] = kt;
351 				}
352 			}
353 		}
354 		writeln(data[0 .. jl_array_len(ret)]);
355 	} else {
356 		static assert(false, V.stringof ~ 
357 				"can not be converted to julia array, PRs welcome");
358 	}
359 	return ret;
360 }