#include #include #include namespace { constexpr char TYPE_BUILDER_MT[] = "lsand.type_builder"; constexpr char RULE_BUILDER_MT[] = "lsand.rule_builder"; constexpr char RULE_MT[] = "lsand.rule"; constexpr char SAND_MT[] = "lsand.sand"; sand::type::builder& check_type_builder(lua_State* L, int index) { return *(sand::type::builder*)luaL_checkudata(L, index, TYPE_BUILDER_MT); } sand::rule::builder& check_rule_builder(lua_State* L, int index) { return *(sand::rule::builder*)luaL_checkudata(L, index, RULE_BUILDER_MT); } sand::rule& check_rule(lua_State* L, int index) { return *(sand::rule*)luaL_checkudata(L, index, RULE_MT); } sand::sand& check_sand(lua_State* L, int index) { return *(sand::sand*)luaL_checkudata(L, index, SAND_MT); } int l_type_builder_gc(lua_State* L) { auto& tb = check_type_builder(L, 1); tb.~builder(); return 0; } int l_type_builder_assign(lua_State* L) { auto& tb = check_type_builder(L, 1); sand::type t = tb.assign(); lua_pushinteger(L, t); return 1; } int l_type_builder_set_default_conversion(lua_State* L) { auto& tb = check_type_builder(L, 1); auto from = sand::type(luaL_checkinteger(L, 2)); auto to = sand::type(luaL_checkinteger(L, 3)); tb.set_default_conversion(to, from); return 0; } int l_type_builder_build(lua_State* L) { auto& tb = check_type_builder(L, 1); auto* rb = (sand::rule::builder*)lua_newuserdata(L, sizeof(sand::rule::builder)); luaL_getmetatable(L, RULE_BUILDER_MT); lua_setmetatable(L, -2); new (rb) sand::rule::builder(tb.build()); return 1; } int l_rule_builder_gc(lua_State* L) { auto& rb = check_rule_builder(L, 1); rb.~builder(); return 0; } int l_rule_builder_add_rule(lua_State* L) { auto& rb = check_rule_builder(L, 1); auto from = sand::type(luaL_checkinteger(L, 2)); auto to = sand::type(luaL_checkinteger(L, 3)); auto* rule = (sand::rule*)lua_newuserdata(L, sizeof(sand::rule)); luaL_getmetatable(L, RULE_MT); lua_setmetatable(L, -2); new (rule) sand::rule(rb.add_rule(from, to)); return 1; } int l_rule_builder_build(lua_State* L) { auto& rb = check_rule_builder(L, 1); int width = luaL_checkinteger(L, 2); int height = luaL_checkinteger(L, 3); auto* sand_object = (sand::sand*)lua_newuserdata(L, sizeof(sand::sand)); luaL_getmetatable(L, SAND_MT); lua_setmetatable(L, -2); new (sand_object) sand::sand(rb.build(width, height, sand::type(1))); return 1; } int l_rule_match(lua_State* L) { auto& r = check_rule(L, 1); std::string match_position = luaL_checkstring(L, 2); luaL_checktype(L, 3, LUA_TFUNCTION); int fn_ref = luaL_ref(L, LUA_REGISTRYINDEX); const auto& match_fn = [L, fn_ref](auto t) -> bool { lua_rawgeti(L, LUA_REGISTRYINDEX, fn_ref); lua_pushinteger(L, t); if (lua_pcall(L, 1, 1, 0) != LUA_OK) { const char* msg = lua_tostring(L, -1); lua_pop(L, 1); luaL_error(L, "Error during match callback: %s", msg); } if (!lua_isboolean(L, -1)) { luaL_error(L, "match callback did not return a boolean"); } bool value = lua_toboolean(L, -1); lua_pop(L, 1); return value; }; if (match_position == "top_left") { r.top_left(match_fn); } else if (match_position == "top") { r.top(match_fn); } else if (match_position == "top_right") { r.top_right(match_fn); } else if (match_position == "left") { r.left(match_fn); } else if (match_position == "right") { r.right(match_fn); } else if (match_position == "bottom_left") { r.bottom_left(match_fn); } else if (match_position == "bottom") { r.bottom(match_fn); } else if (match_position == "bottom_right") { r.bottom_right(match_fn); } else { luaL_argerror(L, 2, "Invalid match position"); } lua_pushvalue(L, 1); return 1; } int l_rule_finish(lua_State* L) { auto& r = check_rule(L, 1); r.~rule(); // mark rule as an invalid object // will error if any other methods // are called on this object lua_pushnil(L); lua_setmetatable(L, -1); return 0; } int l_sand_gc(lua_State* L) { auto& sand = check_sand(L, 1); sand.~sand(); return 0; } int l_sand_tick(lua_State* L) { auto& sand = check_sand(L, 1); sand.tick(); return 0; } int l_sand_get(lua_State* L) { auto& sand = check_sand(L, 1); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); lua_pushinteger(L, sand.get(x, y)); return 1; } int l_sand_set(lua_State* L) { auto& sand = check_sand(L, 1); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); sand::type t = sand::type(luaL_checkinteger(L, 4)); sand.set(x, y, t); return 0; } int l_lsand_new(lua_State* L) { auto* tb = (sand::type::builder*)lua_newuserdata(L, sizeof(sand::type::builder)); luaL_getmetatable(L, TYPE_BUILDER_MT); lua_setmetatable(L, -2); new (tb) sand::type::builder(); return 1; } constexpr luaL_Reg TYPE_BUILDER_FUNCS[] = { "__gc", l_type_builder_gc, "assign", l_type_builder_assign, "set_default_conversion", l_type_builder_set_default_conversion, "build", l_type_builder_build, nullptr, nullptr }; constexpr luaL_Reg RULE_BUILDER_FUNCS[] = { "__gc", l_rule_builder_gc, "add_rule", l_rule_builder_add_rule, "build", l_rule_builder_build, nullptr, nullptr }; constexpr luaL_Reg RULE_FUNCS[] = { "match", l_rule_match, "finish", l_rule_finish, nullptr, nullptr }; constexpr luaL_Reg SAND_FUNCS[] = { "__gc", l_sand_gc, "tick", l_sand_tick, "get", l_sand_get, "set", l_sand_set, nullptr, nullptr }; constexpr luaL_Reg LSAND_FUNCS[] = { "new", l_lsand_new, nullptr, nullptr }; } // namespace extern "C" int luaopen_lsand(lua_State* L) { luaL_newmetatable(L, TYPE_BUILDER_MT); luaL_register(L, nullptr, TYPE_BUILDER_FUNCS); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_newmetatable(L, RULE_BUILDER_MT); luaL_register(L, nullptr, RULE_BUILDER_FUNCS); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_newmetatable(L, RULE_MT); luaL_register(L, nullptr, RULE_FUNCS); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_newmetatable(L, SAND_MT); luaL_register(L, nullptr, SAND_FUNCS); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_openlib(L, "lsand", LSAND_FUNCS, 0); return 1; }