ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코드를 생성해 파일에 코드를 주입하자
    프로그래밍 2018. 1. 17. 04:11
    반응형

    업무에 노가다를 줄여보고자 남는 시간에 코드를 만드는 코드를 만들었다. 

    PQL이라는 이름을 붙인 쿼리 파일로부터 클라이언트에 삽입되는 코드를 만드는 것이 목적이었다.

    PQL은 이렇게 생겼다.

    select [ID:int], [Level:short], [Name:nvarchar(20)] from Character;

    그냥 sql의 select 쿼리에 저장할 타입을 지정한 것이다. 사실 이것도 내가 만든거라 이름도 내가 붙인거다. 필규 쿼리 랭귀지라고 -_-;

    여튼 저 문법을 통해서 만들어야 할 구조체를 알 수 있는데 위 PQL은 다음과 같은 구조체의 컨테이너에 로드가 될 것임을 알 수 있다.

    struct Character
    {
    int id;
    short level;
    wchar_t name[20];
    };

    그런데 저 코드를 만드는 작업은 일일히 사람이 했어야 했고 나도 매번 만드는 게 귀찮아 잠깐 남는 시간에 짬을 내서 만들었다.

    텍스트를 파싱해서 클래스로 만들고 그 클래스의 정보를 바탕으로 특정 파일들에 데이터 구조체 코드, 데이터 로드 코드를 추가한다.

    현재 회사 프로젝트의 방식은 로드 코드를 한 함수에 나열하고, 모든 구조체가 한 파일 안에 있다보니 코드의 경계를 찾기가 어려워 pql이 바뀜에 따라 기존 구조체를 바꿔준다던지... 로드 방식을 바꾼다던지 하는 처리가 좀 까다롭다.

    때문에 완전 자동화보단 커맨드 라인 파서를 만들어서 --add CashItem 처럼 추가할 테이블을 선택해서 수동으로 만들도록 했다.



    기존 코드와의 호환성 유지를 위해 코드를 만들여 붙여넣기만 해주므로 기존 파일 내용을 유지한 체 원하는 위치에 텍스트만 주입하는 것이 결국 전부였다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;

    namespace PQLAdder
    {
    public class FileInjector
    {
    public static Encoding GetFileEncoding(string path)
    {
    using (StreamReader sr = new StreamReader(path))
    {
    return System.Text.Encoding.GetEncoding(51949);
    //sr.Peek();
    //return sr.CurrentEncoding;
    }
    throw new FileLoadException("file not found");
    }

    static public void InjectText(string path, string indicatorOrNull, string srcText)
    {
    Encoding encoding = GetFileEncoding(path);
    string text = File.ReadAllText(path, encoding);

    int idx = text.Length - 1;
    if(!string.IsNullOrWhiteSpace(indicatorOrNull))
    {
    idx = text.IndexOf(indicatorOrNull);
    if(idx == -1)
    {
    string message = string.Format(
    "indicator not defined. {path:{0}, indicator:{1}}",
    path,
    indicatorOrNull);
    throw new ArgumentException(message);
    }
    }

    // 앞에 들어갈 탭, 스페이스를 계산한다.
    string front = "";
    for (int i = idx; i != 0; --i)
    {
    char c = text[i];
    if (c == '\n')
    {
    idx = (text[i-1] == '\r' ? i - 1 : i);
    break;
    }

    if (c == ' ' || c == '\t')
    front += text[i];
    }

    string[] srcTextLines = srcText.Split(new [] { Environment.NewLine }, StringSplitOptions.None);

    srcText = srcText
    .Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
    .Select(line => Environment.NewLine + front + line)
    .Aggregate((dest, src) => dest + src);

    text = text.Insert(idx, srcText);

    File.WriteAllText(path, text, encoding);
    }
    }
    }

    근데 파일 인코딩을 알아오는 부분이 잘 안 되더라. 이 부분은 다시 알아보면 좋을 듯.


    +++

    MDBModel, ColumnModel 등의 클래스는 최대한 간소화된 기능만 가지도록 하는 게 좋아보인다. 이름, 타입, 테이블 키 정도의 정보만 가진다. 그리고 CodeWriter클래스가 이걸 c# 버전 패킷으로 만드느냐 c++ 버전으로 결정한다.

    CppCodeWriter는 키가 없다면 vector로, 키가 하나면 map으로, 키가 여러개면 별도의 키 구조체를 만들어서 map의 key로 만드는 처리를 해보려고 한다.(pql에 key 지정 추가와 함께)

    반응형
Designed by Tistory.