본문으로 건너뛰기
김신건의 로그

프로그래밍 언어론 (Programming Language Theory)

· 수정 · 📖 약 5분 · 2,046자/단어 #plt #compiler #language #foundations #computer-science
Programming Language Theory, 프로그래밍 언어론, PLT, 언어 이론, 컴파일러 이론, 언어 처리기

정의

프로그래밍 언어론 (Programming Language Theory, PLT) 은 프로그래밍 언어를 어떻게 설계하고, 정의하고, 처리하는가 를 다루는 컴퓨터 과학 분야입니다.

크게 두 갈래:

  • 언어 설계 (Language Design): 문법, 의미론, 타입 시스템
  • 언어 구현 (Language Implementation): 어휘 분석, 파싱, 의미 분석, 최적화, 코드 생성

컴퓨터가 소스 코드를 이해하고 실행하는 모든 단계 를 이론적으로 뒷받침합니다.

왜 배워야 하는가

프로그래머는 매일 언어를 사용합니다. 하지만 이 언어가 어떻게 작동하는지 이해하지 않으면:

  • 컴파일 오류 메시지 해석 어려움
  • 타입 시스템의 제약 이유를 모름
  • 왜 특정 코드가 느린지 (컴파일러 최적화 이슈)
  • 새 언어를 배울 때 표면적 문법만 익힘

PLT 를 알면:

  • 디버깅 능력 향상: “왜 이 오류가?”
  • 성능 튜닝: 컴파일러가 뭘 최적화하고 뭘 못 하는지
  • DSL 구축: 자체 언어 만들 수 있음
  • 정적 분석 도구 개발: 린터, 포매터, 리팩터링 도구
  • 언어 선택 판단: Rust vs Go 의 근본 차이

언어 처리기의 큰 그림

컴파일러/인터프리터 는 소스 코드를 다음 파이프라인으로 처리합니다.

flowchart LR
  Src["Source Code<br/>(text)"] --> Lex["Lexer<br/>(Tokenizer)"]
  Lex -->|Tokens| Par["Parser"]
  Par -->|AST| Sem["Semantic<br/>Analyzer"]
  Sem -->|Annotated AST| IR["IR Generator"]
  IR -->|IR| Opt["Optimizer"]
  Opt -->|Optimized IR| CG["Code Generator"]
  CG --> Out["Target Code<br/>(machine / bytecode)"]

각 단계:

  1. Lexer: 문자열을 토큰 (의미 있는 최소 단위) 으로 분해
  2. Parser: 토큰을 AST (Abstract Syntax Tree) 로 조립
  3. Semantic Analyzer: 이름 해석, 타입 검사, scope 결정
  4. IR Generator: 플랫폼 독립 중간 표현
  5. Optimizer: 성능 개선 (죽은 코드 제거, 인라인 등)
  6. Code Generator: 목표 (x86 어셈블리, JVM 바이트코드, WASM 등)

Frontend vs Backend

전통적으로 컴파일러를 두 부분으로 나눔:

  • Frontend: Source → AST → IR (언어 의존)
  • Backend: IR → Target Code (하드웨어 의존)

LLVM 은 이 분리를 극한으로: 각 언어는 LLVM IR 을 생성만 하면, LLVM 이 x86, ARM, WASM, RISC-V 등 다양한 백엔드 지원.

Compiler vs Interpreter

두 접근법이 있습니다.

  • 컴파일러: 소스 → 목표 코드 (실행 파일). 한 번 컴파일, 여러 번 실행.
  • 인터프리터: 소스 → 즉시 실행. AST 를 순회하며 실행.

혼합 (Modern):

  • JIT (Just-In-Time): 실행 중 자주 쓰이는 부분만 컴파일 (V8, HotSpot, LuaJIT, PyPy)
  • AOT (Ahead-Of-Time): 사전 컴파일 (Java native image, Rust)
  • Bytecode + VM: 중간 형태 (JVM, .NET, CPython)

자세한 것은 Interpreter vs Compiler 참조.

주요 학습 주제

이 코스가 다루는 순서:

1. Lexical Analysis (어휘 분석)

문자열을 토큰 으로 분해. 정규 표현식, 유한 오토마타 (DFA/NFA).

Lexical Analysis

2. Parsing (구문 분석)

토큰을 문법 규칙 (grammar) 에 따라 AST 로. Top-down (LL, recursive descent) vs Bottom-up (LR, LALR).

Parsing & Grammars

3. Abstract Syntax Tree

프로그램의 본질 구조 를 트리로. 코드 → AST → 실행/변환/분석.

AST

4. Semantic Analysis

이름 해석, scope, 타입 검사. AST 의 각 노드에 “무엇을 가리키는지” 주석.

Semantic Analysis

5. Type Systems

정적 vs 동적, sound vs unsound, 타입 추론. 언어 안전성의 근간.

Type Systems

