luawiki에서 본 Roberto Ierusalimschy라는 사람이 만든 코드가 참 인상깊었습니다. 일단 이 코드를 퍼놓으면 아래와 같습니다. (luawiki에 있는 내용 그대로입니다)
function parseargs (s)
local arg = {}
gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
%arg[w] = a
end)
return arg
end
function collect (s)
local stack = {n=0}
local top = {n=0}
tinsert(stack, top)
local ni,c,label,args, empty
local i, j = 1, 1
while 1 do
ni,j,c,label,args, empty = strfind(s, "<(%/?)(%w+)(.-)(%/?)>", j)
if not ni then break end
local text = strsub(s, i, ni-1)
if not strfind(text, "^%s*$") then
tinsert(top, text)
end
if empty == "/" then -- empty element tag
tinsert(top, {n=0, label=label, args=parseargs(args), empty=1})
elseif c == "" then -- start tag
top = {n=0, label=label, args=parseargs(args)}
tinsert(stack, top) -- new level
else -- end tag
local toclose = tremove(stack) -- remove top
top = stack[stack.n]
if stack.n < 1 then
error("nothing to close with "..label)
end
if toclose.label ~= label then
error("trying to close "..toclose.label.." with "..label)
end
tinsert(top, toclose)
end
i = j+1
end
local text = strsub(s, i)
if not strfind(text, "^%s*$") then
tinsert(stack[stack.n], text)
end
if stack.n > 1 then
error("unclosed "..stack[stack.n].label)
end
return stack[1]
end
사용법은 다음과 같다는 군요.
x = collect[[
<methodCall kind="xuxu">
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
]]
아마도 다음과 같이 값이 저장될 겁니다.
{ label="methodCall", args={kind="xuxu"} }
이 방법은 XML문서 전체를 파싱하므로 스트리밍 방식으로 파싱할 수 없다는 단점은 있지만, 게임이나 대부분의 용도로 사용할 때에는 간단한 수준으로 끝날때가 많으니까요, 상관없을 거라 생각합니다. 솔직히 간단한 xml 파싱을 위해 무거운 XML 라이브러리를 쓰는 것보다는 낫겠죠. 어쨌든 위 소스는 몇가지 문제가 있습니다. 우선 몇몇 함수가 현재 버전인 5.0.1과 호환되지 않고, 이중 테이블이므로 다소 무겁다는 단점이 있지요. Yutaka Ueno라는 사람이 luawiki에서 제안한 아웃풋이 있는데,
) 어쨌든 위 소스는 lua5에서 동작하지 않으므로 약간 고쳐봅니다.
{ xml="methodCall", kind="xuxu" }
와 같이 테이블을 구성하면 어떨까라고 써있더군요. (불행히도 링크는 깨져있더군요. 사이트 페쇄...
) 어쨌든 위 소스는 lua5에서 동작하지 않으므로 약간 고쳐봅니다.
function parseargs (s)
local arg = {}
string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a) arg[w] = a end)
return arg
end
function collect (s)
local stack = {n=0}
local top = {n=0}
table.insert(stack, top)
local ni,c,label,args, empty
local i, j = 1, 1
while 1 do
ni,j,c,label,args, empty = string.find(s, "<(%/?)(%w+)(.-)(%/?)>", j)
if not ni then break end
local text = string.sub(s, i, ni-1)
if not string.find(text, "^%s*$") then
table.insert(top, text)
end
if empty == "/" then -- empty element tag
table.insert(top, {n=0, label=label, args=parseargs(args), empty=1})
elseif c == "" then -- start tag
top = {n=0, label=label, args=parseargs(args)}
table.insert(stack, top) -- new level
else -- end tag
local toclose = table.remove(stack) -- remove top
top = stack[stack.n]
if stack.n < 1 then
print("nothing to close with "..label)
end
if toclose.label ~= label then
print("trying to close "..toclose.label.." with "..label)
end
table.insert(top, toclose)
end
i = j+1
end
local text = string.sub(s, i)
if not string.find(text, "^%s*$") then
table.insert(stack[stack.n], text)
end
if stack.n > 1 then
print("unclosed "..stack[stack.n].label)
end
return stack[1]
end
(계속)









![[http]](/wiki/imgs/http.png)
