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 }