사용자 도구

사이트 도구


kb:cppidentifierextraction

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:cppidentifierextraction [2014/11/08 14:34] (현재)
줄 1: 줄 1:
 +====== C++ 상수값 뽑아내기 ======
 +게임 안의 특정 구성요소를 외부 스크립트로 빼낼 때 약간 골칫거리가 되는 것이 게임 안에서 쓰이는 상수다. 예를 들어 플레이어 인벤토리의 최대 크기라던지,​ 각종 능력치 최대값 같은 것들 말이다. 스크립트 코드를 작성할 때 이런 상수들을 직접 써주는 방법도 있지만, C/C++ 소스에서 자동으로 뽑아내는 것이 좋다. 에러가 발생할 확률도 줄고, 상수들이 변경되었을 때도 쉽게 처리할 수 있기 때문이다.
 +
 +이런 작업 자체는 C/C++로 짜기보다는 정규식 처리가 가능한 스크립트 언어를 이용하는 것이 좋다. 완벽한 파서를 만들면 좋겠지만,​ 딱히 매달릴 필요는 없으며, 원하는 값들만 뽑아낼 수 있을 정도라면,​ 땜빵이라도 괜찮다고 생각한다.
 +
 +
 +====== enum 값 뽑아내기 ======
 +[[Cpp|C++]] 소스에서 열거값을 뽑아내어 [[Lua]] 소스를 만들어내는 스크립트이다. ​
 +
 +===== 파이썬 버전 =====
 +<code python>
 +# -*- coding: euc-kr -*-
 +# @file extract_enums.py
 +# @author excel96
 +# @brief C++ 소스에서 열거값 데이터를 LUA 소스로 뽑아내기 위한 스크립트
 +
 +import re
 +import string
 +
 +# 문자열에서 C++ identifier를 뽑아낸다.
 +# "​this_is_identifier = 0;" --> "​this_is_identifier"​
 +def extract_id(text):​
 +    m = re.search(r"​[a-zA-Z_][a-zA-Z0-9_]*",​ text)
 +    if m != None:
 +        return m.group(0)
 +    return ""​
 +    ​
 +# 문자열에서 코멘트 문자열을 뽑아낸다. ​
 +# 원래 라인 주석은 "//"​ 뒤쪽의 모든 문자열이지만,​
 +# 현재 소스의 특수성(doxygen)을 고려하여 "///<"​에 대한 처리도 집어넣었다.
 +# "// this is comment"​ --> "this is comment"​
 +# "///<​ this is comment"​ --> "this is comment"​
 +def extract_comment(text):​
 +    m = re.search(r"​(//​[/<​]*)(.*)",​ text)
 +    if m != None:
 +        return m.group(2).strip()
 +    return ""​
 +
 +# 주어진 N개의 파일들에서 열거값을 뽑아내어,​
 +# 하나의 출력 파일에다 그 값들을 기록한다.
 +def generate(input_filenames,​ output_filename):​
 +    # 출력 파일을 연다.
 +    output = file(output_filename,​ "​w"​)
 +    ​
 +    # 입력 파일 목록 모두를 열어서 열거값을 출력 파일에다 기록한다.
 +    for filename in input_filenames:​
 +        input = file(filename,​ "​r"​)
 +        bEnum = 0 # 현재 열거값 내부를 처리 중인가?
 +        count = 0 # 열거값 처리 중이라면 현재 열거값의 실제 값은?
 +        ​
 +        for line in input.readlines():​
 +            # 열거값 처리 중이라면...
 +            if bEnum:
 +                # 열거값 구문의 끝에 도달했는지 체크한다.
 +                if re.search("​^};​$",​ line) != None:
 +                    output.write("​\n"​)
 +                    bEnum = 0
 +                # 열거값 구문의 끝이 아니라면,​ 열거값과 그 값을 기록한다.
 +                else:
 +                    id = extract_id(line)
 +                    if len(id) > 0:
 +                        text = id + " = " + str(count)
 +                        ​
 +                        # 열거값 뒤쪽에 붙은 주석을 처리한다.
 +                        comment = extract_comment(line)
 +                        if len(comment) > 0:
 +                            text = text + " -- " + comment
 +                            ​
 +                        output.write(text + "​\n"​)
 +                        count = count + 1
 +            # 열거값 구문이 새로 시작되는지 체크한다.
 +            elif re.search(r"​(^enum)([\t ]+[a-zA-Z_][a-zA-Z0-9_]*)*",​ line) != None:
 +                bEnum = 1
 +                count = 0
 +            ​
 +        input.close()
 +    output.close()
 +
 +# 해당하는 파일의 들여쓰기를 예쁘게(-_-) 만든다.
 +def decorate(filename):​
 +    f = file(filename,​ "​r"​)
 +
 +    originals ​     = f.readlines()
 +    seperator ​     = "​="​
 +    comment ​       = "//"​
 +    lhs_max_length = 0
 +    ​
 +    for line in originals:
 +        pos = string.find(line,​ seperator)
 +        if pos != -1:
 +            lhs = line[:​pos].strip()
 +            rhs = line[pos+1:​].strip()
 +            lhs_max_length = max(lhs_max_length,​ len(lhs))
 +        ​
 +    f.close()
 +    f = file(filename,​ "​w"​)
 +        ​
 +    for line in originals:
 +        pos = string.find(line,​ seperator)
 +        text = line
 +        if pos != -1:
 +            lhs = line[:​pos].strip()
 +            rhs = line[pos+1:​].strip()
 +            needed_space = lhs_max_length - len(lhs);
 +            for i in range(0, needed_space):​
 +            lhs = lhs + " ";
 +            text = lhs + " " + seperator + " " + rhs + "​\n"​
 +            ​
 +        f.write(text)
 +
 +    f.close()
 +
 +input_files = [
 +    "​D:​\\Project\\Binary\\Shared\\_NetLib\\TypesSkill.h",​
 +    "​D:​\\Project\\Binary\\Shared\\_NetLib\\TypesCreature.h"​
 +]
 +        ​
 +generate(input_files,​ "​test.lua"​)
 +decorate("​test.lua"​)
 +</​code>​
 +
 +===== 루아 버전 =====
 +<code lua>
 +--[[
 +@file extract_enums.lua
 +@author excel96
 +@brief C++ 소스에서 열거값 데이터를 LUA 소스로 뽑아내기 위한 스크립트
 +--]]
 +
 +-- 문자열에서 앞뒷쪽의 빈 공간을 날린다.
 +function strip(text)
 +    return string.gsub(text,​ "^[ \t\r\n]+|[ \t\r\n]+$",​ ""​)
 +end
 +
 +-- 문자열에서 C++ identifier를 뽑아낸다.
 +-- "​this_is_identifier = 0;" --> "​this_is_identifier"​
 +function extract_id(text)
 +    if text == nil then return ""​ end
 +    local b, e = string.find(text,​ "​[a-zA-Z_][a-zA-Z0-9_]*"​)
 +    if b ~= nil then return strip(string.sub(text,​ b, e)) end
 +    return ""​
 +end
 +    ​
 +-- 문자열에서 코멘트 문자열을 뽑아낸다. ​
 +-- 원래 라인 주석은 "//"​ 뒤쪽의 모든 문자열이지만,​
 +-- 현재 소스의 특수성(doxygen)을 고려하여 "///<"​에 대한 처리도 집어넣었다.
 +-- "// this is comment"​ --> "this is comment"​
 +-- "///<​ this is comment"​ --> "this is comment"​
 +function extract_comment(text)
 +    local _, _, m1, m2 = string.find(text,​ "​(//​[/<​]*)(.*)"​)
 +    if m2 ~= nil then return strip(m2) end
 +    return ""​
 +end
 +
 +-- 주어진 N개의 파일들에서 열거값을 뽑아내어,​
 +-- 하나의 출력 파일에다 그 값들을 기록한다.
 +function generate(input_filenames,​ output_filename)
 +    -- 출력 파일을 연다.
 +    local output = assert(io.open(output_filename,​ "​w"​))
 +    ​
 +    -- 입력 파일 목록 모두를 열어서 열거값을 출력 파일에다 기록한다.
 +    for _, filename in pairs(input_filenames) do
 +    ​
 +        local bEnum = false -- 현재 열거값 내부를 처리 중인가?
 +        local count = 0 -- 열거값 처리 중이라면 현재 열거값의 실제 값은?
 +    ​
 +        for line in io.lines(filename) do
 +            -- 열거값 처리 중이라면...
 +            if bEnum then
 +                -- 열거값 구문의 끝에 도달했는지 체크한다.
 +                if string.find(line,​ "​};"​) ~= nil then
 +                    output:​write("​\n"​)
 +                    bEnum = false
 +                -- 열거값 구문의 끝이 아니라면,​ 열거값과 그 값을 기록한다.
 +                else
 +                    local id = extract_id(line)
 +                    if string.len(id) > 0 then
 +                        local text = id .. " = " .. tostring(count)
 +                        ​
 +                        -- 열거값 뒤쪽에 붙은 주석을 처리한다.
 +                        local comment = extract_comment(line)
 +                        if string.len(comment) > 0 then
 +                            text = text .. " -- " .. comment
 +                        end
 +                        ​
 +                        output:​write(text .. "​\n"​)
 +                        count = count + 1
 +                    end
 +                end
 +            -- 열거값 구문이 새로 시작되는지 체크한다.
 +            elseif string.find(line,​ "​^enum"​) ~= nil then
 +                bEnum = true
 +                count = 0
 +            end
 +        end
 +    end
 +    ​
 +    output:​close()
 +end
 +
 +-- 해당하는 파일의 들여쓰기를 예쁘게(-_-) 만든다.
 +function decorate(filename)
 +    local originals ​     = {}
 +    local seperator ​     = "​="​
 +    local comment ​       = "//"​
 +    local lhs_max_length = 0
 +    ​
 +    for line in io.lines(filename) do
 +        local b, e = string.find(line,​ seperator)
 +        if b ~= nil then
 +            local lhs = strip(string.sub(line,​ 1, b-1))
 +            local rhs = strip(string.sub(line,​ b+1, string.len(line)))
 +            lhs_max_length = math.max(lhs_max_length,​ string.len(lhs))
 +        end
 +        table.insert(originals,​ line)
 +    end
 +    ​
 +    f = assert(io.open(filename,​ "​w"​))
 +        ​
 +    for _, line in pairs(originals) do
 +        local text = line
 +
 +        local b, e = string.find(line,​ seperator)
 +        if b ~= nil then
 +            local lhs = strip(string.sub(line,​ 1, b-1))
 +            local rhs = strip(string.sub(line,​ b+1, string.len(line)))
 +            local needed_space = lhs_max_length - string.len(lhs)
 +            for i=1,​needed_space do
 +            lhs = lhs .. " "
 +            end
 +            text = lhs .. seperator .. rhs .. "​\n"​
 +        else
 +            text = text .. "​\n"​
 +        end
 +            ​
 +        f:​write(text)
 +    end
 +    ​
 +    f:close()
 +end
 +
 +input_files = {
 +    "​D:​\\Project\\Binary\\Shared\\_NetLib\\TypesSkill.h",​
 +    "​D:​\\Project\\Binary\\Shared\\_NetLib\\TypesCreature.h"​
 +}
 +        ​
 +generate(input_files,​ "​test.lua"​)
 +decorate("​test.lua"​)
 +</​code>​
 +
 +===== 루비 버전 =====
 +<code ruby>
 +# \file extract_enums.py
 +# \brief C++ 소스에서 열거값 데이터를 LUA 소스로 뽑아내기 위한 스크립트
 +
 +# 문자열에서 C++ identifier를 뽑아낸다.
 +# "​this_is_identifier = 0;" --> "​this_is_identifier"​
 +def extract_id(text)
 +    match = text.scan(/​[a-zA-Z_][a-zA-Z0-9_]*/​)
 +    return match.length > 0 ? match[0] : nil
 +end
 +
 +# 문자열에서 C++ identifier를 뽑아낸다.
 +# "​this_is_identifier = 0;" --> "​this_is_identifier"​
 +def extract_id_ex(text)
 +    match = text.scan(/​([a-zA-Z_][a-zA-Z0-9_]*)([ \t]*=[ \t]*)/)
 +    return match.length > 0 ? match[0][0] : nil
 +end
 +
 +# 문자열에서 " = value" 구문을 찾은 다음, "​value"​ 부분을 뽑아낸다.
 +# "a = 32" --> "​32"​
 +def extract_value(text)
 +    match = text.scan(/​(=)([ \t\r\n]*)([\"​a-zA-Z0-9_.]*)/​)
 +    return match.length > 0 ? match[0][2] : nil
 +end
 +
 +# 문자열이 숫자로만 이루어졌는지의 여부를 반환한다.
 +def is_digit(text)
 +    return text.scan(/​^[0-9]+$/​).length > 0
 +end
 +
 +# 문자열에서 코멘트 문자열을 뽑아낸다. ​
 +# 원래 라인 주석은 "//"​ 뒤쪽의 모든 문자열이지만,​
 +# 현재 소스의 특수성(doxygen)을 고려하여 "///<"​에 대한 처리도 집어넣었다.
 +# "// this is comment"​ --> "this is comment"​
 +# "///>​ this is comment"​ --> "this is comment"​
 +def extract_comment(text)
 +    match = text.scan(/​(\/​\/​[\/<​]*)(.*)/​)
 +    return match.length > 0 ? match[0][1].strip : nil
 +end
 +
 +# 해당하는 파일의 들여쓰기를 예쁘게(-_-) 만든다.
 +def decorate(filename)
 +    originals ​     = IO.readlines(filename)
 +    seperator ​     = "​="​
 +    comment ​       = "//"​
 +    lhs_max_length = 0
 +    ​
 +    for line in originals
 +        pos = line.index(seperator)
 +        if pos != nil
 +            lhs = line[0, pos].to_s.strip # nil인 경우가 있어서 to_s 처리
 +            rhs = line[pos+1..-1].to_s.strip # nil인 경우가 있어서 to_s 처리
 +            lhs_max_length = [lhs_max_length,​ lhs.length].max
 +        end
 +    end
 +        ​
 +    f = File.open(filename,​ "​w"​)
 +        ​
 +    for line in originals
 +        text = line
 +        pos = line.index(seperator)
 +        if pos != nil
 +            lhs = line[0, pos].to_s.strip # nil인 경우가 있어서 to_s 처리
 +            rhs = line[pos+1..-1].to_s.strip # nil인 경우가 있어서 to_s 처리
 +            lhs = lhs + (" " * (lhs_max_length - lhs.length))
 +            text = lhs.to_s + " " + seperator + " " + rhs.to_s + "​\n"​
 +        end
 +            ​
 +        f.write(text)
 +    end
 +
 +    f.close
 +end
 +
 +# 주어진 N개의 파일들에서 열거값을 뽑아내어,​
 +# 하나의 출력 파일에다 그 값들을 기록한다.
 +def generate(input_filenames,​ output_filename)
 +    # 출력 파일을 연다.
 +    output = File.open(output_filename,​ "​w"​)
 +    ​
 +    # 헤더를 적어준다.
 +    output.write("​--[[\n"​)
 +    output.write("​\\file " + output_filename + "​\n"​)
 +    output.write("​\\author excel96\n"​)
 +    output.write("​\\brief 자동으로 생성된 파일입니다.\n"​)
 +    output.write("​잘못된 것이 있다면 수동으로 고치지 말고 스크립트를 손봐야 합니다.\n"​)
 +    output.write("​--]]\n\n"​)
 +    ​
 +    # 입력 파일 목록 모두를 열어서 열거값을 출력 파일에다 기록한다.
 +    for filename in input_filenames
 +        bEnum = false # 현재 열거값 내부를 처리 중인가?
 +        count = 0 # 열거값 처리 중이라면 현재 열거값의 실제 값은?
 +        ​
 +        # 원본 파일에 대한 정보를 기록한다.
 +        header = "-- EXTRACTED FROM " + filename
 +        output.write("​-"​ * (header.length + 1) + "​\n"​)
 +        output.write(header + "​\n"​)
 +        output.write("​-"​ * (header.length + 1) + "​\n\n"​)
 +        ​
 +        for line in IO.readlines(filename)
 +            # 열거값 처리 중이라면...
 +            if bEnum
 +                # 열거값 구문의 끝에 도달했는지 체크한다.
 +                if line.scan(/​^\s*\}\s*;/​).length > 0
 +                    output.write("​\n"​)
 +                    bEnum = false
 +                # 열거값 구문의 끝이 아니라면,​ 열거값과 그 값을 기록한다.
 +                else
 +                    if id = extract_id(line)
 +                        text = id
 +                        ​
 +                        # 값 문자열이 있는지 확인한다.
 +                        if value = extract_value(line)
 +                            if is_digit(value)
 +                                text = text + " = " + value
 +                                count = value.to_i
 +                            else
 +                                text = text + " = " + value
 +                            end
 +                        else
 +                            text = text + " = " + count.to_s
 +                        end
 +                        ​
 +                        # 뒤쪽에 붙은 주석을 처리한다.
 +                        if comment = extract_comment(line)
 +                            text = text + " -- " + comment ​
 +                        end
 +                            ​
 +                        output.write(text + "​\n"​)
 +                        count = count + 1
 +                    end
 +                end
 +            # 열거값 구문이 새로 시작되는지 체크한다.
 +            elsif line.scan(/​(^[ \t]*enum)([\t ]+[a-zA-Z_][a-zA-Z0-9_]*)*/​).length > 0
 +                bEnum = 1
 +                count = 0
 +            # 상수 구문인지를 체크한다.
 +            elsif line.scan(/​(static|)\s*const\s*(static|)\s*(unsigned|)\s*(int|float)\s*[A-Za-z0-9_]+\s*=\s*[\"​A-Za-z0-9.]+;/​).length > 0
 +                id = extract_id_ex(line)
 +                value = extract_value(line)
 +                text = id + " = " + value
 +                ​
 +                # 뒤쪽에 붙은 주석을 처리한다.
 +                if comment = extract_comment(line)
 +                    text = text + " -- " + comment ​
 +                end
 +                ​
 +                output.write(text + "​\n\n"​)
 +            end
 +        end
 +    end
 +    ​
 +    output.close
 +
 +    # 들여쓰기를 손본다.
 +    decorate(output_filename)
 +end
 +
 +files1 = [
 +    "​D:​\\Project\\XM\\Server\\ZTestAI\\Globals.h",​
 +    "​D:​\\Project\\XM\\Server\\ZTestAI\\NPC.h"​
 +]
 +
 +generate(files1,​ "​Constant.lua"​)
 +</​code>​
 +
 +----
 +  * see also [[Lua]], [[Python]], [[Ruby]]
  
kb/cppidentifierextraction.txt · 마지막으로 수정됨: 2014/11/08 14:34 (바깥 편집)