컴파일러의 구조

사람이 읽기 쉽게 쓰여 있는 소스코드를 기계가 실행할 수 있는 byte 코드로 변환하여 주는 프로그램을 컴파일러라고 한다. 소스를 그에 대응하는 기계어의 집합으로 바꿔주는 일은 언뜻 보기에는 간단해 보이지만, 최적화나 platform dependent한 문제들이 엮이면 쉽지 않은 작업이다. 그래서 복잡한 작업을 최대한 간단하게 만들기 위해 현대의 컴파일러는 다음과 같은 복잡한 구조를 가진다.


Source Code
Lexical Analyzer
Token Stream
Syntax Analyzer
Syntax Tree
Semantic Analyzer
Syntax Tree, Symbol table
Intermediate Code Generator
Intermediate Representation, Symbol Table
Machine-Independent Optimizer
Optimized Intermediate Representation, Symbol Table
Code Generator
Machine Code
Machine-Dependent Optimizer
Optimized Machine Code

위의 구조는 컴파일러 교재로 유명한 dragon book1)이 설명하고 있는 모델이다. 이런 복잡한 구조를 가지는 이유는 dragon book의 표지 그림이 상징적으로 설명해준다.

앞에서 말했듯이 컴파일러가 해야 하는 일은 매우 복잡하해서 이 complexity of compiler design을 상대하기 위해서 고안된 모델이다. 하지만 이는 어디까지나 복잡한 일을 쉽게 하기 위해서다. 어디까지나 일을 쉽게 하기 위한 것인 만큼 이 구조에 얽매일 필요는 없다.

하지만 이보다 단순한 구조로 컴파일러를 만들기는 쉽지 않을 것이다. 그래서 경우에 따라 컴파일러를 새로 만드는 것이 아니라, 기존의 언어에서 제공하는 기능을 이용하여 언어를 확장하여 DSL을 만들거나(Lisp의 macro나, Scala의 implicit 등이 여기에 해당한다.), 기존의 언어로 convert하여 기존의 언어 컴파일러를 이용하는 경우(JVM 위에서 돌아가는 Rhino engine이 대표적이다.)도 있다.


1) LAM, Monica, et al. Compilers: Principles, Techniques, and Tools. 2006. p.5

댓글

이 블로그의 인기 게시물

USB 2.0 케이블의 내부 구조

[C++] enum class - 안전하고 쓰기 쉬운 enum

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Web] SpeechSynthesis - TTS API

[Python] cache 데코레이터로 최적화하기