Mercurial > code
changeset 200:617459270743
Add XML parser
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sat, 28 Dec 2013 11:16:11 +0100 |
parents | 08eb7b00b950 |
children | 8ee4a34e166c |
files | C++/Luae.cpp C++/Luae.h C++/XmlParser.cpp C++/XmlParser.h |
diffstat | 4 files changed, 430 insertions(+), 85 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Luae.cpp Sat Nov 30 09:58:48 2013 +0100 +++ b/C++/Luae.cpp Sat Dec 28 11:16:11 2013 +0100 @@ -1,5 +1,5 @@ /* - * Lua.cpp -- Lua helpers and such + * Luae.cpp -- Lua helpers and such * * Copyright (c) 2013 David Demelier <markand@malikania.fr> * @@ -18,29 +18,51 @@ #include "Luae.h" +namespace malikania { + /* -------------------------------------------------------- * LuaeState * -------------------------------------------------------- */ +void LuaeState::initRegistry() +{ + if (LuaeTable::type(*this, LUA_REGISTRYINDEX, "refs") == LUA_TNIL) { + lua_createtable(*this, 0, 0); + lua_createtable(*this, 0, 1); + lua_pushstring(*this, "v"); + lua_setfield(*this, -2, "__mode"); + lua_setmetatable(*this, -2); + lua_setfield(*this, LUA_REGISTRYINDEX, "refs"); + } +} + LuaeState::LuaeState() { m_state = Ptr(luaL_newstate()); + + initRegistry(); } LuaeState::LuaeState(lua_State *L) { m_state = Ptr(L); + + initRegistry(); } LuaeState::LuaeState(LuaeState &&state) { m_state = std::move(state.m_state); + + initRegistry(); } LuaeState &LuaeState::operator=(LuaeState &&state) { m_state = std::move(state.m_state); + initRegistry(); + return *this; } @@ -232,6 +254,104 @@ } /* -------------------------------------------------------- + * LuaeClass + * -------------------------------------------------------- */ + +void LuaeClass::create(lua_State *L, const Def &def) +{ + LUAE_STACK_CHECKBEGIN(L); + + luaL_newmetatable(L, def.name.c_str()); + + // Metamethods + if (def.metamethods.size() > 0) { + for (auto m : def.metamethods) { + lua_pushcfunction(L, m.func); + lua_setfield(L, -2, m.name); + } + } + + // Methods + if (def.methods.size() > 0) { + lua_createtable(L, 0, 0); + + for (auto m : def.methods) { + lua_pushcfunction(L, m.func); + lua_setfield(L, -2, m.name); + } + + lua_setfield(L, -2, "__index"); + } + + lua_pop(L, 1); + + LUAE_STACK_CHECKEQUALS(L); +} + +/* -------------------------------------------------------- + * LuaeEnum + * -------------------------------------------------------- */ + +void LuaeEnum::create(lua_State *L, + const Def &def, + int index, + const std::string &name) +{ + LUAE_STACK_CHECKBEGIN(L); + + lua_createtable(L, 0, def.size()); + for (auto p : def) { + lua_pushinteger(L, p.second); + lua_setfield(L, -2, p.first.c_str()); + } + + if (index < 0) + -- index; + + lua_setfield(L, index, name.c_str()); + + LUAE_STACK_CHECKEQUALS(L); +} + +void LuaeEnum::push(lua_State *L, const Def &def, int value) +{ + LUAE_STACK_CHECKBEGIN(L); + lua_createtable(L, 0, 0); + + for (auto p : def) { + if (value & p.second) { + lua_pushinteger(L, p.second); + lua_setfield(L, -2, p.first.c_str()); + } + } + + LUAE_STACK_CHECKEND(L, - 1); +} + +int LuaeEnum::get(lua_State *L, int index) +{ + int value = 0; + + LUAE_STACK_CHECKBEGIN(L); + luaL_checktype(L, index, LUA_TTABLE); + + if (index < 0) + -- index; + + lua_pushnil(L); + while (lua_next(L, index)) { + if (lua_type(L, -1) == LUA_TNUMBER) + value |= lua_tointeger(L, -1); + + lua_pop(L, 1); + } + + LUAE_STACK_CHECKEQUALS(L); + + return value; +} + +/* -------------------------------------------------------- * Luae * -------------------------------------------------------- */ @@ -258,15 +378,7 @@ LUAE_STACK_CHECKEQUALS(L); } -void Luae::initRegistry(lua_State *L) -{ - lua_createtable(L, 0, 0); - lua_createtable(L, 0, 1); - lua_pushstring(L, "v"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); - lua_setfield(L, LUA_REGISTRYINDEX, "refs"); -} +} // !malikania void *operator new(size_t size, lua_State *L) {
--- a/C++/Luae.h Sat Nov 30 09:58:48 2013 +0100 +++ b/C++/Luae.h Sat Dec 28 11:16:11 2013 +0100 @@ -1,5 +1,5 @@ /* - * Lua.h -- Lua helpers and such + * Luae.h -- Lua helpers and such * * Copyright (c) 2013 David Demelier <markand@malikania.fr> * @@ -23,10 +23,13 @@ #include <functional> #include <memory> #include <string> +#include <unordered_map> #include <vector> #include <lua.hpp> +namespace malikania { + /* {{{ LuaeState */ /** @@ -49,6 +52,8 @@ Ptr m_state; + void initRegistry(); + public: LuaeState(const LuaeState &) = delete; LuaeState &operator=(const LuaeState &) = delete; @@ -223,6 +228,137 @@ /* }}} */ +/* {{{ LuaeClass */ + +/** + * @class LuaeClass + * @brief Support for object oriented programming between C++ and Lua + * + * This class provides functions for passing and retrieving objects from C++ and + * Lua. + */ +class LuaeClass { +public: + using Methods = std::vector<luaL_Reg>; + + template <typename T> + using Ptr = std::shared_ptr<T>; + + struct Def { + std::string name; //! metatable name + Methods methods; //! methods + Methods metamethods; //! metamethods + const Def *parent; //! optional parent class + }; + + static void create(lua_State *L, const Def &def); + + /** + * Push a shared object to Lua, it also push it to the "refs" + * table with __mode = "v". That is if we need to push the object + * again we use the same reference so Lua get always the same + * userdata and gain the following benefits: + * + * 1. The user can use the userdata as table key + * 2. A performance gain thanks to less allocations + * + * @param L the Lua state + * @param o the object to push + * @param name the object metatable name + */ + template <typename T> + static void push(lua_State *L, Ptr<T> o, const std::string &name) + { + lua_getfield(L, LUA_REGISTRYINDEX, "refs"); + assert(lua_type(L, -1) == LUA_TTABLE); + + lua_rawgetp(L, -1, o.get()); + + if (lua_type(L, -1) == LUA_TNIL) { + lua_pop(L, 1); + + new (L, name.c_str()) std::shared_ptr<T>(o); + + lua_pushvalue(L, -1); + lua_rawsetp(L, -3, o.get()); + } + + lua_replace(L, -2); + } + + /** + * Get an object from Lua that was previously push with pushShared. + * + * @param L the Lua state + * @param index the object index + * @param meta the object metatable name + * @return the object + */ + template <typename T> + static Ptr<T> get(lua_State *L, int index, const char *meta) + { + Ptr<T> *ptr = static_cast<Ptr<T> *>(luaL_checkudata(L, index, meta)); + + return *ptr; + } +}; + +/* }}} */ + +/* {{{ LuaeEnum */ + +/** + * @class LuaeEnum + * @brief Binds and get enumeration + * + * Bind C/C++ enumeration as tables to Lua in the key-value form. Two + * methods push and get are also available for enumeration flags. + */ +class LuaeEnum { +public: + /** + * The definition of the enumeration + */ + using Def = std::unordered_map<std::string, int>; + + /** + * Bind the enumeration as a table into an existing table. + * + * @param L the Lua state + * @param def the definition + * @param index the table index + * @param name the field name to store the enumeration + */ + static void create(lua_State *L, + const Def &def, + int index, + const std::string &name); + + /** + * Push the value enumeration as a table to Lua. This is used + * as the OR replacement. + * + * @param L the Lua state + * @param def the definition + * @param value the value + */ + static void push(lua_State *L, const Def &def, int value); + + /** + * Get the enumeration from Lua. Raises an error if the + * value at the index is not a table. + * + * This is used as the OR replacement. + * + * @param L the Lua state + * @param index the value index + * @return the value + */ + static int get(lua_State *L, int index); +}; + +/* }}} */ + /* {{{ Luae */ /** @@ -262,66 +398,6 @@ bool global); /** - * Initialize the registry for shared objects. - * - * @param L the Lua state - */ - static void initRegistry(lua_State *L); - - /** - * Push a shared object to Lua, it also push it to the "refs" - * table with __mode = "v". That is if we need to push the object - * again we use the same reference so Lua get always the same - * userdata and gain the following benefits: - * - * 1. The user can use the userdata as table key - * 2. A performance gain thanks to less allocations - * - * @param L the Lua state - * @param o the object to push - * @param name the object metatable name - */ - template <typename T> - static void pushShared(lua_State *L, - std::shared_ptr<T> o, - const std::string &name) - { - lua_getfield(L, LUA_REGISTRYINDEX, "refs"); - assert(lua_type(L, -1) == LUA_TTABLE); - - lua_rawgetp(L, -1, o.get()); - - if (lua_type(L, -1) == LUA_TNIL) { - lua_pop(L, 1); - - new (L, name.c_str()) std::shared_ptr<T>(o); - - lua_pushvalue(L, -1); - lua_rawsetp(L, -3, o.get()); - } - - lua_replace(L, -2); - } - - /** - * Get an object from Lua that was previously push with pushShared. - * - * @param L the Lua state - * @param index the object index - * @param meta the object metatable name - * @return the object - */ - template <typename T> - static std::shared_ptr<T> getShared(lua_State *L, int index, const char *meta) - { - using Ptr = std::shared_ptr<T>; - - Ptr *ptr = static_cast<Ptr *>(luaL_checkudata(L, index, meta)); - - return *ptr; - } - - /** * Convert a new placement made object, without testing if its a real * object. * @@ -334,20 +410,6 @@ { return reinterpret_cast<T>(lua_touserdata(L, idx)); } - - /** - * Convert a class created with new placement. - * - * @param L the Lua state - * @param idx the value index - * @param metaname the metatable name - * @return the converted object - */ - template <typename T> - static T toType(lua_State *L, int idx, const char *metaname) - { - return reinterpret_cast<T>(luaL_checkudata(L, idx, metaname)); - } }; /* }}} */ @@ -371,6 +433,8 @@ #endif +} // !malikania + void *operator new(size_t size, lua_State *L); void *operator new(size_t size, lua_State *L, const char *metaname);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/XmlParser.cpp Sat Dec 28 11:16:11 2013 +0100 @@ -0,0 +1,79 @@ +/* + * XmlParser.h -- C++ wrapper around libexpat + * + * Copyright (c) 2013 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <fstream> + +#include "XmlParser.h" + +namespace { + +void xmlStartElementHandler(XmlParser *p, const XML_Char *name, const XML_Char **attrs) +{ + XmlParser::Attrs attributes; + + for (const XML_Char **p = attrs; *p != NULL; p += 2) + attributes[p[0]] = p[1]; + + p->startElementHandler(name, attributes); +} + +void xmlEndElementHandler(XmlParser *p, const XML_Char *name) +{ + p->endElementHandler(name); +} + +void xmlCharacterDataHandler(XmlParser *p, const XML_Char *data, int length) +{ + std::string str; + + str.reserve(length); + str.insert(0, data, length); + + p->characterDataHandler(str); +} + +} + +XmlParser::XmlParser(const std::string &path) + : m_path(path) +{ + auto p = XML_ParserCreate(nullptr); + + XML_SetUserData(p, this); + XML_SetElementHandler(p, + reinterpret_cast<XML_StartElementHandler>(xmlStartElementHandler), + reinterpret_cast<XML_EndElementHandler>(xmlEndElementHandler)); + XML_SetCharacterDataHandler(p, + reinterpret_cast<XML_CharacterDataHandler>(xmlCharacterDataHandler)); + + m_handle = Ptr(new XML_Parser(p)); +} + +void XmlParser::open() +{ + std::string line; + std::ifstream ifile(m_path); + int done = 0; + + if (ifile.is_open() && !done) { + while (std::getline(ifile, line)) { + done = ifile.eof(); + XML_Parse(*m_handle.get(), line.c_str(), line.length(), done); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/XmlParser.h Sat Dec 28 11:16:11 2013 +0100 @@ -0,0 +1,90 @@ +/* + * XmlParser.h -- C++ wrapper around libexpat + * + * Copyright (c) 2013 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _XML_PARSER_H_ +#define _XML_PARSER_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include <expat.h> + +/** + * @class XmlParser + * @brief Wrapper around libexpat + */ +class XmlParser { +public: + using Attrs = std::unordered_map<std::string, std::string>; + +private: + struct Deleter { + void operator()(XML_Parser *p) + { + XML_ParserFree(*p); + + delete p; + } + }; + + using Ptr = std::unique_ptr<XML_Parser, Deleter>; + + Ptr m_handle; + std::string m_path; + +public: + /** + * Constructor and open a file. + * + * @param path the path + */ + XmlParser(const std::string &path); + + /** + * Read the file. + */ + void open(); + + /** + * Handler when a tag occurs. + * + * @param name the tag name + * @param attributes the optional attributes + */ + virtual void startElementHandler(const std::string &name, + const Attrs &attributes) { } + + /** + * Handler when a tag ends. + * + * @param name the tag name + */ + virtual void endElementHandler(const std::string &name) { } + + /** + * Handler when data occurs. The data may have leading and + * trailing spaces. + * + * @param data the data + */ + virtual void characterDataHandler(const std::string &data) { } +}; + + +#endif // !_XML_PARSER_H_