1 /**
2 Copyright: Copyright (c) 2016-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module datadriven.query;
7 
8 import std.conv : to;
9 import std.meta : Filter, staticMap;
10 import std.traits : TemplateArgsOf, Unqual;
11 
12 import datadriven.api;
13 
14 auto componentQuery(ComponentStorages...)(ComponentStorages storages)
15 {
16 	return ComponentQuery!(ComponentStorages)(storages);
17 }
18 
19 template StorageComponentTypePtr(T)
20 {
21 	alias StorageComponentTypePtr = T.ComponentType*;
22 }
23 
24 struct ComponentQuery(ComponentStorages...)
25 {
26 	mixin(genComponentStorages!ComponentStorages);
27 	mixin(genRowDefinition!ComponentStorages);
28 	alias Components = staticMap!(StorageComponentTypePtr, Filter!(isAnyComponentStorage, ComponentStorages));
29 
30 	// Sorts component storages by length.
31 	// Then iterates by smallest storage to reduce number of lookups.
32 	int opApply(scope int delegate(Row) dg)
33 	{
34 		static struct StorageLength
35 		{
36 			size_t index;
37 			size_t length;
38 			int opCmp(StorageLength other) { return cast(int)(cast(long)length - cast(long)other.length); }
39 		}
40 
41 		StorageLength[ComponentStorages.length] lengths;
42 
43 		foreach(size_t i, cs; ComponentStorages)
44 		{
45 			lengths[i].index = i;
46 			lengths[i].length = mixin(genComponentStorageName!(cs, i) ~ ".length");
47 		}
48 
49 		import std.algorithm : sort;
50 		sort(lengths[]);
51 
52 		// Iteration variables
53 		int result = 0;
54 		Row row;
55 
56 		mixin(genComponentIterationCode!ComponentStorages);
57 
58 		return result;
59 	}
60 
61 	// ditto
62 	int opApply(scope int delegate(EntityId id, Components) dg)
63 	{
64 		int iterate(Row row)
65 		{
66 			if (auto res = dg(row.tupleof)) return res;
67 			return 0;
68 		}
69 
70 		return opApply(&iterate);
71 	}
72 }
73 
74 
75 ///////////////////////////////////////////////////////////////////////////////
76 // QUERY CODE GEN
77 ///////////////////////////////////////////////////////////////////////////////
78 
79 // // example
80 ///////////////////////////////////////////////////////////////////////////////
81 // genComponentIterationCode!(EntitySet, EntitySet, HashmapComponentStorage!Velocity)
82 ///////////////////////////////////////////////////////////////////////////////
83 // // yields
84 /*
85   switch(lengths[0].index) {
86     case 0:
87         foreach(key; _entity_set_0) {
88             row.id = key;
89             bool component_1 = _entity_set_1.get(key);
90             if (!component_1) continue;
91             auto component_2 = _velocity_2_storage.get(key);
92             if (component_2 is null) continue;
93             row.velocity_2 = component_2;
94             result = dg(row);
95             if (result)
96                 break;
97         }
98         break;
99     case 1:
100         foreach(key; _entity_set_1) {
101             row.id = key;
102             bool component_0 = _entity_set_0.get(key);
103             if (!component_0) continue;
104             auto component_2 = _velocity_2_storage.get(key);
105             if (component_2 is null) continue;
106             row.velocity_2 = component_2;
107             result = dg(row);
108             if (result)
109                 break;
110         }
111         break;
112     case 2:
113         foreach(key, value; _velocity_2_storage) {
114             row.id = key;
115             row.velocity_2 = &value;
116             bool component_0 = _entity_set_0.get(key);
117             if (!component_0) continue;
118             bool component_1 = _entity_set_1.get(key);
119             if (!component_1) continue;
120             result = dg(row);
121             if (result)
122                 break;
123         }
124         break;
125     default: assert(0);
126   }
127 */
128 // // as code inside ComponentQuery.opApply()
129 
130 
131 
132 string genComponentIterationCode(ComponentStorages...)()
133 {
134 	string result;
135 
136 	result ~= "switch(lengths[0].index) {\n";
137 
138 	// gen code for each case when table has fewest items
139 	foreach(i, CS_1; ComponentStorages)
140 	{
141 		string istr = i.to!string;
142 		result ~=  "\tcase "~istr~":\n";
143 		//result ~=  "writeln("~istr~");\n";
144 
145 		static if(isAnyComponentStorage!CS_1)
146 		{
147 			result ~=  "\t\tforeach(key, ref value; " ~ genComponentStorageName!(CS_1, i) ~ ") {\n";
148 			// gen component selection for shortest table
149 			result ~=  "\t\t\trow.id = key;\n";
150 			result ~=  "\t\t\trow." ~ genRowComponentName!(CS_1, i) ~" = &value;\n\n";
151 		}
152 		else
153 		{
154 			result ~=  "\t\tforeach(key; " ~ genComponentStorageName!(CS_1, i) ~ ") {\n";
155 			// gen component selection for shortest table
156 			result ~=  "\t\t\trow.id = key;\n\n";
157 		}
158 
159 		// gen component selection for other tables
160 		foreach(j, CS_2; ComponentStorages)
161 		if (i != j)
162 		{
163 			// gen component selection for other tables via random access lookup
164 			static if(isAnyComponentStorage!CS_2)
165 			{
166 				string jstr = j.to!string;
167 				result ~= "\t\t\tauto component_"~ jstr ~" = " ~ genComponentStorageName!(CS_2, j) ~ ".get(key);\n";
168 				result ~= "\t\t\tif (component_"~ jstr ~" is null) continue;\n";
169 				result ~= "\t\t\trow." ~ genRowComponentName!(CS_2, j) ~ " = component_"~ jstr ~";\n\n";
170 			}
171 			else
172 			{
173 				string jstr = j.to!string;
174 				result ~= "\t\t\tbool component_"~ jstr ~" = " ~ genComponentStorageName!(CS_2, j) ~ ".get(key);\n";
175 				result ~= "\t\t\tif (!component_"~ jstr ~") continue;\n\n";
176 			}
177 		}
178 
179 		// call foreach body passing current row
180 		result ~=  "\t\t\tresult = dg(row);\n";
181 		result ~=  "\t\t\tif (result)\n";
182 		result ~=  "\t\t\t\tbreak;\n";
183 
184 		result ~=  "\t\t}\n"; // end foreach
185 		result ~=  "\t\tbreak;\n";
186 	}
187 
188 	result ~= "\tdefault: assert(0);\n";
189 	result ~= "}\n"; // end switch
190 
191 	return result;
192 }
193 
194 // // example
195 ///////////////////////////////////////////////////////////////////////////////
196 // genComponentStorages!(HashmapComponentStorage!Transform, EntitySet, HashmapComponentStorage!Velocity);
197 ///////////////////////////////////////////////////////////////////////////////
198 // // yields
199 // private HashmapComponentStorage!Transform _transform_0_storage;
200 // private EntitySet _entity_set_1;
201 // private HashmapComponentStorage!Velocity _velocity_2_storage;
202 // // as fields inside ComponentQuery
203 
204 string genComponentStorages(ComponentStorages...)()
205 {
206 	string result;
207 
208 	foreach(i, cs; ComponentStorages)
209 	{
210 		result ~= "private ComponentStorages[" ~ i.to!string ~ "] " ~
211 			genComponentStorageName!(cs, i) ~ ";\n";
212 	}
213 
214 	return result;
215 }
216 
217 string genComponentStorageName(ComponentStorage, uint i)()
218 {
219 	static if(isAnyComponentStorage!ComponentStorage)
220 		return "_" ~ genStorageComponentName!ComponentStorage ~ "_" ~ i.to!string ~ "_storage";
221 	else
222 		return "_entity_set_" ~ i.to!string;
223 }
224 
225 string genStorageComponentName(ComponentStorage)()
226 {
227 	alias C = TemplateArgsOf!ComponentStorage[0];
228 	return genComponentName!C;
229 }
230 
231 string genComponentName(Component)()
232 {
233 	string ident = Unqual!Component.stringof;
234 	import std.uni : toLower;
235 	return toLower(ident[0]).to!string ~ ident[1..$];
236 }
237 
238 // // example
239 ///////////////////////////////////////////////////////////////////////////////
240 // genComponentStorages!(HashmapComponentStorage!Transform, EntitySet, HashmapComponentStorage!Velocity);
241 ///////////////////////////////////////////////////////////////////////////////
242 // // yields
243 // static struct Row
244 // {
245 //     EntityId id;
246 //     Transform* transform_0;
247 //     Velocity* velocity_2;
248 // }
249 // // as type definition inside ComponentQuery
250 // // numbers start from zero. entity sets affect numbers.
251 // // Entity sets need no entries since they only affect which entities will be returned by query.
252 
253 string genRowDefinition(ComponentStorages...)()
254 {
255 	string result;
256 
257 	result =
258 		"static struct Row\n"~
259 		"{\n"~
260 		"\tEntityId id;\n";
261 
262 	foreach(i, CS; ComponentStorages)
263 	{
264 		static if (isAnyComponentStorage!CS)
265 		{
266 			result ~= "\tTemplateArgsOf!(ComponentStorages["~ i.to!string ~"])[0]* "~
267 				genRowComponentName!(CS, i) ~ ";\n";
268 		}
269 	}
270 	result ~= "}\n";
271 
272 	return result;
273 }
274 
275 string genRowComponentName(ComponentStorage, uint i)()
276 {
277 	return genStorageComponentName!ComponentStorage ~ "_" ~ i.to!string;
278 }