From a260aa27ab2af0c71d29b3e18cfa30569d3bcd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be> Date: Fri, 27 Dec 2013 20:50:41 +0100 Subject: Sluift: Add with() function Change-Id: Ife0a7748c2b354017bec5cfdddb0d096950dd15b diff --git a/Sluift/Examples/EchoBot_With.lua b/Sluift/Examples/EchoBot_With.lua new file mode 100644 index 0000000..1f7d0bb --- /dev/null +++ b/Sluift/Examples/EchoBot_With.lua @@ -0,0 +1,32 @@ +--[[ + Copyright (c) 2010-2013 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Alternative version of EchoBot that uses with() + + This script logs into an XMPP server, sends initial presence, + and then waits for incoming messages, and echoes them back. + + The following environment variables are used: + * SLUIFT_JID, SWIFT_PASS: JID and password to log in with + * SLUIFT_DEBUG: Sets whether debugging should be turned on + +--]] + +require "sluift" + +sluift.debug = os.getenv("SLUIFT_DEBUG") or false + +client = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) +sluift.with(client, function () + connect() + set_version{name = "EchoBot", version = "0.1"} + send_presence("Send me a message") + for message in messages() do + send_message{to = message["from"], body = message["body"]} + end +end) diff --git a/Sluift/core.lua b/Sluift/core.lua index aeb3286..f387354 100644 --- a/Sluift/core.lua +++ b/Sluift/core.lua @@ -97,10 +97,31 @@ local function copy(object) end end +local function clear(table) + setmetatable(table, nil) + for key, value in pairs(table) do + rawset(table, key, nil) + end +end + local function trim(string) return string:gsub("^%s*(.-)%s*$", "%1") end +local function keys(table) + local result = {} + for key in pairs(table) do + result[#result+1] = key + end + return result +end + +local function insert_all(table, values) + for _, value in pairs(values) do + table[#table+1] = value + end +end + -------------------------------------------------------------------------------- -- Help -------------------------------------------------------------------------------- @@ -405,7 +426,9 @@ end _H = { [[ Client interface ]] } -local Client = {} +local Client = { + _with_prompt = function(client) return client:jid() end +} Client.__index = Client register_class_table_help(Client, "Client") @@ -424,6 +447,90 @@ local PubSubNode = {} PubSubNode.__index = PubSubNode register_class_table_help(PubSubNode, "PubSubNode") + +-------------------------------------------------------------------------------- +-- with +-------------------------------------------------------------------------------- + +local original_G + +local function with (target, f) + -- Dynamic scope + if f then + with(target) + return call{f, finally = function() with() end} + end + + -- No scope + if target then + if not original_G then + original_G = copy(_G) + setmetatable(original_G, getmetatable(_G)) + clear(_G) + end + + setmetatable(_G, { + __index = function(_, key) + local value = target[key] + if value then + if type(value) == 'function' then + -- Add 'self' argument to all functions + return function(...) return value(target, ...) end + else + return value + end + else + return original_G[key] + end + end, + __newindex = original_G, + _completions = function () + local result = {} + if type(target) == "table" then + insert_all(result, keys(target)) + end + local mt = getmetatable(target) + if mt and type(mt.__index) == 'table' then + insert_all(result, keys(mt.__index)) + end + insert_all(result, keys(original_G)) + return result + end + }) + + -- Set prompt + local prompt = nil + + -- Try '_with_prompt' in metatable + local target_metatable = getmetatable(target) + if target_metatable then + if type(target_metatable._with_prompt) == "function" then + prompt = target_metatable._with_prompt(target) + else + prompt = target_metatable._with_prompt + end + end + + if not prompt then + -- Use tostring() + local target_string = tostring(target) + if string.len(target_string) > 25 then + prompt = string.sub(target_string, 0, 22) .. "..." + else + prompt = target_string + end + end + rawset(_G, "_PROMPT", prompt .. '> ') + else + -- Reset _G + clear(_G) + for key, value in pairs(original_G) do + _G[key] = value + end + setmetatable(_G, original_G) + end +end + -------------------------------------------------------------------------------- -- Client -------------------------------------------------------------------------------- @@ -884,4 +991,5 @@ return { help = help, extra_help = extra_help, copy = copy, + with = with } diff --git a/Sluift/main.cpp b/Sluift/main.cpp index e2fa9c8..76ba572 100644 --- a/Sluift/main.cpp +++ b/Sluift/main.cpp @@ -14,6 +14,7 @@ #include <boost/program_options.hpp> #include <boost/version.hpp> #include <boost/numeric/conversion/cast.hpp> +#include <boost/assign/list_of.hpp> #include <Sluift/Console.h> #include <Sluift/StandardTerminal.h> #include <Sluift/sluift.h> @@ -151,8 +152,13 @@ int main(int argc, char* argv[]) { if (arguments.count("interactive") || arguments.count("script") == 0) { // Import some useful functions into the global namespace lua_getglobal(L, "sluift"); - lua_getfield(L, -1, "help"); - lua_setglobal(L, "help"); + std::vector<std::string> globalImports = boost::assign::list_of + ("help")("with"); + foreach (const std::string& globalImport, globalImports) { + lua_getfield(L, -1, globalImport.c_str()); + lua_setglobal(L, globalImport.c_str()); + } + lua_pop(L, 1); std::cout << SLUIFT_WELCOME_STRING << std::endl; #ifdef HAVE_EDITLINE diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index b2bdc29..39b92fc 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -126,7 +126,7 @@ static int sluift_index(lua_State* L) { lua_pushnumber(L, Sluift::globals.timeout); return 1; } - throw Lua::Exception("Unknown property"); + return 0; } catch (const std::exception& e) { return luaL_error(L, e.what()); @@ -339,7 +339,7 @@ SLUIFT_API int luaopen_sluift(lua_State* L) { // Register convenience functions lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); std::vector<std::string> coreLibExports = boost::assign::list_of - ("tprint")("disco")("help")("get_help")("copy"); + ("tprint")("disco")("help")("get_help")("copy")("with"); foreach (const std::string& coreLibExport, coreLibExports) { lua_getfield(L, -1, coreLibExport.c_str()); lua_setfield(L, -3, coreLibExport.c_str()); -- cgit v0.10.2-6-g49f6