diff --git a/LUA_API.rst b/LUA_API.rst index 542034f40..22130efd6 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -550,6 +550,16 @@ Exception handling The default value of the ``verbose`` argument of ``err:tostring()``. +Miscellaneous +------------- + +* ``dfhack.curry(func,args...)``, or ``curry(func,args...)`` + + Returns a closure that invokes the function with args combined + both from the curry call and the closure call itself. I.e. + ``curry(func,a,b)(c,d)`` equals ``func(a,b,c,d)``. + + Locking and finalization ------------------------ diff --git a/Lua API.html b/Lua API.html index 63a4c8547..f6f2d42b3 100644 --- a/Lua API.html +++ b/Lua API.html @@ -337,42 +337,43 @@ ul.auto-toc {
The current version of DFHack has extensive support for @@ -842,8 +843,18 @@ following properties:
dfhack.curry(func,args...), or curry(func,args...)
+Returns a closure that invokes the function with args combined +both from the curry call and the closure call itself. I.e. +curry(func,a,b)(c,d) equals func(a,b,c,d).
+dfhack.with_suspend(f[,args...])
Calls f with arguments after grabbing the DF core suspend lock. @@ -876,7 +887,7 @@ Implemented using call_with_final
This api is intended for storing configuration options in the world itself. It probably should be restricted to data that is world-dependent.
Entries are identified by a string key, but it is also possible to manage @@ -911,7 +922,7 @@ functions can just copy values in memory without doing any actual I/O. However, currently every entry has a 180+-byte dead-weight overhead.
A material info record has fields:
type, index, material
@@ -956,7 +967,7 @@ Accept dfhack_material_category auto-assign table.Thin wrappers around C++ functions, similar to the ones for virtual methods. One notable difference is that these explicit wrappers allow argument count adjustment according to the usual lua rules, so trailing false/nil arguments @@ -985,7 +996,7 @@ can be omitted.
dfhack.gui.getCurViewscreen([skip_dismissed])
Returns the topmost viewscreen. If skip_dismissed is true, @@ -1032,7 +1043,7 @@ above operations accordingly. If enabled, pauses and zooms to position.
dfhack.job.cloneJobStruct(job)
Creates a deep copy of the given job.
@@ -1069,7 +1080,7 @@ a lua list containing them.dfhack.units.getPosition(unit)
Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.
@@ -1130,7 +1141,7 @@ or raws. The ignore_noble boolean disables thedfhack.items.getPosition(item)
Returns true x,y,z of the item, or nil if invalid; may be not equal to item.pos if in inventory.
@@ -1173,7 +1184,7 @@ Returns false in case of error.dfhack.maps.getSize()
Returns map size in blocks: x, y, z
@@ -1221,7 +1232,7 @@ burrows, or the presence of invaders.dfhack.burrows.findByName(name)
Returns the burrow pointer or nil.
@@ -1256,7 +1267,7 @@ burrows, or the presence of invaders.dfhack.buildings.setOwner(item,unit)
Replaces the owner of the building. If unit is nil, removes ownership. @@ -1400,7 +1411,7 @@ can be determined this way, constructBuilding
dfhack.constructions.designateNew(pos,type,item_type,mat_index)
Designates a new construction at given position. If there already is @@ -1416,7 +1427,7 @@ Returns true, was_only_planned if removed; or false if none fo
The screen module implements support for drawing to the tiled screen of the game. Note that drawing only has any effect when done from callbacks, so it can only be feasibly used in the core context.
@@ -1555,7 +1566,7 @@ options; if multiple interpretations exist, the table will contain multiple keysThese functions are intended for the use by dfhack developers, and are only documented here for completeness:
While plugins can create any number of interpreter instances, there is one special context managed by dfhack core. It is the only context that can receive events from DF and plugins.
@@ -1634,7 +1645,7 @@ Using timeout_active(id,nil) cancels the timerAn event is a native object transparently wrapping a lua table, and implementing a __call metamethod. When it is invoked, it loops through the table with next and calls all contained values. @@ -1666,7 +1677,7 @@ order using dfhack.safecall.
DFHack sets up the lua interpreter so that the built-in require function can be used to load shared lua code from hack/lua/. The dfhack namespace reference itself may be obtained via @@ -1695,7 +1706,7 @@ in this document.
A number of variables and functions are provided in the base global environment by the mandatory init file dfhack.lua:
utils.compare(a,b)
Comparator function; returns -1 if a<b, 1 if a>b, 0 otherwise.
@@ -1849,7 +1860,7 @@ throws an error.A third-party lua table dumper module from http://lua-users.org/wiki/DataDumper. Defines one function:
@@ -1863,14 +1874,14 @@ the other arguments see the original documentation link above.DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.
The following plugins have lua support.
Any files with the .lua extension placed into hack/scripts/* are automatically used by the DFHack core as commands. The matching command name consists of the name of the file sans diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 7c2c8f8d6..9f0477538 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1211,6 +1211,39 @@ static int dfhack_open_plugin(lua_State *L) return 0; } +static int dfhack_curry_wrap(lua_State *L) +{ + int nargs = lua_gettop(L); + int ncurry = lua_tointeger(L, lua_upvalueindex(1)); + int scount = nargs + ncurry; + + luaL_checkstack(L, ncurry, "stack overflow in curry"); + + // Insert values in O(N+M) by first shifting the existing data + lua_settop(L, scount); + for (int i = 0; i < nargs; i++) + lua_copy(L, nargs-i, scount-i); + for (int i = 1; i <= ncurry; i++) + lua_copy(L, lua_upvalueindex(i+1), i); + + lua_callk(L, scount-1, LUA_MULTRET, 0, lua_gettop); + + return lua_gettop(L); +} + +static int dfhack_curry(lua_State *L) +{ + luaL_checkany(L, 1); + if (lua_isnil(L, 1)) + luaL_argerror(L, 1, "nil function in curry"); + if (lua_gettop(L) == 1) + return 1; + lua_pushinteger(L, lua_gettop(L)); + lua_insert(L, 1); + lua_pushcclosure(L, dfhack_curry_wrap, lua_gettop(L)); + return 1; +} + bool Lua::IsCoreContext(lua_State *state) { // This uses a private field of the lua state to @@ -1234,6 +1267,7 @@ static const luaL_Reg dfhack_funcs[] = { { "call_with_finalizer", dfhack_call_with_finalizer }, { "with_suspend", lua_dfhack_with_suspend }, { "open_plugin", dfhack_open_plugin }, + { "curry", dfhack_curry }, { NULL, NULL } }; diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 2cbd019a6..a1e899761 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -46,6 +46,7 @@ end -- Error handling safecall = dfhack.safecall +curry = dfhack.curry function dfhack.pcall(f, ...) return xpcall(f, dfhack.onerror, ...) @@ -118,7 +119,12 @@ function defclass(class,parent) if parent then setmetatable(class, parent) else - rawset_default(class, { init_fields = rawset_default }) + rawset_default(class, { + init_fields = rawset_default, + callback = function(self, name, ...) + return dfhack.curry(self[name], self, ...) + end + }) end return class end