«   2020/07   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Tags
more
Archives
Today
0
Total
1,731
관리 메뉴

넓적부리의 작업실

If~Else, Begin~End 명령어 구현 (1) 본문

CSharp/PEBakery

If~Else, Begin~End 명령어 구현 (1)

넓적부리 2016. 9. 18. 21:45

WinBuilder 스크립트는 문법 체계가 고급 언어라기보다는 어셈블리에 가까운 편입니다.

따라서, PEBkaery는 현재 명령어를 한 줄씩 읽어서 처리하는 인터프리터 구조로 구현되었습니다.

MIPS 어셈블리 에뮬레이터인 Spim과 같은, 일종의 CPU VM과 같은 구조입니다.


이 상황에서 가장 두통을 안겨주는 문법적 요소는 바로 분기문입니다. 한 줄 안에서 처리가 힘들거든요.

Run/Exec, If~Else, Begin~End 등이 여기에 해당합니다.


Run/Exec 명령은 스크립트의 한 섹션에 인자를 주어 실행하는 명령이니, 여기서 섹션을 프로그래밍 언어의 함수라고 간주할 수 있습니다. 그래서 x86 CPU가 함수 호출을 처리하는 방법 (CALL 명령어의 행동)을 흉내내 보았습니다.


PEBakery 내부에 "다음에 처리할 명령어의 주소"를 저장하는 변수 (Program Counter)를 만듭니다.

일반적인 명령어 수행 후에는 현재 명령어와 인접한 다음 명령어의 주소를 명시해 놓습니다.

Run/Exec 명령어가 호출되는 경우, 현재 명어의 주소를 returnAddress 스택에 저장하고, 호출된 섹션의 주소 다음에 처리할 명령어 주소로 명기하면 PEBakery 엔진이 새 명령어를 읽어오면서 자연스럽게 지정된 섹션에 진입하게 됩니다.


그런데 WinBuilder 스크립트의 분기문, 특히 If~Else와, Begin~End 명령어는 유별나게도 고급 언어의 성격을 가지고 있습니다.
FileCopy, Run 등 대부분의 명령어들은 한 줄만으로 모든 처리가 가능하지만, If~Else / Begin~End는 여러 줄에 걸쳐 맥락을 검사해야 하고, 그 난이도도 상당합니다. 게다가, If와 Begin~End 명령어가 중첩될 수 있다는 걸 생각하면 매우 골치가 아파지지요.


WinBuilder의 If 문은 특정 조건이 충족될 시 특정 명령어를 수행합니다.

이 때, 특정 명령어에는 단순한 명령어 하나가 올 수 있지만, 다른 If이 중첩될수도 있고 Begin~End 문으로 여러 줄에 걸쳐서 명령어를 실행할 수도 있습니다. Else 문은 If 문의 조건이 충족되지 않을 경우 실행되며, 조건이 없는 것을 제외하면 If와 동일합니다. Else는 If문이나 If~Begin~End 다음에만 올 수 있다는 것도 특징입니다.


이러한 조건들로 인해 If~Else 처리를 위해선 Begin~End를 반드시 처리할 수 있어야 합니다.

그리고 Begin~End 명령어는 중첩될 수 있으므로, 이 Begin과 매칭되는 End가 어디에 오는지 찾는 작업이 필요합니다.


백문이 불여일견, 직접 봅시다.

다음은 Win10PESE의 Korean IME 스크립트 중 일부입니다.


Else,Begin
  If,Not,ExistDir,%ProvideFiles%,DirMake,%ProvideFiles%
  If,Not,ExistFile,%ProvideFiles%\gulim.ttc,Run,%ScriptFile%,Process-Gulim-GetFromHost
  If,ExistFile,%ProvideFiles%\gulim.ttc,Begin
    Set,%IsGulimExist%,True
    FileCopy,%ProvideFiles%\gulim.ttc,%target_win%\Fonts\gulim.ttc
  End
  If,Not,ExistFile,%target_win%\Fonts\gulim.ttc,Begin
    Message,"The Windows source you are using does not have gulim.ttc.#$xAs a fallback, D2Coding option will be activated.",Warning,10
    Set,%UseD2CodingConsole%,True
  End
End


여기서, 첫번째 줄의 Else 뒤에 오는 Begin은 FileCopy 다음에 오는 End와 매칭되지 않고 맨 마지막 End와 매칭됩니다.

그냥 무턱대고 처음 만나는 End를 Begin의 끝으로 간주했다가는 중첩 Begin~End 문 때문에 오류가 발생하는 거죠.


문제는, 이 Begin과 매칭되는 End가 어디에 오는지 찾는 작업이 현 모델 하에서는 매우 골치아프고 복잡한 작업이라는 점입니다. 이번 추석 연휴 때 이걸 구현하기 위해 끙끙거렸는데, 그럭저럭 돌아는 가지만 유지보수가 전혀 불가능한 괴상한 코드 덩어리가 남더군요.


그러다가, 이 생각이 문득 들었습니다.

"현 엔진에서 고급언어스러운 분기문을 처리하지 못한다면, 엔진을 엉망으로 뜯어고치는 대신 분기문 문법을 어셈블리어스럽게 바꾸면 되지 않나?"


이미 존재하는 문법을 바꿀 순 없으니, 컴파일러를 내장해서 If 뒤에 반드시 Goto 명령어가 오게 번역해버리면 한 줄 안에 처리가 가능해지죠. 구조가 간단해지니 당연히 성능도 올라갈 겁니다.


컴파일러를 구현하는 과정은 2편에서 포스팅하겠습니다.


0 Comments
댓글쓰기 폼
Prev 1 2 3 4 5 6 7 8 Next