1 module vayne.lib;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.conv;
7 import std.format;
8 import std.string;
9 import std.utf;
10 
11 
12 import vayne.value;
13 
14 
15 template bindVars(size_t i, alias Container, Vars...) {
16 	static if(i < Vars.length) {
17 		enum bindVars = Vars.length ? (__traits(identifier, Container) ~ "[\"" ~ __traits(identifier, Vars[i]) ~ "\"]=Value(Vars[" ~ i.to!string ~ "]);\n" ~ bindVars!(i + 1, Container, Vars)) : "";
18 	} else {
19 		enum bindVars = "";
20 	}
21 }
22 
23 
24 void bindLibBasic(ref Value[string] globals) {
25 	static long length(Value x) {
26 		return cast(long)x.length;
27 	}
28 
29 	static bool empty(Value x) {
30 		return x.length == 0;
31 	}
32 
33 	static Value keys(Value x) {
34 		return x.keys();
35 	}
36 
37 	static Value front(Value x) {
38 		return x[0];
39 	}
40 
41 	static Value back(Value x) {
42 		return x[x.length - 1];
43 	}
44 
45 	static Value get(Value x, Value key, Value def) {
46 		Value result;
47 		if (x.has(key, &result))
48 			return result;
49 		return def;
50 	}
51 
52 	static Value def(Value x, Value def) {
53 		if ((x.type == Value.Type.Undefined) || (x.type == Value.Type.Null))
54 			return def;
55 		return x;
56 	}
57 
58 	static long tointeger(Value x) {
59 		return x.get!long;
60 	}
61 
62 	static double tofloat(Value x) {
63 		return x.get!double;
64 	}
65 
66 	static string tostring(Value x) {
67 		return x.toString();
68 	}
69 
70 	static bool tobool(Value x) {
71 		return x.get!bool;
72 	}
73 
74 	static string escape(Value[] args) {
75 		auto value = args[0].get!string;
76 
77 		foreach (f; args[1..$]) {
78 			switch (f.get!string) {
79 			case "html":
80 				value = escapeHTML(value);
81 				break;
82 			case "js":
83 				value = escapeJS(value);
84 				break;
85 			case "uri":
86 				value = escapeURI(value);
87 				break;
88 			default:
89 				assert("unimplemented escape filter: " ~ f.get!string);
90 				break;
91 			}
92 		}
93 
94 		return value;
95 	}
96 
97 	static string translate(Value[] args) {
98 		return args[0].get!string;
99 	}
100 
101 	globals["length"] = Value(&length);
102 	globals["empty"] = Value(&empty);
103 	globals["keys"] = Value(&keys);
104 	globals["front"] = Value(&front);
105 	globals["back"] = Value(&back);
106 
107 	globals["integer"] = Value(&tointeger);
108 	globals["float"] = Value(&tofloat);
109 	globals["string"] = Value(&tostring);
110 	globals["bool"] = Value(&tobool);
111 
112 	globals["get"] = Value(&get);
113 	globals["default"] = Value(&def);
114 
115 	globals["escape"] = Value(&escape);
116 
117 	globals["__escape"] = Value(&escape);
118 	globals["__translate"] = Value(&translate);
119 }
120 
121 
122 void bindLibMath(ref Value[string] globals) {
123 	import std.math;
124 
125 	static Value abs(Value x) {
126 		final switch (x.type) with (Value.Type) {
127 		case Undefined:
128 		case Function:
129 		case String:
130 		case Array:
131 		case AssocArray:
132 		case Object:
133 		case Pointer:
134 		case Null:
135 		case Bool:
136 			return x;
137 		case Integer:
138 			return Value(std.math.abs(x.get!long));
139 		case Float:
140 			return Value(std.math.abs(x.get!double));
141 		}
142 	}
143 
144 	globals["abs"] = Value(&abs);
145 }
146 
147 
148 void bindLibString(ref Value[string] globals) {
149 	static string join(Value[] x) {
150 		auto app = appender!string;
151 		auto value = x[0];
152 		auto len = value.length;
153 		auto seps = (x.length > 1) ? x[1].get!string : ",";
154 		foreach (size_t i, v; value) {
155 			app.put(v.get!string);
156 			if (i + 1 != len)
157 				app.put(seps);
158 		}
159 		return app.data;
160 	}
161 
162 	static string[] split(Value[] x) {
163 		auto value = x[0].get!string;
164 		auto sep = (x.length > 1) ? x[1].get!string : " ";
165 
166 		return value.split(sep);
167 	}
168 
169 	static string strip(Value x) {
170 		return x.get!string.strip;
171 	}
172 
173 	static string lower(Value x) {
174 		return x.get!string.toLower();
175 	}
176 
177 	static string upper(Value x) {
178 		return x.get!string.toUpper();
179 	}
180 
181 	static long indexOf(Value[] args) {
182 		auto haystack = args[0].get!string;
183 		auto needle = args[1].get!string;
184 		auto start = (args.length > 2) ? args[2].get!size_t : 0;
185 
186 		return haystack.indexOf(needle, start);
187 	}
188 
189 	static string format(Value[] args) {
190 		auto fmt = args[0].get!string;
191 		auto spec = FormatSpec!char(fmt);
192 
193 		auto app = appender!string;
194 		app.reserve(max(32, fmt.length + fmt.length >> 1));
195 
196 		size_t arg = 1;
197     	while (spec.writeUpToNextSpec(&app)) {
198 			if (arg >= args.length)
199 				break;
200 
201 			auto value = args[arg++];
202 			final switch (value.type) with (Value.Type) {
203 			case Undefined:
204 			case Function:
205 			case String:
206 			case Array:
207 			case AssocArray:
208 			case Object:
209 			case Pointer:
210 				formatValue(&app, value.get!string, spec);
211 				break;
212 			case Null:
213 				formatValue(&app, null, spec);
214 				break;
215 			case Bool:
216 				formatValue(&app, value.get!bool, spec);
217 				break;
218 			case Integer:
219 				formatValue(&app, value.get!long, spec);
220 				break;
221 			case Float:
222 				formatValue(&app, value.get!double, spec);
223 				break;
224 			}
225     	}
226 
227 		if (arg != args.length)
228 			throw new Exception(std.format.format("number of arguments doesn't match number of format specifiers - expected %d, got %d", arg - 1, args.length - 1));
229 		return app.data;
230 	}
231 
232 	globals["join"] = Value(&join);
233 	globals["split"] = Value(&split);
234 	globals["format"] = Value(&format);
235 	globals["strip"] = Value(&strip);
236 	globals["lower"] = Value(&lower);
237 	globals["upper"] = Value(&upper);
238 	globals["indexOf"] = Value(&indexOf);
239 }
240 
241 
242 void bindLibDefault(ref Value[string] globals) {
243 	bindLibBasic(globals);
244 	bindLibString(globals);
245 	bindLibMath(globals);
246 }
247 
248 
249 string escapeHTML(string x) {
250 	auto app = appender!string;
251 	app.reserve(8 + x.length + (x.length >> 1));
252 
253 	foreach (ch; x.byDchar) {
254 		switch (ch) {
255 		case '"':
256 			app.put("&quot;");
257 			break;
258 		case '\'':
259 			app.put("&#39;");
260 			break;
261 		case 'a': .. case 'z':
262 			goto case;
263 		case 'A': .. case 'Z':
264 			goto case;
265 		case '0': .. case '9':
266 			goto case;
267 		case ' ', '\t', '\n', '\r', '-', '_', '.', ':', ',', ';',
268 			'#', '+', '*', '?', '=', '(', ')', '/', '!',
269 			'%' , '{', '}', '[', ']', '$', '^', '~':
270 			app.put(cast(char)ch);
271 			break;
272 		case '<':
273 			app.put("&lt;");
274 			break;
275 		case '>':
276 			app.put("&gt;");
277 			break;
278 		case '&':
279 			app.put("&amp;");
280 			break;
281 		default:
282 			formattedWrite(&app, "&#x%02X;", cast(uint)ch);
283 			break;
284 		}
285 	}
286 	return app.data;
287 }
288 
289 
290 string escapeJS(string x) {
291 	auto app = appender!string;
292 	app.reserve(x.length + (x.length >> 1));
293 
294 	foreach (ch; x.byDchar) {
295 		switch (ch) {
296 		case '\\':
297 			app.put(`\\`);
298 			break;
299 		case '\'':
300 			app.put(`\'`);
301 			break;
302 		case '\"':
303 			app.put(`\"`);
304 			break;
305 		case '\r':
306 			break;
307 		case '\n':
308 			app.put(`\n`);
309 			break;
310 		default:
311 			app.put(ch);
312 			break;
313 		}
314 	}
315 	return app.data;
316 }
317 
318 
319 string escapeURI(string x) {
320 	auto app = appender!string;
321 	app.reserve(8 + x.length + (x.length >> 1));
322 
323 	foreach (i; 0..x.length) {
324 		switch (x.ptr[i]) {
325 		case 'A': .. case 'Z':
326 		case 'a': .. case 'z':
327 		case '0': .. case '9':
328 		case '-': case '_': case '.': case '~':
329 			app.put(x.ptr[i]);
330 			break;
331 		default:
332 			formattedWrite(&app, "%%%02X", x.ptr[i]);
333 			break;
334 		}
335 	}
336 
337 	return app.data;
338 }