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_