Skip to content

Commit

Permalink
Added support for enum's.
Browse files Browse the repository at this point in the history
Enums are held in Lua as strings.
No support for enum values that are not valid keys.
No support for bitfields (yet?).
Conversion function performance could be improved (linear search >_<).
  • Loading branch information
TurkeyMan committed Aug 21, 2014
1 parent c2d85ad commit 66ec778
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 2 deletions.
182 changes: 182 additions & 0 deletions luad/conversions/enums.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/**
Internal module for pushing and getting _enums.
... info?
*/
module luad.conversions.enums;

import luad.c.all;
import luad.stack;

import std.traits;
import std.conv;
import std.range;
import std.string;

struct KeyValuePair(ValueType)
{
string key;
ValueType value;
}

// produce a tuple of KeyValuePair's for an enum.
template EnumKeyValuePair(Enum)
{
template impl(size_t len, size_t offset, Items...)
{
static if(offset == len)
alias impl = TypeTuple!();
else
alias impl = TypeTuple!(KeyValuePair!Enum(Items[offset], Items[len + offset]), impl!(len, offset + 1, Items));
}

alias Keys = TypeTuple!(__traits(allMembers, Enum));
alias Values = EnumMembers!Enum;
static assert(Keys.length == Values.length);

alias EnumKeyValuePair = impl!(Keys.length, 0, TypeTuple!(Keys, Values));
}

immutable(KeyValuePair!Enum)[] getKeyValuePairs(Enum)() pure nothrow @nogc
{
static immutable(KeyValuePair!Enum[]) kvp = [ EnumKeyValuePair!Enum ];
return kvp;
}

// TODO: These linear lookups are pretty crappy... we can do better, but this get's us working.
Enum getEnumValue(Enum)(lua_State* L, const(char)[] value) if(is(Enum == enum))
{
value = value.strip;
if(!value.empty)
{
auto kvp = getKeyValuePairs!Enum();
foreach(ref i; kvp)
{
if(!icmp(i.key, value)) // case inseneitive enum keys...
return i.value;
}
}
luaL_error(L, "invalid enum key '%s' for enum type %s", value.ptr, Enum.stringof.ptr);
return Enum.init;
}

string getEnumFromValue(Enum)(Enum value)
{
auto kvp = getKeyValuePairs!Enum();
foreach(ref i; kvp)
{
if(value == i.value)
return i.key;
}
return null;
}

/+ TODO: I'd quite like to support bitfields too...
uint getBitfieldValue(Enum)(const(char)[] flags)
{
uint value;
foreach(token; flags.splitter('|').map!(a => a.strip).filter!(a => !a.empty))
{
Enum val = getEnumValue!Enum(token);
if(val != cast(Enum)-1)
value |= val;
}
return value;
}
string getBitfieldFromValue(Enum)(uint bits)
{
string bitfield;
foreach(i; 0..32)
{
uint bit = 1 << i;
if(!(bits & bit))
continue;
string key = getEnumFromValue(cast(Enum)bit);
if(key)
{
if(!bitfield)
bitfield = key;
else
bitfield = bitfield ~ "|" ~ key;
}
}
return bitfield;
}
+/

void pushEnum(T)(lua_State* L, T value) if (is(T == enum))
{
string key = getEnumFromValue(value);
if(key)
lua_pushlstring(L, key.ptr, key.length);
else
luaL_error(L, "invalid value for enum type %s", T.stringof.ptr);
}

T getEnum(T)(lua_State* L, int idx) if(is(T == enum))
{
// TODO: check to see if idx is a number, if it is, convert it directly?

size_t len;
const(char)* s = lua_tolstring(L, idx, &len);
return getEnumValue!T(L, s[0..len]);
}

void pushStaticTypeInterface(T)(lua_State* L) if(is(T == enum))
{
lua_newtable(L);

// TODO: we could get fancy and make an __index table of keys, so that they are read-only
// ... but for now, we'll just populate a table with the keys as strings

// set 'init'
string initVal = getEnumFromValue(T.init);
lua_pushlstring(L, initVal.ptr, initVal.length);
lua_setfield(L, -2, "init");

// TODO: integral enums also have 'min' and 'max'

// we'll create tables for the keys and valyes arrays.
lua_newtable(L); // keys
lua_newtable(L); // values

// add the enum keys
auto kvp = getKeyValuePairs!T();
foreach(int i, ref e; kvp)
{
// set the key to the key string (lua will carry enums by string)
lua_pushlstring(L, e.key.ptr, e.key.length);
lua_setfield(L, -4, e.key.ptr);

// push the key to the keys array
lua_pushlstring(L, e.key.ptr, e.key.length);
lua_rawseti(L, -3, i+1);

// push the value to the values array
pushValue!(OriginalType!T)(L, e.value);
lua_rawseti(L, -2, i+1);
}

lua_setfield(L, -3, "values");
lua_setfield(L, -2, "keys");
}

version(unittest)
{
import luad.base;

//...
}

unittest
{
import luad.testing;

lua_State* L = luaL_newstate();
scope(success) lua_close(L);
luaL_openlibs(L);

//...
}
14 changes: 12 additions & 2 deletions luad/stack.d
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import luad.conversions.arrays;
import luad.conversions.structs;
import luad.conversions.assocarrays;
import luad.conversions.classes;
import luad.conversions.enums;
import luad.conversions.variant;

/**
Expand All @@ -90,6 +91,9 @@ void pushValue(T)(lua_State* L, T value)
else static if(is(T == Nil))
lua_pushnil(L);

else static if(is(T == enum))
pushEnum(L, value);

else static if(is(T == bool))
lua_pushboolean(L, cast(bool)value);

Expand Down Expand Up @@ -157,7 +161,10 @@ template isVoidArray(T)
*/
template luaTypeOf(T)
{
static if(is(T == bool))
static if(is(T == enum))
enum luaTypeOf = LUA_TSTRING;

else static if(is(T == bool))
enum luaTypeOf = LUA_TBOOLEAN;

else static if(is(T == Nil))
Expand Down Expand Up @@ -251,6 +258,9 @@ T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int
else static if(is(T == Nil))
return nil;

else static if(is(T == enum))
return getEnum!T(L, idx);

else static if(is(T == bool))
return lua_toboolean(L, idx);

Expand Down Expand Up @@ -626,4 +636,4 @@ unittest
assert(stack[1].type == LuaType.String);
assert(stack[2].type == LuaType.Number);
assert(stack[3].type == LuaType.Boolean);
}
}
1 change: 1 addition & 0 deletions luad/state.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import std.typecons : isTuple;
import luad.c.all;
import luad.stack;
import luad.conversions.classes;
import luad.conversions.enums;

import luad.base, luad.table, luad.lfunction, luad.dynamic, luad.error;

Expand Down

0 comments on commit 66ec778

Please sign in to comment.