Provide a partial application utility function to lua.

Implemented in C++ for efficiency.
develop
Alexander Gavrilov 2012-09-05 17:37:36 +04:00
parent 9c6fcee9a9
commit 27f169e298
4 changed files with 110 additions and 49 deletions

@ -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
------------------------

@ -337,42 +337,43 @@ ul.auto-toc {
<li><a class="reference internal" href="#native-utilities" id="id11">Native utilities</a><ul>
<li><a class="reference internal" href="#input-output" id="id12">Input &amp; Output</a></li>
<li><a class="reference internal" href="#exception-handling" id="id13">Exception handling</a></li>
<li><a class="reference internal" href="#locking-and-finalization" id="id14">Locking and finalization</a></li>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id15">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#material-info-lookup" id="id16">Material info lookup</a></li>
<li><a class="reference internal" href="#miscellaneous" id="id14">Miscellaneous</a></li>
<li><a class="reference internal" href="#locking-and-finalization" id="id15">Locking and finalization</a></li>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id16">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#material-info-lookup" id="id17">Material info lookup</a></li>
</ul>
</li>
<li><a class="reference internal" href="#c-function-wrappers" id="id17">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id18">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id19">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id20">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id21">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id22">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id23">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id24">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id25">Constructions module</a></li>
<li><a class="reference internal" href="#screen-api" id="id26">Screen API</a></li>
<li><a class="reference internal" href="#internal-api" id="id27">Internal API</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id18">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id19">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id20">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id21">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id22">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id23">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id24">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id25">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id26">Constructions module</a></li>
<li><a class="reference internal" href="#screen-api" id="id27">Screen API</a></li>
<li><a class="reference internal" href="#internal-api" id="id28">Internal API</a></li>
</ul>
</li>
<li><a class="reference internal" href="#core-interpreter-context" id="id28">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id29">Event type</a></li>
<li><a class="reference internal" href="#core-interpreter-context" id="id29">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id30">Event type</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#lua-modules" id="id30">Lua Modules</a><ul>
<li><a class="reference internal" href="#global-environment" id="id31">Global environment</a></li>
<li><a class="reference internal" href="#utils" id="id32">utils</a></li>
<li><a class="reference internal" href="#dumper" id="id33">dumper</a></li>
<li><a class="reference internal" href="#lua-modules" id="id31">Lua Modules</a><ul>
<li><a class="reference internal" href="#global-environment" id="id32">Global environment</a></li>
<li><a class="reference internal" href="#utils" id="id33">utils</a></li>
<li><a class="reference internal" href="#dumper" id="id34">dumper</a></li>
</ul>
</li>
<li><a class="reference internal" href="#plugins" id="id34">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id35">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id36">sort</a></li>
<li><a class="reference internal" href="#plugins" id="id35">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id36">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id37">sort</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scripts" id="id37">Scripts</a></li>
<li><a class="reference internal" href="#scripts" id="id38">Scripts</a></li>
</ul>
</div>
<p>The current version of DFHack has extensive support for
@ -842,8 +843,18 @@ following properties:</p>
</li>
</ul>
</div>
<div class="section" id="miscellaneous">
<h3><a class="toc-backref" href="#id14">Miscellaneous</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.curry(func,args...)</span></tt>, or <tt class="docutils literal"><span class="pre">curry(func,args...)</span></tt></p>
<p>Returns a closure that invokes the function with args combined
both from the curry call and the closure call itself. I.e.
<tt class="docutils literal"><span class="pre">curry(func,a,b)(c,d)</span></tt> equals <tt class="docutils literal">func(a,b,c,d)</tt>.</p>
</li>
</ul>
</div>
<div class="section" id="locking-and-finalization">
<h3><a class="toc-backref" href="#id14">Locking and finalization</a></h3>
<h3><a class="toc-backref" href="#id15">Locking and finalization</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_suspend(f[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">f</tt> with arguments after grabbing the DF core suspend lock.
@ -876,7 +887,7 @@ Implemented using <tt class="docutils literal"><span class="pre">call_with_final
</ul>
</div>
<div class="section" id="persistent-configuration-storage">
<h3><a class="toc-backref" href="#id15">Persistent configuration storage</a></h3>
<h3><a class="toc-backref" href="#id16">Persistent configuration storage</a></h3>
<p>This api is intended for storing configuration options in the world itself.
It probably should be restricted to data that is world-dependent.</p>
<p>Entries are identified by a string <tt class="docutils literal">key</tt>, 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.</p>
</div>
<div class="section" id="material-info-lookup">
<h3><a class="toc-backref" href="#id16">Material info lookup</a></h3>
<h3><a class="toc-backref" href="#id17">Material info lookup</a></h3>
<p>A material info record has fields:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p>
@ -956,7 +967,7 @@ Accept dfhack_material_category auto-assign table.</p>
</div>
</div>
<div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id17">C++ function wrappers</a></h2>
<h2><a class="toc-backref" href="#id18">C++ function wrappers</a></h2>
<p>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.</p>
</li>
</ul>
<div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id18">Gui module</a></h3>
<h3><a class="toc-backref" href="#id19">Gui module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getCurViewscreen([skip_dismissed])</span></tt></p>
<p>Returns the topmost viewscreen. If <tt class="docutils literal">skip_dismissed</tt> is <em>true</em>,
@ -1032,7 +1043,7 @@ above operations accordingly. If enabled, pauses and zooms to position.</p>
</ul>
</div>
<div class="section" id="job-module">
<h3><a class="toc-backref" href="#id19">Job module</a></h3>
<h3><a class="toc-backref" href="#id20">Job module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p>
@ -1069,7 +1080,7 @@ a lua list containing them.</p>
</ul>
</div>
<div class="section" id="units-module">
<h3><a class="toc-backref" href="#id20">Units module</a></h3>
<h3><a class="toc-backref" href="#id21">Units module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p>
<p>Returns true <em>x,y,z</em> of the unit, or <em>nil</em> if invalid; may be not equal to unit.pos if caged.</p>
@ -1130,7 +1141,7 @@ or raws. The <tt class="docutils literal">ignore_noble</tt> boolean disables the
</ul>
</div>
<div class="section" id="items-module">
<h3><a class="toc-backref" href="#id21">Items module</a></h3>
<h3><a class="toc-backref" href="#id22">Items module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p>
<p>Returns true <em>x,y,z</em> of the item, or <em>nil</em> if invalid; may be not equal to item.pos if in inventory.</p>
@ -1173,7 +1184,7 @@ Returns <em>false</em> in case of error.</p>
</ul>
</div>
<div class="section" id="maps-module">
<h3><a class="toc-backref" href="#id22">Maps module</a></h3>
<h3><a class="toc-backref" href="#id23">Maps module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getSize()</tt></p>
<p>Returns map size in blocks: <em>x, y, z</em></p>
@ -1221,7 +1232,7 @@ burrows, or the presence of invaders.</p>
</ul>
</div>
<div class="section" id="burrows-module">
<h3><a class="toc-backref" href="#id23">Burrows module</a></h3>
<h3><a class="toc-backref" href="#id24">Burrows module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.findByName(name)</tt></p>
<p>Returns the burrow pointer or <em>nil</em>.</p>
@ -1256,7 +1267,7 @@ burrows, or the presence of invaders.</p>
</ul>
</div>
<div class="section" id="buildings-module">
<h3><a class="toc-backref" href="#id24">Buildings module</a></h3>
<h3><a class="toc-backref" href="#id25">Buildings module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.setOwner(item,unit)</tt></p>
<p>Replaces the owner of the building. If unit is <em>nil</em>, removes ownership.
@ -1400,7 +1411,7 @@ can be determined this way, <tt class="docutils literal">constructBuilding</tt>
</ul>
</div>
<div class="section" id="constructions-module">
<h3><a class="toc-backref" href="#id25">Constructions module</a></h3>
<h3><a class="toc-backref" href="#id26">Constructions module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateNew(pos,type,item_type,mat_index)</tt></p>
<p>Designates a new construction at given position. If there already is
@ -1416,7 +1427,7 @@ Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none fo
</ul>
</div>
<div class="section" id="screen-api">
<h3><a class="toc-backref" href="#id26">Screen API</a></h3>
<h3><a class="toc-backref" href="#id27">Screen API</a></h3>
<p>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.</p>
@ -1555,7 +1566,7 @@ options; if multiple interpretations exist, the table will contain multiple keys
</ul>
</div>
<div class="section" id="internal-api">
<h3><a class="toc-backref" href="#id27">Internal API</a></h3>
<h3><a class="toc-backref" href="#id28">Internal API</a></h3>
<p>These functions are intended for the use by dfhack developers,
and are only documented here for completeness:</p>
<ul>
@ -1603,7 +1614,7 @@ Returns: <em>found_index</em>, or <em>nil</em> if end reached.</p>
</div>
</div>
<div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id28">Core interpreter context</a></h2>
<h2><a class="toc-backref" href="#id29">Core interpreter context</a></h2>
<p>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.</p>
@ -1634,7 +1645,7 @@ Using <tt class="docutils literal">timeout_active(id,nil)</tt> cancels the timer
</li>
</ul>
<div class="section" id="event-type">
<h3><a class="toc-backref" href="#id29">Event type</a></h3>
<h3><a class="toc-backref" href="#id30">Event type</a></h3>
<p>An 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 <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div>
</div>
<div class="section" id="lua-modules">
<h1><a class="toc-backref" href="#id30">Lua Modules</a></h1>
<h1><a class="toc-backref" href="#id31">Lua Modules</a></h1>
<p>DFHack sets up the lua interpreter so that the built-in <tt class="docutils literal">require</tt>
function can be used to load shared lua code from hack/lua/.
The <tt class="docutils literal">dfhack</tt> namespace reference itself may be obtained via
@ -1695,7 +1706,7 @@ in this document.</p>
</li>
</ul>
<div class="section" id="global-environment">
<h2><a class="toc-backref" href="#id31">Global environment</a></h2>
<h2><a class="toc-backref" href="#id32">Global environment</a></h2>
<p>A number of variables and functions are provided in the base global
environment by the mandatory init file dfhack.lua:</p>
<ul>
@ -1736,7 +1747,7 @@ Returns <em>nil</em> if any of obj or indices is <em>nil</em>, or a numeric inde
</ul>
</div>
<div class="section" id="utils">
<h2><a class="toc-backref" href="#id32">utils</a></h2>
<h2><a class="toc-backref" href="#id33">utils</a></h2>
<ul>
<li><p class="first"><tt class="docutils literal">utils.compare(a,b)</tt></p>
<p>Comparator function; returns <em>-1</em> if a&lt;b, <em>1</em> if a&gt;b, <em>0</em> otherwise.</p>
@ -1849,7 +1860,7 @@ throws an error.</p>
</ul>
</div>
<div class="section" id="dumper">
<h2><a class="toc-backref" href="#id33">dumper</a></h2>
<h2><a class="toc-backref" href="#id34">dumper</a></h2>
<p>A third-party lua table dumper module from
<a class="reference external" href="http://lua-users.org/wiki/DataDumper">http://lua-users.org/wiki/DataDumper</a>. Defines one
function:</p>
@ -1863,14 +1874,14 @@ the other arguments see the original documentation link above.</p>
</div>
</div>
<div class="section" id="plugins">
<h1><a class="toc-backref" href="#id34">Plugins</a></h1>
<h1><a class="toc-backref" href="#id35">Plugins</a></h1>
<p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p>
<div class="section" id="burrows">
<h2><a class="toc-backref" href="#id35">burrows</a></h2>
<h2><a class="toc-backref" href="#id36">burrows</a></h2>
<p>Implements extended burrow manipulations.</p>
<p>Events:</p>
<ul>
@ -1908,13 +1919,13 @@ set is the same as used by the command line.</p>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div>
<div class="section" id="sort">
<h2><a class="toc-backref" href="#id36">sort</a></h2>
<h2><a class="toc-backref" href="#id37">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p>
</div>
</div>
<div class="section" id="scripts">
<h1><a class="toc-backref" href="#id37">Scripts</a></h1>
<h1><a class="toc-backref" href="#id38">Scripts</a></h1>
<p>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

@ -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 }
};

@ -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