From ba1d20dda513d2b5ddda3788a9d63aa9f9da7623 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Fri, 27 Sep 2013 04:15:01 +0300 Subject: [PATCH] Lua: print() can print any type, add tostringx() for print-to-string --- include/lua/internal.hpp | 16 +++++++ manual.lyx | 9 ++++ manual.txt | 27 +++++++----- src/lua/core.cpp | 91 ++++++++++++++++++++++++++++++---------- src/lua/lua.cpp | 27 ++++++++++++ 5 files changed, 138 insertions(+), 32 deletions(-) diff --git a/include/lua/internal.hpp b/include/lua/internal.hpp index a877a541..88cd0ad4 100644 --- a/include/lua/internal.hpp +++ b/include/lua/internal.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include extern "C" { #include @@ -19,6 +21,9 @@ extern bool lua_booted_flag; extern uint64_t lua_idle_hook_time; extern uint64_t lua_timer_hook_time; +std::list& userdata_recogn_fns(); +std::string try_recognize_userdata(lua_State* LS, int index); + template T get_numeric_argument(lua_State* LS, unsigned argindex, const char* fname) { @@ -90,6 +95,7 @@ public: lua_class(const std::string& _name) { name = _name; + userdata_recogn_fns().push_back(lua_class::recognize); } template T* _create(lua_State* LS, U... args) @@ -208,6 +214,16 @@ badtype: return objclass()._is(LS, arg); } + std::string _recognize(lua_State* LS, int arg) + { + return _is(LS, arg) ? name : ""; + } + + static std::string recognize(lua_State* LS, int arg) + { + return objclass()._recognize(LS, arg); + } + lua_obj_pin* _pin(lua_State* LS, int arg, const char* fname) { T* obj = get(LS, arg, fname); diff --git a/manual.lyx b/manual.lyx index 4adceed4..80bd6bb0 100644 --- a/manual.lyx +++ b/manual.lyx @@ -1923,6 +1923,15 @@ print Print line to message console. \end_layout +\begin_layout Subsubsection +tostringx +\end_layout + +\begin_layout Standard +Convert arbitrary lua value into a string (using same conversions as print) + and return the result. +\end_layout + \begin_layout Subsubsection exec(string command) \end_layout diff --git a/manual.txt b/manual.txt index c8003c54..b1827d47 100644 --- a/manual.txt +++ b/manual.txt @@ -940,36 +940,41 @@ Sets the delay for subsequent characters in typematic autorepeat. Print line to message console. -8.1.2 exec(string command) +8.1.2 tostringx + +Convert arbitrary lua value into a string (using same conversions +as print) and return the result. + +8.1.3 exec(string command) Run command as it was entered on the command line -8.1.3 utime() +8.1.4 utime() Returns two values. First is time since some epoch in seconds, the second is microseconds mod 10^6 since that epoch. -8.1.4 emulator_ready() +8.1.5 emulator_ready() Returns true if emulator has finished booting, false if not (on_startup() will be issued later). -8.1.5 set_idle_timeout(number timeout) +8.1.6 set_idle_timeout(number timeout) Set number of microseconds to block idle for. After this timeout has expired, on_idle() will be called once. -8.1.6 set_timer_timeout(number timeout) +8.1.7 set_timer_timeout(number timeout) Set number of microseconds to block timer for. After this timeout has expired, on_timer() will be called once. -8.1.7 bus_address(number snesaddr) +8.1.8 bus_address(number snesaddr) Returns virtual address corresponding to specified address on SNES bus. -8.1.8 loopwrapper(function fun, ...) +8.1.9 loopwrapper(function fun, ...) Calls function fun with function and specified arguments. The function passed suspends execution until the function returned is @@ -988,21 +993,21 @@ on_paint = loopwrapper(function(wait) end); -8.1.9 list_bindings([string cmd]) +8.1.10 list_bindings([string cmd]) Get table of all keybindings, indexed by keyspec (modifiers|mask/key). If command is specified, the table is limited to that command. -8.1.10 get_alias(string aname) +8.1.11 get_alias(string aname) Get expansion of given alias. -8.1.11 set_alias(string aname, string value) +8.1.12 set_alias(string aname, string value) Set expansion of given alias. -8.1.12 create_ibind(string name, string cmd) +8.1.13 create_ibind(string name, string cmd) Return object representing inverse binding with specified name and specified command. diff --git a/src/lua/core.cpp b/src/lua/core.cpp index 3d002900..f3c785ec 100644 --- a/src/lua/core.cpp +++ b/src/lua/core.cpp @@ -3,9 +3,75 @@ #include "lua/internal.hpp" #include "core/framerate.hpp" #include "core/window.hpp" +#include "library/string.hpp" +#include namespace { + std::string luavalue_to_string(lua_State* LS, int index, std::set& printed, bool quote) + { + switch(lua_type(LS, index)) { + case LUA_TNONE: + return "none"; + case LUA_TNIL: + return "nil"; + case LUA_TBOOLEAN: + return lua_toboolean(LS, index) ? "true" : "false"; + case LUA_TNUMBER: + return (stringfmt() << lua_tonumber(LS, index)).str(); + case LUA_TSTRING: { + const char* tmp2; + size_t len; + tmp2 = lua_tolstring(LS, index, &len); + if(quote) + return "\"" + std::string(tmp2, tmp2 + len) + "\""; + else + return std::string(tmp2, tmp2 + len); + } + case LUA_TLIGHTUSERDATA: + return (stringfmt() << "Lightuserdata:" << lua_touserdata(LS, index)).str(); + case LUA_TFUNCTION: + return (stringfmt() << "Function:" << lua_topointer(LS, index)).str(); + case LUA_TTHREAD: + return (stringfmt() << "Thread:" << lua_topointer(LS, index)).str(); + break; + case LUA_TUSERDATA: + return (stringfmt() << "Userdata<" << try_recognize_userdata(LS, index) << ">:" + << lua_touserdata(LS, index)).str(); + case LUA_TTABLE: { + //Fun with recursion. + const void* ptr = lua_topointer(LS, index); + if(printed.count(ptr)) + return (stringfmt() << "").str(); + printed.insert(ptr); + std::ostringstream s; + s << "<" << ptr << ">{"; + lua_pushnil(LS); + bool first = true; + while(lua_next(LS, index)) { + if(!first) + s << ", "; + int stacktop = lua_gettop(LS); + s << "[" << luavalue_to_string(LS, stacktop - 1, printed, true) << "]=" + << luavalue_to_string(LS, stacktop, printed, true); + first = false; + lua_pop(LS, 1); + } + s << "}"; + return s.str(); + } + default: + return (stringfmt() << "???:" << lua_topointer(LS, index)).str(); + } + } + + function_ptr_luafun lua_tostringx("tostringx", [](lua_State* LS, const std::string& fname) -> int { + std::set tmp2; + std::string y = luavalue_to_string(LS, 1, tmp2, false); + lua_pushlstring(LS, y.c_str(), y.length()); + return 1; + }); + function_ptr_luafun lua_print("print", [](lua_State* LS, const std::string& fname) -> int { int stacksize = 0; while(!lua_isnone(LS, stacksize + 1)) @@ -13,29 +79,12 @@ namespace std::string toprint; bool first = true; for(int i = 0; i < stacksize; i++) { - size_t len; - const char* tmp = NULL; - if(lua_isnil(LS, i + 1)) { - tmp = "nil"; - len = 3; - } else if(lua_isboolean(LS, i + 1) && lua_toboolean(LS, i + 1)) { - tmp = "true"; - len = 4; - } else if(lua_isboolean(LS, i + 1) && !lua_toboolean(LS, i + 1)) { - tmp = "false"; - len = 5; - } else { - tmp = lua_tolstring(LS, i + 1, &len); - if(!tmp) { - tmp = "(unprintable)"; - len = 13; - } - } - std::string localmsg(tmp, tmp + len); + std::set tmp2; + std::string tmp = luavalue_to_string(LS, i + 1, tmp2, false); if(first) - toprint = localmsg; + toprint = tmp; else - toprint = toprint + "\t" + localmsg; + toprint = toprint + "\t" + tmp; first = false; } platform::message(toprint); diff --git a/src/lua/lua.cpp b/src/lua/lua.cpp index d3a6b86f..6913a448 100644 --- a/src/lua/lua.cpp +++ b/src/lua/lua.cpp @@ -670,6 +670,33 @@ bool lua_do_once(lua_State* LS, void* key) } } +std::list& userdata_recogn_fns() +{ + static std::list x; + return x; +} + +std::string try_recognize_userdata(lua_State* LS, int index) +{ + for(auto i : userdata_recogn_fns()) { + std::string x = i(LS, index); + if(x != "") + return x; + } + //Hack: Lua builtin file objects. + lua_pushstring(LS, "FILE*"); + lua_rawget(LS, LUA_REGISTRYINDEX); + if(lua_getmetatable(LS, index)) { + if(lua_rawequal(LS, -1, -2)) { + lua_pop(LS, 2); + return "FILE*"; + } + lua_pop(LS, 1); + } + lua_pop(LS, 1); + return "unknown"; +} + bool lua_requests_repaint = false; bool lua_requests_subframe_paint = false; bool lua_supported = true;