Files
lunch-games/source/luad/base.d
2025-04-29 01:59:15 +02:00

225 lines
4.4 KiB
D

module luad.base;
import luad.c.all;
import luad.stack;
import core.stdc.string : strlen;
/**
* Enumerates all Lua types.
*/
enum LuaType
{
///string
String = LUA_TSTRING,
///number
Number = LUA_TNUMBER,
//table
Table = LUA_TTABLE,
///nil
Nil = LUA_TNIL,
///boolean
Boolean = LUA_TBOOLEAN,
///function
Function = LUA_TFUNCTION,
///userdata
Userdata = LUA_TUSERDATA,
///ditto
LightUserdata = LUA_TLIGHTUSERDATA,
///thread
Thread = LUA_TTHREAD
}
package struct Nil{}
/**
* Special value representing the Lua type and value nil.
* Examples:
* Useful for clearing keys in a table:
* --------------------------
lua["n"] = 1.23;
assert(lua.get!double("n") == 1.23);
lua["n"] = nil;
assert(lua["n"].type == LuaType.Nil);
* --------------------------
*/
public Nil nil;
/**
* Represents a reference to a Lua value of any type.
* It contains only the bare minimum of functionality which all Lua values support.
* For a generic reference type with more functionality, see $(DPREF dynamic,LuaDynamic).
*/
struct LuaObject
{
private:
int r = LUA_REFNIL;
lua_State* L = null;
package:
this(lua_State* L, int idx)
{
this.L = L;
lua_pushvalue(L, idx);
r = luaL_ref(L, LUA_REGISTRYINDEX);
}
void push() nothrow
{
lua_rawgeti(L, LUA_REGISTRYINDEX, r);
}
static void checkType(lua_State* L, int idx, int expectedType, const(char)* expectedName)
{
int t = lua_type(L, idx);
if(t != expectedType)
{
luaL_error(L, "attempt to create %s with %s", expectedName, lua_typename(L, t));
}
}
public:
@trusted this(this)
{
push();
r = luaL_ref(L, LUA_REGISTRYINDEX);
}
@trusted nothrow ~this()
{
luaL_unref(L, LUA_REGISTRYINDEX, r);
}
/// The underlying $(D lua_State) pointer for interfacing with C.
lua_State* state() pure nothrow @safe @property
{
return L;
}
/**
* Release this reference.
*
* This reference becomes a nil reference.
* This is only required when you want to _release the reference before the lifetime
* of this $(D LuaObject) has ended.
*/
void release() pure nothrow @safe
{
r = LUA_REFNIL;
L = null;
}
/**
* Type of referenced object.
* See_Also:
* $(MREF LuaType)
*/
@property LuaType type() @trusted nothrow
{
push();
auto result = cast(LuaType)lua_type(state, -1);
lua_pop(state, 1);
return result;
}
/**
* Type name of referenced object.
*/
@property string typeName() @trusted /+ nothrow +/
{
push();
const(char)* cname = luaL_typename(state, -1); // TODO: Doesn't have to use luaL_typename, i.e. no copy
auto name = cname[0.. strlen(cname)].idup;
lua_pop(state, 1);
return name;
}
/// Boolean whether or not the referenced object is nil.
@property bool isNil() pure nothrow @safe
{
return r == LUA_REFNIL;
}
/**
* Convert the referenced object into a textual representation.
*
* The returned string is formatted in the same way the Lua $(D tostring) function formats.
*
* Returns:
* String representation of referenced object
*/
string toString() @trusted
{
push();
size_t len;
const(char)* cstr = luaL_tolstring(state, -1, &len);
auto str = cstr[0 .. len].idup;
lua_pop(state, 2);
return str;
}
/**
* Attempt _to convert the referenced object _to the specified D type.
* Examples:
-----------------------
auto results = lua.doString(`return "hello!"`);
assert(results[0].to!string() == "hello!");
-----------------------
*/
T to(T)()
{
static void typeMismatch(lua_State* L, int t, int e)
{
luaL_error(L, "attempt to convert LuaObject with type %s to a %s", lua_typename(L, t), lua_typename(L, e));
}
push();
return popValue!(T, typeMismatch)(state);
}
/**
* Compare this object to another with Lua's equality semantics.
* Also returns false if the two objects are in different Lua states.
*/
bool opEquals(T : LuaObject)(ref T o) @trusted
{
if(o.state != this.state)
return false;
push();
o.push();
scope(success) lua_pop(state, 2);
return lua_equal(state, -1, -2);
}
}
unittest
{
lua_State* L = luaL_newstate();
scope(success) lua_close(L);
lua_pushstring(L, "foobar");
auto o = popValue!LuaObject(L);
assert(!o.isNil);
assert(o.type == LuaType.String);
assert(o.typeName == "string");
assert(o.to!string() == "foobar");
lua_pushnil(L);
auto nilref = popValue!LuaObject(L);
assert(nilref.isNil);
assert(nilref.typeName == "nil");
assert(o != nilref);
auto o2 = o;
assert(o2 == o);
}