diff --git a/luad/conversions/enums.d b/luad/conversions/enums.d new file mode 100644 index 0000000..79da00f --- /dev/null +++ b/luad/conversions/enums.d @@ -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); + + //... +} diff --git a/luad/stack.d b/luad/stack.d index 50566bd..ad55f55 100644 --- a/luad/stack.d +++ b/luad/stack.d @@ -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; /** @@ -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); @@ -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)) @@ -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); @@ -626,4 +636,4 @@ unittest assert(stack[1].type == LuaType.String); assert(stack[2].type == LuaType.Number); assert(stack[3].type == LuaType.Boolean); -} \ No newline at end of file +} diff --git a/luad/state.d b/luad/state.d index d58757a..6c350bc 100644 --- a/luad/state.d +++ b/luad/state.d @@ -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;