출처: 컴퓨터 사이언스 부트캠프 with 파이썬 (양태환, 길벗)
컴파일러 언어와 인터프리터 언어는 컴파일 타임이 있느냐 없느냐 즉, 소스 코드를 분석하는 시점과 입력 데이터를 받는 시점이 언제인지에 따라 나뉜다.
1. C: 컴파일러 언어 분석
- 소스 코드를 컴파일
- 목적 코드(object code)인 기계어로 된 인스트럭션 생성
- 링커(linker)는 필요한 라이브러리를 가져오고 여러 개의 목적 파일을 함께 묶어 실행 파일(executable file)을 생성
- 소스 코드를 분석하는 컴파일 타임(compile time)과 실제 데이터를 받아 출력하는 런타임(run time)일 분리되어 있음
2. Python: 인터프리터 언어 분석
- 소스 코드를 컴파일해 바이트 코드(byte code)를 생성
- 바이트 코드가 생성된 후에는 PVM(Python Virtual Machine)에서 바이트 코드를 해석하여 프로그램을 실행
- 소스 코드를 분석하는 컴파일 타임이 따로 없고 실행과 동시에 분석을 시작 (소스 코드와 입력 데이터가 같은 시점에 삽입됨)
3. Python: 소스 코드부터 실행까지
3-1. 컴파일러
- 일반적인 컴파일러는 렉서(lexer)와 파서(parser)로 구성
- 소스 코드는 렉서를 거치며 여러 개의 토큰으로 변경됨
- 파서는 토큰을 분석해 분석 트리(parse tree)를 구성
- 코드 생성(code generation): 분석 트리가 만들어지만 이를 이용해 목적 코드가 생성
3.2. Python의 바이트 코드 생성 과정
- 소스 코드 $\rightarrow$ 분석 트리
- 분석 트리 $\rightarrow$ 추상 구문 트리
- 심벌 테이블 생성
- 추상 구문 트리 $\rightarrow$ 바이트 코드
1 |
|
1 |
|
TokenInfo(type=59 (BACKQUOTE), string='utf-8', start=(0, 0), end=(0, 0), line='')
TokenInfo(type=1 (NAME), string='def', start=(1, 0), end=(1, 3), line='def func(a, b):\n')
TokenInfo(type=1 (NAME), string='func', start=(1, 4), end=(1, 8), line='def func(a, b):\n')
----------- 중략 -----------
TokenInfo(type=53 (OP), string=')', start=(8, 7), end=(8, 8), line='print(c)\n')
TokenInfo(type=4 (NEWLINE), string='\n', start=(8, 8), end=(8, 9), line='print(c)\n')
TokenInfo(type=0 (ENDMARKER), string='', start=(9, 0), end=(9, 0), line='')
이렇게 얻어진 토큰으로 분석 트리를 만든 다음, 추상 구문 트리로 변형
1 |
|
3.2.1. 추상 구문 트리
- 추상 구문 트리(Abstract Syntax Tree, AST): 소스 코드의 구조를 나타내는 자료 구조
- 추상 구문 트리를 바탕으로 심벌 테이블, 바이트 코드를 생성할 수 있음
1 |
|
<_ast.Module object at 0x1a0fede828>
<_ast.FunctionDef object at 0x1a0fede898>
<_ast.Assign object at 0x1a0fedea58>
3.2.2 심벌 테이블
- 심벌 테이블(symbol table): 변수나 함수의 이름과 그 속성에 대해 기술해 놓은 테이블
1 |
|
1 |
|
1 |
|
1 |
|
3.2.3. 바이트 코드와 PVM
1 |
|
1 |
|
추가: PVM - CPython 소스 코드 중 ceval.c에 있는 PVM 일부 코드
1 |
|