lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I've been parsing SVG files lately and even though my pure Lua 20-line XML
parser works almost 100% of the time, I'm now looking into expat. Since I
want the complete SVG content as a hierarchy of Lua tables, I don't need
the complexity of luaexpat and I have written a barebones binding.

Here is a sample client:

local parse=require"xml"
local t,e,w=parse(io.read"*a")
print(t,e,w)
if t==nil then return end
local function dump(t)
	print(t.kind,"(")
	for k,v in pairs(t) do
		print(k,v)
		if type(v)=="table" then dump(v) end
	end
	print(t.kind,")")
end
dump(t)

Here is the binding. Note how the Lua stack is used to collect subitems into
subtables. I don't do anything at the moment about text data, that is, data
not inside <...> mainly because I don't really understand the semantics of
this in XML. If all such strings are to be concatened then it should be simple.

I hope this is useful for someone.  All feedback welcome.

/*
* lxml.c
* parse xml data into Lua tables using expat
* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
% Thu Jan 23 13:19:36 BRST 2014
* This code is hereby placed in the public domain.
*/

#include <string.h>
#include <expat.h>

#include "lua.h"
#include "lauxlib.h"

static void XMLCALL start(void *data, const char *kind, const char **attr)
{
	int i;
	lua_State *L=data;
	luaL_checkstack(L,2,"XML too deep");
	i=lua_tointeger(L,-1)+1;
	lua_newtable(L);
	lua_pushvalue(L,-1);
	lua_rawseti(L,-4,i);
	lua_pushinteger(L,i);
	lua_replace(L,-3);
	lua_pushstring(L,kind);
	lua_setfield(L,-2,"kind");
	for (i=0; attr[i]!=NULL; i+=2) {
		lua_pushstring(L,attr[i+1]);
		lua_setfield(L,-2,attr[i]);
	}
	lua_pushinteger(L,0);
}

static void XMLCALL end(void *data, const char *kind)
{
	lua_State *L=data;
	(void)kind;
	lua_pop(L,2);
}

static void XMLCALL text(void *data, const XML_Char *s, int len)
{
}

static int load(lua_State *L)			/** load(s) */
{
	size_t l;
	const char *s=luaL_checklstring(L,1,&l);
	XML_Parser p=XML_ParserCreate(NULL);
	if (p==NULL) {
		lua_pushnil(L);
		lua_pushliteral(L,"cannot create XML parser");
		return 2;
	}
	XML_SetElementHandler(p,start,end);
	XML_SetCharacterDataHandler(p,text);
	XML_SetUserData(p,L);
	lua_newtable(L);
	lua_pushinteger(L,0);
	if (XML_Parse(p,s,l,1)==XML_STATUS_ERROR) {
		lua_pushnil(L);
		lua_pushstring(L,XML_ErrorString(XML_GetErrorCode(p)));
		lua_pushinteger(L,XML_GetCurrentByteIndex(p));
		XML_ParserFree(p);
		return 3;
	}
	XML_ParserFree(p);
	lua_rawgeti(L,-2,1);
	return 1;
}

LUALIB_API int luaopen_xml(lua_State *L)
{
 lua_pushcfunction(L,load);
 return 1;
}