6. Interpreter vs Compiler

두 실행 모델의 근본 차이. 각 접근법의 트레이드오프.

Interpreter vs Compiler

7. IR + 최적화 + 코드 생성

컴파일러 백엔드. 3-address code, SSA, dead code elimination, register allocation.

IR, 최적화, 코드 생성

이론 vs 실전

이 코스는 실전 언어 구현 관점. 하지만 배경 이론도 관련:

  • 형식 언어 이론: Chomsky 위계 (Regular, CFG, CSG, Recursive)
  • 오토마타 이론: DFA, NFA, PDA, Turing Machine
  • 람다 계산법: 함수형 언어 근본
  • 타입 이론: Hindley-Milner, System F, Dependent Types
  • 의미론 (Semantics): Operational, Denotational, Axiomatic

이론에 관심 있으면 Pierce, Types and Programming Languages 부터.

형식 언어 계층 (Chomsky Hierarchy)

프로그래밍 언어의 이론적 토대는 Chomsky 위계 다. 계층마다 표현력과 인식 오토마타가 다르다.

flowchart BT
    RE["Type 3: 정규 언어<br/>DFA / NFA / 정규식<br/>어휘 분석 (Lexer) 에 사용"]
    CFL["Type 2: 문맥 자유 언어 CFL<br/>Push-Down Automata<br/>대부분의 프로그래밍 언어 문법"]
    CSL["Type 1: 문맥 의존 언어<br/>선형 유한 오토마타 LBA<br/>자연어 일부"]
    RE0["Type 0: 귀납적 열거 언어<br/>Turing Machine<br/>계산 가능한 모든 언어"]

    RE -->|"부분집합"| CFL
    CFL -->|"부분집합"| CSL
    CSL -->|"부분집합"| RE0

실전 연관:

  • 어휘 분석: 정규 언어 (Type 3). 정규식이나 DFA 로 토큰 인식.
  • 구문 분석: 문맥 자유 언어 (Type 2). LL, LR 파서가 CFG 를 처리.
  • 의미 분석: CFG 를 벗어난 규칙 (같은 타입 변수 선언 등) 은 별도 통과.
  • C++ / Rust 의 일부 구문은 CFG 의 표현 한계를 벗어나 파서가 의미 정보를 참조.

파서 기법 선택

기법방향특징사용 예
LL(1)Top-down예측 파싱, 왼쪽 재귀 금지교육용
Recursive DescentTop-down손으로 작성, 에러 메시지 우수Rust, Go, TypeScript
LALR(1)Bottom-up파서 생성기 (yacc/bison)구형 컴파일러
Pratt ParserTop-down중위 연산자 우선순위 처리 탁월실전 언어 구현
PEG / PackratTop-down백트래킹, 결정적pest, PEGN

현대 컴파일러 (Clang, Rust, Go, TypeScript) 는 대부분 hand-written recursive descent 채택. 에러 메시지 품질과 IDE 지원 (incremental parsing) 때문.

명작 자료

  • Crafting Interpreters (Robert Nystrom, 무료 웹): 실전 인터프리터 구현. 최고 입문서.
  • Dragon Book (Aho et al.): 정통 교과서. 두꺼움.
  • Engineering a Compiler (Cooper & Torczon): 현대 관점.
  • Programming Language Pragmatics (Scott): 언어 설계 관점.
  • TAPL (Pierce): 타입 이론.
  • PLAI (Krishnamurthi, 무료 온라인): 함수형 관점.

실전 언어 만들기

이 코스를 마치면 다음 프로젝트를 스스로 할 수 있어야:

  • 작은 인터프리터 (calculator, Lisp subset)
  • 트리 순회 인터프리터
  • 바이트코드 VM
  • 간단한 컴파일러 (자체 문법 → LLVM IR 또는 C)
  • DSL (config 언어, query 언어)
  • 정적 분석 도구 (린터, 포매터)

컴퓨터 과학 응용

PLT 지식이 쓰이는 곳:

  • 컴파일러 / 트랜스파일러: gcc, clang, Babel, TypeScript, SWC
  • 인터프리터: Python, Ruby, Lua
  • VM: JVM, V8, .NET CLR
  • JIT: V8 TurboFan, LLVM ORC, PyPy, LuaJIT
  • 정적 분석: ESLint, mypy, Clippy, Sonar
  • IDE: Language Server Protocol, 자동완성, 리팩터링
  • DSL: SQL, GraphQL, HTML, CSS, regex
  • 템플릿 엔진: Handlebars, Jinja2
  • 쿼리 옵티마이저: SQL query planner
  • 정규식 엔진
  • 암호화 회로: 형식 검증
  • AI: 코드 생성 LLM 이 AST 활용

실전 도구와 언어 처리기

PLT 지식이 직접 들어가는 실전 도구 분류:

