1 // Module to generate usefull TypeInfo
2 module mutils.type_info;
3 
4 import std.algorithm : minElement;
5 import std.meta : AliasSeq;
6 import std.stdio;
7 import std.traits;
8 
9 struct TypeData {
10 	string name;
11 	long size;
12 	long aligment;
13 	Field[] fields;
14 	bool isCustomVector;
15 }
16 
17 struct Field {
18 	string name;
19 	TypeData typeData;
20 	string stringUda;
21 }
22 
23 TypeData getTypeData(T)() {
24 	TypeData data;
25 	data.name = T.stringof;
26 	data.size = T.sizeof;
27 	data.aligment = T.alignof;
28 
29 	static if (is(T == struct)) {
30 		alias TFields = Fields!T;
31 		alias Names = FieldNameTuple!T;
32 		foreach (i, F; TFields) {
33 			alias TP = AliasSeq!(__traits(getAttributes, T.tupleof[i]));
34 			string stringUda;
35 			static if (is(typeof(TP[0]) == string)) {
36 				stringUda = TP[0];
37 
38 			}
39 			data.fields ~= Field(Names[i], getTypeData!F, stringUda);
40 		}
41 	}
42 	return data;
43 }
44 
45 // Working with function overloads migh be painful so generate some data for it
46 
47 // Maps to lua constant values
48 enum LuaType {
49 	none = -1,
50 	nil = 0,
51 	boolean = 1,
52 	lightuserdata = 2,
53 	number = 3,
54 	string = 4,
55 	table = 5,
56 	function_ = 6,
57 	userdata = 7,
58 	thread = 9
59 }
60 
61 LuaType toLuaType(T)() {
62 	static if (isIntegral!T || isFloatingPoint!T) {
63 		return LuaType.number;
64 	} else static if (is(T == string)) {
65 		return LuaType..string;
66 	} else {
67 		return LuaType.userdata;
68 	}
69 }
70 
71 struct ParameterData {
72 	TypeData typeData;
73 	LuaType luaType;
74 	bool hasDefaultValue;
75 }
76 
77 struct OverloadData {
78 	TypeData returnTypeData;
79 	ParameterData[] parameters;
80 
81 	size_t minParametersNum() {
82 		size_t parsNum = parameters.length;
83 		foreach (p; parameters) {
84 			parsNum -= p.hasDefaultValue;
85 		}
86 		return parsNum;
87 	}
88 
89 	bool callableUsingArgsNum(size_t argsNum) {
90 		return (argsNum >= minParametersNum && argsNum <= parameters.length);
91 	}
92 }
93 
94 struct ProcedureData {
95 	string name;
96 	OverloadData[] overloads;
97 
98 	size_t minParametersNum() {
99 
100 		return minElement!"a.minParametersNum"(overloads).minParametersNum;
101 	}
102 
103 }
104 
105 // To generate data for normal function give module in place of StructType
106 ProcedureData getProcedureData(alias StructType, string procedureName)() {
107 	ProcedureData procedureData;
108 	procedureData.name = procedureName;
109 	alias overloads = typeof(__traits(getOverloads, StructType, procedureName));
110 	foreach (overloadNum, overload; overloads) {
111 		OverloadData overloadData;
112 
113 		alias FUN = overloads[overloadNum];
114 		alias Parms = Parameters!FUN;
115 		alias ParmsDefault = ParameterDefaults!(__traits(getOverloads,
116 				StructType, procedureName)[overloadNum]);
117 		enum bool hasReturn = !is(ReturnType!FUN == void);
118 		enum bool hasParms = Parms.length > 0;
119 
120 		overloadData.returnTypeData = getTypeData!(ReturnType!FUN);
121 
122 		foreach (ParNum, Par; Parms) {
123 			ParameterData parameterData;
124 			parameterData.typeData = getTypeData!Par;
125 			parameterData.hasDefaultValue = (!is(ParmsDefault[ParNum] == void));
126 			parameterData.luaType = toLuaType!Par;
127 			overloadData.parameters ~= parameterData;
128 		}
129 
130 		procedureData.overloads ~= overloadData;
131 	}
132 
133 	return procedureData;
134 }
135 
136 unittest {
137 	static struct Test {
138 		int procA(int a, int b, int c = 10, int d = 10) {
139 			return 0;
140 		}
141 
142 		int procA(int a, int b = 100, int c = 10) {
143 			return 0;
144 		}
145 
146 		int proc() {
147 			return 0;
148 		}
149 
150 		int proc(int a) {
151 			return 0;
152 		}
153 
154 		int proc(int a, int b) {
155 			return 0;
156 		}
157 
158 		int proc(int a, int b = 100, int c = 10) {
159 			return 0;
160 		}
161 
162 		int proc(string a, int b) {
163 			return 0;
164 		}
165 
166 		int proc(int a, string b) {
167 			return 0;
168 		}
169 
170 		int proc(int a, string b, double c) {
171 			return 0;
172 		}
173 	}
174 	// Data for procedure
175 	enum ProcedureData procedureDataA = getProcedureData!(Test, "procA");
176 	static assert(procedureDataA.overloads.length == 2);
177 	static assert(procedureDataA.overloads[0].parameters.length == 4);
178 	static assert(procedureDataA.overloads[1].parameters.length == 3);
179 	static assert(procedureDataA.overloads[0].minParametersNum == 2);
180 	static assert(procedureDataA.overloads[1].minParametersNum == 1);
181 	static assert(procedureDataA.minParametersNum == 1);
182 	static assert(procedureDataA.overloads[0].returnTypeData.name == "int");
183 	static assert(procedureDataA.overloads[0].parameters[0].typeData.name == "int");
184 
185 	// Data for function
186 	enum ProcedureData funcData = getProcedureData!(mutils.type_info, "func");
187 	static assert(funcData.overloads.length == 7);
188 	static assert(funcData.overloads[0].parameters.length == 0);
189 	static assert(funcData.overloads[1].parameters.length == 1);
190 	static assert(funcData.overloads[2].parameters.length == 2);
191 	static assert(funcData.overloads[3].parameters.length == 3);
192 	static assert(funcData.overloads[4].parameters.length == 2);
193 	static assert(funcData.overloads[5].parameters.length == 2);
194 	static assert(funcData.overloads[6].parameters.length == 3);
195 	static assert(funcData.overloads[3].minParametersNum == 1);
196 	static assert(funcData.minParametersNum == 0);
197 	static assert(funcData.overloads[0].returnTypeData.name == "int");
198 	static assert(funcData.overloads[1].parameters[0].typeData.name == "int");
199 }
200 
201 // Used only for tests
202 private:
203 int func() {
204 	return 0;
205 }
206 
207 int func(int a) {
208 	return 0;
209 }
210 
211 int func(int a, int b) {
212 	return 0;
213 }
214 
215 int func(int a, int b = 100, int c = 10) {
216 	return 0;
217 }
218 
219 int func(string a, int b) {
220 	return 0;
221 }
222 
223 int func(int a, string b) {
224 	return 0;
225 }
226 
227 int func(int a, string b, double c) {
228 	return 0;
229 }