Skip to content

Commit bdb3faa

Browse files
committed
7장 컴파일러 - 핵심 컴파일 과정 작성중인 부분까지 커밋
1 parent 329b05f commit bdb3faa

File tree

4 files changed

+162
-1
lines changed

4 files changed

+162
-1
lines changed

book/docs/7_0_compiler.md

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,167 @@
11
# 컴파일러
2+
![컴파일 과정](../images/7_compiler/00_compile_process.png)
23
파이썬 코드를 파싱하면 연산자, 함수, 클래스, 이름 공간을 포함하는 AST가 생성
34
AST를 CPU 명령으로 바꾸는 것이 **컴파일러**
45

5-
![컴파일 과정](../images/7_compiler/00_compile_process.png)
6+
## 컴파일 과정
7+
![컴파일 과정2](../images/7_compiler/01_compile_process2.png)
8+
컴파일 과정을 그림으로 표현
9+
앞장에서 본 렉싱과 파서를 통하여 생성한 AST를 컴파일러를 통하여 CFG(Control Flow Graph)로 변환
10+
어셈블러를 통하여 CFG의 노드를 순서대로 **바이트 코드로 변환****실행**
11+
12+
### 컴파일과 관련된 소스
13+
14+
| 파일 | 목적 |
15+
|-------------------|-----------------|
16+
| Python/compile.c | 컴파일러의 구현 |
17+
| Include/compile.h | 컴파일러 API와 타입 정의 |
18+
19+
PyAST_CompileObject() CPython 컴파일러의 주 진입점
20+
21+
![컴파일 과정2](../images/7_compiler/02_compile_status.png)
22+
**컴파일러 상태**는 심벌 테이블을 담는 타입
23+
**심벌 테이블**은 변수 이름을 포함하고 추가로 하위 심벌 테이블을 포함할 수 있음
24+
**컴파일러 타입**에는 컴파일러 유닛도 포함
25+
**컴파일러 유닛**은 이름, 변수 이름, 상수, 셀(cell) 변수들을 포함
26+
컴파일러 유닛은 **기본 프레임 블록**을 포함
27+
기본 프레임 블록은 **바이트코드 명령**을 포함
28+
29+
## 컴파일러 인스턴스 생성
30+
컴파일러를 실행하기 앞서 전역 컴파일러 상태가 생성
31+
compiler 타입은 컴파일러 플래그, 스택, PyArena 등 컴파일러를 위한 다양한 프로퍼티를 포함
32+
컴파일러 상태는 심벌 테이블 등의 다른 구조체도 포함
33+
```cpp
34+
struct compiler {
35+
PyObject *c_filename;
36+
struct symtable *c_st;
37+
PyFutureFeatures *c_future; /* pointer to module's __future__ */
38+
PyCompilerFlags *c_flags;
39+
40+
int c_optimize; /* optimization level */
41+
int c_interactive; /* true if in interactive mode */
42+
int c_nestlevel;
43+
int c_do_not_emit_bytecode; /* The compiler won't emit any bytecode
44+
if this value is different from zero.
45+
This can be used to temporarily visit
46+
nodes without emitting bytecode to
47+
check only errors. */
48+
49+
PyObject *c_const_cache; /* Python dict holding all constants,
50+
including names tuple */
51+
struct compiler_unit *u; /* compiler state for current block */
52+
PyObject *c_stack; /* Python list holding compiler_unit ptrs */
53+
PyArena *c_arena; /* pointer to memory allocation arena */
54+
};
55+
```
56+
PyAST_CompileObject()가 다음과 같이 컴파일러 상태 초기화
57+
58+
모듈에 문서화 문자열(\_\_doc\_\_)이 없다면 빈 문서화 문자열 생성
59+
\_\_annotations\_\_ 프로퍼티도 동일 작업 수행
60+
스택 트레이스 및 예외 처리에 필요한 파일 이름을 컴파일러 상태에 설정
61+
인터프리터가 사용한 메모리 할당 아레나(arena)를 컴파일러의 메모리 할당 아레나로 설정 (메모리 할당자는 9장 메모리 관리 참고)
62+
코드 컴파일 전 퓨처 플래그들을 설정
63+
64+
## 퓨처 플래그와 컴파일러 플래그
65+
컴파일러 기능 설정
66+
1. 환경 변수 명령줄 플래그를 담는 구성 상태
67+
2. 모듈 소스 코드의 \_\_future\_\_ 문
68+
69+
**퓨처 플래그**는 Python 2와 3 간 이식 지원을 위해 사용
70+
71+
**컴파일러 플래그**는 실행 환경에 의존적, 실행 방식을 변경 할 수 있음
72+
예시로 -O 플래그는 디버그 용도의 assert 문을 비활성화 하는 최적화를 진행
73+
PYTHONOPTIMIZE=1 환경 변수로도 활성화 가능
74+
75+
## 심벌 테이블
76+
코드 컴파일 전 PySymtable_BuildObject() API로 심벌 테이블 생성
77+
전역, 지역 등 이름 공간 목록을 컴파일러에 제공
78+
컴파일러는 심벌 테이블에서 얻은 이름 공간에서 스코프를 결정, 참조를 실행
79+
80+
```cpp
81+
struct symtable {
82+
PyObject *st_filename; /* name of file being compiled,
83+
decoded from the filesystem encoding */
84+
struct _symtable_entry *st_cur; /* current symbol table entry */
85+
struct _symtable_entry *st_top; /* symbol table entry for module */
86+
PyObject *st_blocks; /* dict: map AST node addresses
87+
* to symbol table entries */
88+
PyObject *st_stack; /* list: stack of namespace info */
89+
PyObject *st_global; /* borrowed ref to st_top->ste_symbols */
90+
int st_nblocks; /* number of blocks used. kept for
91+
consistency with the corresponding
92+
compiler structure */
93+
PyObject *st_private; /* name of current class or NULL */
94+
PyFutureFeatures *st_future; /* module's future features that affect
95+
the symbol table */
96+
int recursion_depth; /* current recursion depth */
97+
int recursion_limit; /* recursion limit */
98+
};
99+
```
100+
컴파일러당 하나의 symtable 인스턴스만 사용, 공간 관리 중요
101+
두 클래스가 동일한 이름의 메서드를 가지고 있을 경우 모듈에서 어떤 메서드를 호출할지 정해주는 것
102+
하위 스코프의 변수를 상위 스코프에서 사용하지 못하게 하는 것
103+
위 두 가지가 symtable의 역할
104+
105+
### 심벌 테이블 구현
106+
symtable.c 에서 찾을 수 있다
107+
주 인터페이스는 PySymtable_BuildObject()
108+
mod_ty 타입(Module, Interactive, Expression, FunctionType)에 따라 모듈 내의 문장을 순회
109+
mod_ty 타입인 AST의 노드의 분기를 재귀적으로 탐색하며 symtable의 엔트리로 추가
110+
```cpp
111+
struct symtable *
112+
PySymtable_BuildObject(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
113+
{
114+
struct symtable *st = symtable_new();
115+
asdl_seq *seq;
116+
int i;
117+
PyThreadState *tstate;
118+
int recursion_limit = Py_GetRecursionLimit();
119+
int starting_recursion_depth;
120+
...
121+
st->st_top = st->st_cur;
122+
switch (mod->kind) {
123+
case Module_kind:
124+
seq = mod->v.Module.body;
125+
for (i = 0; i < asdl_seq_LEN(seq); i++)
126+
if (!symtable_visit_stmt(st,
127+
(stmt_ty)asdl_seq_GET(seq, i)))
128+
goto error;
129+
break;
130+
case Expression_kind:
131+
if (!symtable_visit_expr(st, mod->v.Expression.body))
132+
goto error;
133+
break;
134+
case Interactive_kind:
135+
seq = mod->v.Interactive.body;
136+
for (i = 0; i < asdl_seq_LEN(seq); i++)
137+
if (!symtable_visit_stmt(st,
138+
(stmt_ty)asdl_seq_GET(seq, i)))
139+
goto error;
140+
break;
141+
case FunctionType_kind:
142+
PyErr_SetString(PyExc_RuntimeError,
143+
"this compiler does not handle FunctionTypes");
144+
goto error;
145+
}
146+
...
147+
```
148+
모듈의 각 문을 순회, symtable_visit_stmt() 를 호출
149+
Parser → Python.asdl에서 정의한 모든문 타입에 대한 case를 가지고 있는 거대한 swich 문
150+
151+
각 문 타입마다 심벌을 처리하는 함수 존재
152+
함수 정의문 타입을 처리하는 함수는 다음 처리를 위한 로직이 있다
153+
- 현재 재귀 깊이가 제귀 제한을 넘지 않았는지 검사
154+
- 함수가 함수 객체로 넘겨지거나 호출될 수 있도록 함수 이름 심벌 테이블에 추가
155+
- 기본 인자 중 리터럴이 아닌 인자는 심벌 테이블에서 찾음
156+
- 타입 힌트 처리
157+
- 함수 데코레이터 처리
158+
159+
마지막으로 symtable_enter_block()이 함수 블록을 방문해 인자와 함수 본문 처리
160+
161+
이렇게 생성된 심벌 테이블은 컴파일러로 넘김
162+
163+
## 핵심 컴파일 과정
164+
PyAST_CompileObject()에 컴파일러 상태와 symtable, AST로 파싱된 모듈이 준비되면 컴파일 시작
165+
1. 컴파일러 상태, 심벌 테이블, AST를 제어 흐름 그래프로 변환
166+
2. 논리 오류나 코드 오류를 탐지, 실행 단계를 런타임 예외로부터 보호
6167
17.2 KB
Loading
6.62 KB
Loading
18.6 KB
Loading

0 commit comments

Comments
 (0)