도구 / 프레임워크언어관련 PLT 개념
Babel / SWCJSAST 변환 트랜스파일러
TypeScriptTS타입 시스템, 타입 추론, 타입 체킹
ESLint / ClippyJS / Rust정적 분석, 패턴 매칭 on AST
LLVM다언어IR, 최적화 패스, 코드 생성
Tree-sitter다언어증분 파서, IDE 심볼 추출
ANTLR다언어LL(*) 파서 생성기
CraneliftRustJIT 백엔드 (Wasmtime, Cranelift)

Babel AST 변환 예시

JSX 를 일반 JS 함수 호출로 변환하는 것이 Babel 트랜스파일의 핵심:

// 입력 (JSX)
<Button onClick={fn}>Click</Button>

// AST 변환 후 출력
React.createElement(Button, { onClick: fn }, "Click")

Babel 은:

  1. 소스를 파싱 → AST (parser: @babel/parser)
  2. 플러그인 체인에서 AST visitor 로 노드 변환
  3. 변환된 AST → 코드 생성 (@babel/generator)

이 파이프라인이 PLT 컴파일러 프론트엔드와 완전히 동일한 구조다.

오토마타와 실전 연결

오토마타실전
DFALexer 토큰 인식, 정규식 엔진
PDACFG 파서 (LL, LR)
Turing Machine범용 계산, 정지 문제

Lex (flex) 는 정규식 → DFA 변환으로 빠른 lexer 생성. Yacc/bison 은 LALR(1) 파서 테이블 생성.

함정

WARNING

이론만 배우고 구현 안 하면 잊음. 반드시 작은 언어를 직접 구현해봐야.

CAUTION

Dragon Book 부터 시작하면 좌절. 두껍고 오래됨. Crafting Interpreters 로 시작.

WARNING

파서 생성기 만능 아님. yacc/bison 이 옛 관용, 요즘은 hand-written recursive descent 가 더 흔함 (에러 메시지 우수).

IMPORTANT

최신 언어는 표준 파이프라인을 따르지 않음. Rust 는 여러 pass, Swift 는 별도 SIL, Go 는 매우 단순. 원리 이해 후 각 언어 특성.

관련 위키

이 글의 용어 (10개)
[JavaScript] Bundling (번들링 개요)javascript
정의 JavaScript 번들링 (Bundling) 은 여러 소스 파일 (JS, CSS, 이미지, JSON 등) 을 브라우저나 런타임이 효율적으로 실행할 수 있는 최소한의 결과물…
[PLT] Abstract Syntax Tree (AST)plt
정의 Abstract Syntax Tree (AST, 추상 구문 트리) 는 프로그램의 문법 구조를 트리로 표현 한 자료구조입니다. 파서의 출력물이자, 이후 컴파일러/인터프리터의 …
[PLT] Interpreter vs Compilerplt
정의 프로그래밍 언어의 실행 모델 두 접근: - Compiler (컴파일러): 소스 → 목표 코드 (기계어, 바이트코드) 로 변환. 실행은 나중. - Interpreter (인터…
[PLT] IR, 최적화, 코드 생성 (Compiler Backend)plt
정의 컴파일러의 백엔드 는 semantic 분석까지 마친 AST 를 실제 실행 가능한 목표 코드로 변환합니다. 세 주요 단계: 1. IR (Intermediate Represen…
[PLT] Lexical Analysis (어휘 분석)plt
정의 어휘 분석 (Lexical Analysis) 은 소스 코드 문자 시퀀스 를 의미 있는 최소 단위인 토큰 (Token) 으로 분해하는 과정입니다. 담당 컴포넌트를 Lexer …
[PLT] Parsing & Grammarsplt
정의 Parsing (구문 분석) 은 lexer 의 토큰 스트림 을 언어의 문법 규칙 에 따라 구조화된 트리 (AST, Abstract Syntax Tree) 로 조립하는 과정입…
[PLT] Semantic Analysis (의미 분석)plt
정의 Semantic Analysis (의미 분석) 는 파서가 생성한 AST 를 검사하여 문법적으로 올바르지만 의미상 오류인 경우 를 발견하고, 각 이름 (identifier) …
[PLT] Type Systems (타입 시스템)plt
정의 타입 시스템 (Type System) 은 프로그램의 값과 표현식에 타입 (type) 을 부여하고, 이 타입들이 규칙에 맞게 결합되는지 검증하는 형식 체계입니다. 타입 = 값…
이산수학 (Discrete Mathematics)discrete-math
정의 이산수학 (Discrete Mathematics) 은 이산 (discrete, 셀 수 있는) 대상을 다루는 수학의 분야입니다. 연속 (continuous) 대상 을 다루는 …
TypeScripttypescript
정의 TypeScript 는 Microsoft 가 2012년 발표한 JavaScript 의 상위 집합 프로그래밍 언어입니다. 정적 타입 시스템, 인터페이스, 제네릭, enum, …

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기