1 module ws.script.lua;
2
3 import
4 ws.list,
5 ws.io,
6 ws.exception,
7 ws.string,
8 ws.sys.library;
9
10 __gshared:
11
12
13 class Lua {
14
15 class Var {
16
17 enum: int { Number, String, Table, Reference };
18
19 int reference;
20
21 this(int t){
22 if(t == Number)
23 push(0);
24 else if(t == String)
25 push("");
26 else if(t == Table)
27 lua_createtable(state, 0, 0);
28 reference = luaL_ref(state, LUA_REGISTRYINDEX);
29 }
30
31 void setTop(){
32 lua_rawgeti(state, LUA_REGISTRYINDEX, reference);
33 }
34
35 ~this(){
36 luaL_unref(state, LUA_REGISTRYINDEX, reference);
37 }
38
39 bool valid(){
40 setTop();
41 scope(exit)
42 lua_pop(state, 1);
43 return lua_type(state, -1) != 0;
44 }
45
46 Var opIndex(string s){
47 mixin(checkstack);
48 setTop();
49 scope(exit)
50 lua_pop(state, 1);
51 lua_getfield(state, LUA_REGISTRYINDEX, s.toStringz());
52 return new Var(Reference);
53 }
54
55 void opIndexAssign(T)(T value, string key){
56 mixin(checkstack);
57 setTop();
58 push(value);
59 lua_setfield(state, -2, key.toStringz());
60 lua_pop(state, 1);
61 }
62
63 void opIndexAssign(T)(T value, int key){
64 opIndexAssign(value, tostring(key));
65 }
66
67 int opApply(int delegate(Var) dg){
68 mixin(checkstack);
69 setTop();
70 if(lua_type(state, -1) != 5)
71 throw new Exception("Lua Var is not a table, cannot iterate");
72 int result = 0;
73 lua_pushnil(state);
74 Var[] list;
75 while(lua_next(state, -2))
76 list ~= new Var(Reference);
77 foreach(v; list){
78 result = dg(v);
79 if(result)
80 break;
81 }
82 return result;
83 }
84
85
86 int opApply(int delegate(string, Var) dg){
87 mixin(checkstack);
88 setTop();
89 if(lua_type(state, -1) != 5)
90 throw new Exception("Lua Var is not a table, cannot iterate");
91 int result = 0;
92 lua_pushnil(state);
93 Var[string] list;
94 while(lua_next(state, -2)){
95 bool n = false;
96 if(lua_isnumber(state, -2)){
97 setTop();
98 lua_pushvalue(state, -3);
99 lua_pushvalue(state, -3);
100 n = true;
101 }
102 list[Lua.toString(-2)] = new Var(Reference);
103 if(n)
104 lua_settop(state, -(3)-1);
105 }
106 lua_pop(state, 1);
107 foreach(k, e; list){
108 result = dg(k, e);
109 if(result)
110 break;
111 }
112 return result;
113 }
114
115
116 Var opCall(Args...)(Args args){
117 mixin(checkstack);
118 int i=lua_gettop(state);
119 scope(exit)
120 assert(i == lua_gettop(state));
121 setTop();
122 foreach(a; args)
123 push(a);
124 check(lua_pcall(state, args.length, 1, 0));
125 return new Var(Var.Reference);
126 }
127
128 override string toString(){
129 mixin(checkstack);
130 setTop();
131 scope(exit)
132 lua_pop(state, 1);
133 if(!valid()){
134 return "nil";
135 }else if(getType() == "table"){
136 if(true)
137 return "Table: 0x" ~ tostring(lua_topointer(state, -1));
138 else{
139 string s = "{\n";
140 foreach(k, e; this)
141 s ~= indent(k ~ " = " ~ e.toString()) ~ "\n";
142 s ~= "}";
143 return s;
144 }
145 }else if(getType() == "userdata"){
146 if(this["__tostring"].valid()){
147 return this["__tostring"]().toString();
148 }else{
149 PtrContainer!(void*) container = *cast(PtrContainer!(void*)*)lua_touserdata(state, -1);
150 return "Userdata: 0x" ~ tostring(cast(void*)container.reference);
151 }
152 }else if(getType() == "function"){
153 return "Function: 0x" ~ tostring(lua_topointer(state, -1));
154 }else{
155 return tostring(luaL_checklstring(state, -1, null));
156 }
157 }
158
159
160 T userdata(T)(){
161 mixin(checkstack);
162 setTop();
163 scope(exit)
164 lua_pop(state, 1);
165 return getUserdata!T("", -1);
166 }
167
168
169 void invalidate(){
170 setTop();
171 lua_getfield(state, LUA_REGISTRYINDEX, "nil");
172 lua_setmetatable(state, -2);
173 }
174
175 void setMetatable(string s){
176 mixin(checkstack);
177 setTop();
178 luaL_getmetatable(state, s.toStringz());
179 if(lua_type(state, -1) == 0)
180 writeln("Warning: " ~ s ~ " is nil");
181 //metatable(s).setTop();
182 lua_setmetatable(state, -2);
183 lua_pop(state, 1);
184 }
185
186 Var getMetatable(){
187 mixin(checkstack);
188 setTop();
189 scope(exit)
190 lua_pop(state, 1);
191 lua_getmetatable(state, -1);
192 return new Var(Var.Reference);
193 }
194
195 string getType(){
196 mixin(checkstack);
197 setTop();
198 scope(exit)
199 lua_pop(state, 1);
200 int t = lua_type(state, -1);
201 return tostring(lua_typename(state, t));
202 }
203
204 private string indent(string s){
205 string n = "\t";
206 foreach(c; s){
207 n ~= c;
208 if(c == '\n')
209 n ~= '\t';
210 }
211 return n;
212 }
213
214 }
215
216
217 void run(string s){
218 check(luaL_loadstring(state, s.toStringz()) || lua_pcall(state, 0, -1, 0));
219 }
220
221
222 void runFile(string path){
223 fileStack ~= path;
224 check(luaL_loadfile(state, path.toStringz()) || lua_pcall(state, 0, -1, 0));
225 fileStack.popBack();
226 }
227
228
229 Var opIndex(string key){
230 mixin(checkstack);
231 lua_getfield(state, LUA_GLOBALSINDEX, key.toStringz());
232 return new Var(Var.Reference);
233 }
234
235
236 void opIndexAssign(T)(T t, string key){
237 mixin(checkstack);
238 push(t);
239 lua_setfield(state, LUA_GLOBALSINDEX, key.toStringz());
240 }
241
242
243 Var table(Args...)(Args args){
244 mixin(checkstack);
245 lua_createtable(state, 0, 0);
246 pushField(args);
247 return new Var(Var.Reference);
248 }
249
250
251 Var metatable(string n){
252 mixin(checkstack);
253 luaL_newmetatable(state, n.toStringz());
254 return new Var(Var.Reference);
255 }
256
257
258 /+
259 void startMetatable(string name){
260 luaL_newmetatable(state, name.toStringz());
261 }
262 +/
263
264 Var newUserdata(T)(T e, string n=""){
265 mixin(checkstack);
266 assert(e);
267 //n = (n.length ? n : typeid(T).toString());
268 auto container = cast(PtrContainer!T*)lua_newuserdata(state, (PtrContainer!T).sizeof);
269 //lua_getfield(state, LUA_GLOBALSINDEX, n.toStringz());
270 //lua_pushstring(state, "__index");
271 //lua_pushvalue(state, -2);
272 //lua_settable(state, -3); // metatable.__index = metatable
273 //lua_setmetatable(state, -2);
274 container.reference = e;
275 return new Var(Var.Reference);
276 }
277
278
279 protected {
280
281 string printStack(){
282 mixin(checkstack);
283 string s = "Lua Stack {\n";
284 for(int i=1; i<=lua_gettop(state); i++){
285 lua_pushvalue(state, i);
286 auto v = new Var(Var.Reference);
287 s ~= "\t%s = %s\n".format(i, v);
288 }
289 s ~= "}\n";
290 return s;
291 }
292
293 string toString(int i){
294 return tostring(luaL_checklstring(state, i, null));
295 }
296
297
298 T get(T)(int idx){
299 static if(is(T == string))
300 return toString(idx);
301 else static if(is(T == double) || is(T==long) || is(T==int))
302 return cast(T)luaL_checknumber(state, idx);
303 else static if(is(T == Var)){
304 lua_pushvalue(state, idx);
305 return new Var(Var.Reference);
306 }else
307 return getUserdata!T();
308 //else static assert(0, "Return type not implemented");
309 }
310
311 void push(Ret, Args...)(Ret function(Args) f){
312 push(delegate Ret(Args args){ return f(args); });
313 }
314
315 void push(Ret, Args...)(Ret delegate(Args) f){
316 push(delegate int(int argc) nothrow {
317 try {
318 Args args;
319 foreach(i, T; Args)
320 args[i] = get!T(i+1);
321 static if(is(Ret==void)){
322 f(args);
323 return 0;
324 }else{
325 push(f(args));
326 return 1;
327 }
328 }catch(Exception e)
329 writeln("Error in delegate: ", e);
330 return 0;
331 });
332 }
333
334 void push()(double n){
335 lua_pushnumber(state, n);
336 }
337
338 void push()(string s){
339 lua_pushstring(state, s.toStringz());
340 }
341
342 void push()(Var v){
343 v.setTop();
344 }
345
346 void push()(Function f){
347 closures ~= f;
348 lua_pushnumber(state, closures.length-1);
349 lua_pushcclosure(state, &staticClosure, 1);
350 }
351
352
353 void pushField()(){}
354
355
356 void pushField(Args...)(string c, string g, Args args){
357 lua_pushstring(state, n.toStringz);
358 lua_getfield(state, LUA_GLOBALSINDEX, n.toStringz);
359 lua_settable(state, -3);
360 pushField(args);
361 }
362
363
364 void pushField(T, Args...)(string n, T t, Args args){
365 push(t);
366 lua_setfield(state, -2, n.toStringz());
367 pushField(args);
368 }
369
370
371 private struct PtrContainer(T) {
372 T reference;
373 }
374
375
376 /// return reference to userdata on stack
377 T getUserdata(T)(string n="", int i = 1){
378 mixin(checkstack);
379 n = (n.length ? n : typeid(T).toString());
380 auto container = cast(PtrContainer!T*)lua_touserdata(state, i);
381 if(!container)
382 luaL_argerror(state, i, "'%s' expected".format(n).toStringz());
383 return container.reference;
384 /+ Checks if ud's metatable is the same as luaL_getmetatable(state,n)
385 n = (n.length ? n : typeid(T).toString());
386 auto container = cast(PtrContainer!T*)luaL_checkudata(state, i, n.toStringz());
387 if(!container)
388 luaL_argerror(state, i, "'%' expected".format(n).toStringz());
389 return container.reference;
390 +/
391 }
392
393 }
394
395
396 alias nothrow int delegate(int) Function;
397
398 protected {
399 /+
400 const static string checkstack = "
401 int checkstacksize=lua_gettop(state);
402 scope(exit)
403 assert(checkstacksize == lua_gettop(state), \"Before: \" ~ tostring(checkstacksize) ~ \"\nNow: \" ~ printStack());
404 ";
405 +/
406 const static string checkstack = "";
407 Library library;
408 state_ptr state;
409 List!string fileStack;
410 Function[] closures;
411 size_t closureCurrent = 0;
412 }
413
414 this(){
415 library = luaLib.load();
416 state = luaL_newstate();
417 //luaL_openlibs(state);
418 luaopen_base(state);
419 luaopen_table(state);
420
421 /*lua_pushcclosure(state, luaopen_table, 0);
422 pushLiteral("table");
423 lua_call(state, 1, 0);*/
424 fileStack = new List!string;
425 states[state] = this;
426 }
427
428
429 ~this(){
430 lua_close(state);
431 }
432
433
434 state_ptr getState(){
435 return state;
436 }
437
438
439 private void check(int e){
440 if(e){
441 writeln("Lua error: ", lua_tolstring(state, -1, null));
442 lua_settop(state, -2);
443 }
444 }
445
446
447 static Lua[state_ptr] states;
448
449
450 extern(C) static int staticClosure(state_ptr state){
451 return states[state].closures[cast(size_t)lua_tonumber(state,-10003)](lua_gettop(state));
452 }
453
454
455 }
456
457
458 extern(C){
459 const int LUA_REGISTRYINDEX = -10000;
460 const int LUA_ENVIRONINDEX = -10001;
461 const int LUA_GLOBALSINDEX = -10002;
462
463 alias void* state_ptr;
464
465 alias int function(state_ptr) lua_CFunction;
466
467 version(Windows)
468 private const string LibraryFile = "lua51";
469 version(Posix)
470 private const string LibraryFile = "lua5.1";
471
472 void lua_pop(state_ptr state, int n){
473 lua_settop(state, -(n)-1);
474 }
475
476 void luaL_getmetatable(state_ptr s, const(char)* n){
477 lua_getfield(s, LUA_REGISTRYINDEX, n);
478 }
479
480 mixin library!(
481 "luaLib", LibraryFile,
482 "luaL_newstate", state_ptr function(),
483 "luaL_openlibs", void function(state_ptr),
484 "luaL_newmetatable", void function(state_ptr, const(char)*),
485 "luaL_checknumber", double function(state_ptr, int),
486 "luaL_loadstring", int function(state_ptr, const(char)*),
487 "luaL_loadfile", int function(state_ptr, const(char)*),
488 "luaL_checkudata", void* function(state_ptr, int, const(char)*),
489 "luaL_argerror", int function(state_ptr, int, const(char)*),
490 "luaL_ref", int function(state_ptr, int),
491 "luaL_unref", void function(state_ptr, int, int),
492 "lua_setfield", void function(state_ptr, int, const(char)*),
493 "lua_getfield", void function(state_ptr, int, const(char)*),
494 "lua_pcall", int function(state_ptr, int, int, int),
495 "lua_call", void function(state_ptr, int, int),
496 "luaL_checklstring", const(char*) function(state_ptr, int, size_t*),
497 "luaopen_base", int function(state_ptr),
498 "luaopen_table", int function(state_ptr),
499 "lua_close", void function(state_ptr),
500 "lua_createtable", void function(state_ptr, int, int),
501 "lua_setmetatable", int function(state_ptr, int),
502 "lua_getmetatable", int function(state_ptr, int),
503 "lua_settable", void function(state_ptr, int),
504 "lua_pushvalue", void function(state_ptr, int),
505 "lua_isstring", int function(state_ptr, int),
506 "lua_isnumber", int function(state_ptr, int),
507 "lua_settop", void function(state_ptr, int),
508 "lua_gettop", int function(state_ptr),
509 "lua_tonumber", double function(state_ptr, int),
510 "lua_tolstring", const(char)* function(state_ptr, int, size_t*),
511 "lua_touserdata", void* function(state_ptr, int),
512 "lua_topointer", void* function(state_ptr, int),
513 "lua_pushstring", void function(state_ptr, const char*),
514 "lua_pushnumber", void function(state_ptr, double),
515 "lua_pushcclosure", "void function(state_ptr, lua_CFunction, int)",
516 "lua_newuserdata", void* function(state_ptr, size_t),
517 "lua_rawseti", void function(state_ptr, int, int),
518 "lua_rawgeti", void function(state_ptr, int, int),
519 "lua_pushnil", void function(state_ptr),
520 "lua_next", int function(state_ptr, int),
521 "lua_type", int function(state_ptr, int),
522 "lua_typename", const(char)* function(state_ptr, int)
523 );
524
525 }