Skip to content

Commit 7265e68

Browse files
committed
Added support for enum's.
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 >_<).
1 parent ff8dfe5 commit 7265e68

3 files changed

Lines changed: 166 additions & 1 deletion

File tree

luad/conversions/enums.d

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
Internal module for pushing and getting _enums.
3+
4+
Enum's are treated in Lua as strings.
5+
6+
Conversion of enum keys is case-insensitive, which I think is more useful for Lua's typical 'config' style usage
7+
8+
This is still a work-in-progress. Outstanding issues include:
9+
Handling of bitfields.
10+
Assignment of integer keys?
11+
Conversion function needs to be improved (linear search! >_<)
12+
*/
13+
module luad.conversions.enums;
14+
15+
import luad.c.all;
16+
import luad.stack;
17+
18+
import std.traits;
19+
import std.conv;
20+
import std.range;
21+
import std.string;
22+
23+
struct KeyValuePair(ValueType)
24+
{
25+
string key;
26+
ValueType value;
27+
}
28+
29+
// produce a tuple of KeyValuePair's for an enum.
30+
template EnumKeyValuePair(Enum)
31+
{
32+
template impl(size_t len, size_t offset, Items...)
33+
{
34+
static if(offset == len)
35+
alias impl = TypeTuple!();
36+
else
37+
alias impl = TypeTuple!(KeyValuePair!Enum(Items[offset], Items[len + offset]), impl!(len, offset + 1, Items));
38+
}
39+
40+
alias Keys = TypeTuple!(__traits(allMembers, Enum));
41+
alias Values = EnumMembers!Enum;
42+
static assert(Keys.length == Values.length);
43+
44+
alias EnumKeyValuePair = impl!(Keys.length, 0, TypeTuple!(Keys, Values));
45+
}
46+
47+
immutable(KeyValuePair!Enum)[] getKeyValuePairs(Enum)() pure nothrow @nogc
48+
{
49+
static immutable(KeyValuePair!Enum[]) kvp = [ EnumKeyValuePair!Enum ];
50+
return kvp;
51+
}
52+
53+
// TODO: These linear lookups are pretty crappy... we can do better, but this get's us working.
54+
Enum getEnumValue(Enum)(lua_State* L, const(char)[] value) if(is(Enum == enum))
55+
{
56+
value = value.strip;
57+
if(!value.empty)
58+
{
59+
auto kvp = getKeyValuePairs!Enum();
60+
foreach(ref i; kvp)
61+
{
62+
if(!icmp(i.key, value)) // case inseneitive enum keys...
63+
return i.value;
64+
}
65+
}
66+
luaL_error(L, "invalid enum key '%s' for enum type %s", value.ptr, Enum.stringof.ptr);
67+
return Enum.init;
68+
}
69+
70+
string getEnumFromValue(Enum)(Enum value)
71+
{
72+
auto kvp = getKeyValuePairs!Enum();
73+
foreach(ref i; kvp)
74+
{
75+
if(value == i.value)
76+
return i.key;
77+
}
78+
return null;
79+
}
80+
81+
void pushEnum(T)(lua_State* L, T value) if (is(T == enum))
82+
{
83+
string key = getEnumFromValue(value);
84+
if(key)
85+
lua_pushlstring(L, key.ptr, key.length);
86+
else
87+
luaL_error(L, "invalid value for enum type %s", T.stringof.ptr);
88+
}
89+
90+
T getEnum(T)(lua_State* L, int idx) if(is(T == enum))
91+
{
92+
// TODO: check to see if idx is a number, if it is, convert it directly?
93+
94+
size_t len;
95+
const(char)* s = lua_tolstring(L, idx, &len);
96+
return getEnumValue!T(L, s[0..len]);
97+
}
98+
99+
void pushStaticTypeInterface(T)(lua_State* L) if(is(T == enum))
100+
{
101+
lua_newtable(L);
102+
103+
// TODO: we could get fancy and make an __index table of keys, so that they are read-only
104+
// ... but for now, we'll just populate a table with the keys as strings
105+
106+
// set 'init'
107+
string initVal = getEnumFromValue(T.init);
108+
lua_pushlstring(L, initVal.ptr, initVal.length);
109+
lua_setfield(L, -2, "init");
110+
111+
// TODO: integral enums also have 'min' and 'max'
112+
113+
// we'll create tables for the keys and valyes arrays.
114+
lua_newtable(L); // keys
115+
lua_newtable(L); // values
116+
117+
// add the enum keys
118+
auto kvp = getKeyValuePairs!T();
119+
foreach(int i, ref e; kvp)
120+
{
121+
// set the key to the key string (lua will carry enums by string)
122+
lua_pushlstring(L, e.key.ptr, e.key.length);
123+
lua_setfield(L, -4, e.key.ptr);
124+
125+
// push the key to the keys array
126+
lua_pushlstring(L, e.key.ptr, e.key.length);
127+
lua_rawseti(L, -3, i+1);
128+
129+
// push the value to the values array
130+
pushValue!(OriginalType!T)(L, e.value);
131+
lua_rawseti(L, -2, i+1);
132+
}
133+
134+
lua_setfield(L, -3, "values");
135+
lua_setfield(L, -2, "keys");
136+
}
137+
138+
version(unittest)
139+
{
140+
import luad.base;
141+
142+
//...
143+
}
144+
145+
unittest
146+
{
147+
import luad.testing;
148+
149+
lua_State* L = luaL_newstate();
150+
scope(success) lua_close(L);
151+
luaL_openlibs(L);
152+
153+
//...
154+
}

luad/stack.d

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import luad.conversions.arrays;
7171
import luad.conversions.structs;
7272
import luad.conversions.assocarrays;
7373
import luad.conversions.classes;
74+
import luad.conversions.enums;
7475
import luad.conversions.variant;
7576

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

94+
else static if(is(T == enum))
95+
pushEnum(L, value);
96+
9397
else static if(is(T == bool))
9498
lua_pushboolean(L, cast(bool)value);
9599

@@ -157,7 +161,10 @@ template isVoidArray(T)
157161
*/
158162
template luaTypeOf(T)
159163
{
160-
static if(is(T == bool))
164+
static if(is(T == enum))
165+
enum luaTypeOf = LUA_TSTRING;
166+
167+
else static if(is(T == bool))
161168
enum luaTypeOf = LUA_TBOOLEAN;
162169

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

261+
else static if(is(T == enum))
262+
return getEnum!T(L, idx);
263+
254264
else static if(is(T == bool))
255265
return lua_toboolean(L, idx);
256266

luad/state.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import std.typecons : isTuple;
88
import luad.c.all;
99
import luad.stack;
1010
import luad.conversions.classes;
11+
import luad.conversions.enums;
1112

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

0 commit comments

Comments
 (0)