U E D R S I H C RSS
ID
Password
Join
시내를 보고 대양이 존재함을 믿는 것, 그것이 신념이다. ―W.A.W.

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에서 제안한 아웃풋이 있는데,
{ 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 

(계속)

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2010-10-28 12:42:52
Processing time 0.3586 sec