콘텐츠로 바로가기

문법과 의미론 (Grammar & Semantics)

문법과 의미론이 프로그램 텍스트를 구조와 의미로 해석하는 방식을 정리한 언어 이론 학습 노드입니다.

Article
M

Me

hyunyoun's Blog

programming-languages-compilersprogramming-languagescompilerslanguage-theorytype-systemsgrammarsemanticslanguages-compilers9 min read

1. Overview

문법과 의미론(Grammar & Semantics)은 프로그래밍 언어가 "어떤 코드가 문법적으로 올바른가(Syntax)"를 넘어, "그 코드가 실행될 때 어떤 의미인가(Semantics)"를 수학적으로 정의하는 형식 언어 이론(Formal Language Theory)의 기초 공학입니다.

학습자는 정규 문법(Regular Grammar)→문맥 자유 문법(Context-Free Grammar, CFG)→문맥 의존 문법(Context-Sensitive Grammar)의 촘스키 계층(Chomsky Hierarchy)을 살펴봅니다. 나아가 CFG의 BNF/EBNF 표기와 파스 트리(Parse Tree), 추상 구문 트리(AST)를 거쳐 연산 의미론(Operational Semantics), 표시 의미론(Denotational Semantics), **공리 의미론(Axiomatic Semantics, Hoare Logic)**의 세 가지 프로그램 의미 정의 방식을 비교하고, 프로그래밍 언어 설계와 검증(Program Verification)의 이론적 토대를 확보합니다.

2. Scope & Boundaries

In-Scope

  • 촘스키 계층 (Chomsky Hierarchy): 정규 언어(Regular, Type 3), 문맥 자유 언어(CFL, Type 2), 문맥 의존(Type 1), 재귀 열거(Type 0)의 포함 관계와 인식 기계.
  • 문맥 자유 문법 (CFG): BNF/EBNF 표기법, 유도(Derivation), 파스 트리(Parse Tree), 모호성(Ambiguity), CNF(촘스키 정규 형태).
  • 추상 구문 트리 (AST): 파스 트리 → AST 변환, AST 노드 설계, 방문자 패턴(Visitor Pattern).
  • 프로그램 의미론 (Semantics): 연산 의미론(Small-step/Big-step), Hoare Logic(사전/사후 조건), 타입 건전성(Type Soundness, Progress & Preservation).

Out-of-Scope

  • 렉서/파서 구현 도구: Lex/Yacc, ANTLR 도구 사용법 → 05-02-01. Frontend Compilers 영역.
  • 정리 증명기(Proof Assistant): Coq, Isabelle 같은 형식 검증 도구 → Formal Verification 영역.

Boundaries

  • 문법(Syntax) vs 의미(Semantics): 문법은 "어떤 토큰 열이 유효한 문장인가"를 정의(파서가 처리). 의미론은 "유효한 문장이 실행되면 무슨 일이 일어나는가"를 정의(인터프리터/컴파일러가 처리). x + y * z는 문법적으로 올바르지만, *+보다 우선순위가 높다는 것은 의미론(Semantic)의 영역입니다.

3. Counterexample

  • 모호한 문법과 파스 트리의 충돌 (Ambiguous Grammar): 문법 E → E + E | E * E | (E) | ida + b * c를 파싱하면, (a+b)*c로도, a+(b*c)로도 파싱 가능한 두 개의 파스 트리가 생성됩니다. 이런 모호한 문법(Ambiguous Grammar)은 파서가 어떤 규칙을 먼저 적용해야 할지 결정할 수 없어 언어가 의도한 의미를 잘못 계산할 수 있습니다. 우선순위(Precedence)와 결합성(Associativity)을 문법 규칙 자체에 인코딩하여 비모호 문법(Unambiguous Grammar)을 만들어야 합니다.
  • 타입 건전성 위반과 런타임 타입 오류 (Type Unsoundness): 동적 타입 언어(Python, JavaScript)에서 def add(x, y): return x + yadd("hello", 42) 로 호출하면 문법적으로는 허용되지만, 런타임에 TypeError: can only concatenate str (not "int") to str가 발생합니다. 정적 타입 시스템(Haskell, Rust)은 이 오류를 컴파일 시점에 타입 추론으로 탐지하여 런타임 타입 오류 가능성을 줄입니다.

4. Prerequisites

  • 오토마톤 기초 (Basic): 정규 문법을 인식하는 유한 오토마톤(DFA/NFA) 개념이 필요합니다. (04-04-02 String Matching & Automata)
  • 재귀 (Basic): CFG 유도와 파스 트리 구축이 재귀 기반입니다. (04-03-01 Recursion)

5. Learning Map

Sequence Core Cluster Objective & Description Evidence (BoK)
1 Chomsky Hierarchy & Regular 정규 표현식(Regular)이 DFA로 인식 가능한 언어 클래스임을 이해하고, a^n b^n이 정규가 아닌 이유를 설명합니다. P1
2 CFG & Parse Tree BNF로 산술식 문법을 정의하고, 2+3*4의 파스 트리와 AST를 그리는 과정을 익힙니다. P5
3 Operational Semantics while 루프 한 스텝의 의미를 수학적 추론 규칙(Small-step, Structural Operational Semantics)으로 정의하는 방식을 살펴봅니다. Industry
4 Hoare Logic & Verification {P} C {Q} 삼중체(Hoare Triple)로 프로그램 정확성을 수학적으로 증명하는 공리 의미론을 이해합니다. Industry

6. Learning Topics

Basic

Core Topic 01: 언어 클래스의 계층과 정규의 한계, 촘스키 계층 (Chomsky Hierarchy)

  • Why to Learn: 정규 표현식(RegEx)이 HTML 파싱에 적합하지 않은 이유(HTML은 정규 언어가 아닌 CFL), XML/JSON 파서가 스택 기계를 요구하는 이유의 형식 언어 이론적 근거를 이해하기 위해서입니다.
  • What to Learn:
    • Concepts: 촘스키 계층(Type 0~3), 정규 언어(DFA 인식), 문맥 자유 언어(PDA 인식), 펌핑 보조 정리(Pumping Lemma).
    • Skills: 펌핑 보조 정리로 특정 언어가 정규가 아님을 증명.
  • How to Learn:
    • 1단계: a^n b^n (같은 수의 a와 b) 언어가 정규가 아님을 펌핑 보조 정리로 증명합니다. DFA는 유한 상태(Finite State)라 n의 정확한 카운트를 기억할 수 없어 무한한 {ab, aabb, aaabbb,...}를 인식할 수 없음을 확인합니다.
    • 2단계: HTML의 중첩 태그 <a><b></b></a>a^n b^n 패턴(여는 태그 = a, 닫는 태그 = b)과 동일 구조라 정규 언어로 파싱하기 어렵습니다. CFG로는 가능한 이유를 살펴봅니다.
  • Implement: 파이썬 SimpleLanguageChecker. 정규 표현식으로 a^n b^n 언어를 파싱 불가임을 re.match(r'^a+b+$', 'aaabbb') 가 단순 패턴만 확인하고 n=n 균형을 검증 못함을 증명. 반면 재귀 함수로 check_balanced(s) → O(n) 균형 확인 가능 비교.

Core Topic 02: BNF로 언어를 정의하고 트리로 읽다, CFG와 파스 트리 (CFG & Parse Tree)

  • Why to Learn: 프로그래밍 언어 설계자가 새 언어의 문법을 BNF로 규정하고, 컴파일러 프론트엔드가 소스 코드를 파스 트리로 변환하는 과정이 모든 컴파일러의 출발점임을 이해하기 위해서입니다.
  • What to Learn:
    • Concepts: BNF(Backus-Naur Form), EBNF, 생산 규칙(Production Rule), 유도(Derivation), 최좌측/최우측 유도(Leftmost/Rightmost Derivation), 파스 트리(Parse Tree) vs AST.
    • Skills: 주어진 문법에서 특정 문자열 유도하기, 모호성 제거(Precedence/Associativity 규칙 인코딩).
  • How to Learn:
    • 1단계: 산술식 BNF: E → E + T | T; T → T * F | F; F → (E) | num. 이 문법은 *+보다 우선순위 높음을 구조적으로 인코딩합니다. 2+3*4의 파스 트리에서 3*4가 먼저 계산되는 트리 구조를 분석합니다.
    • 2단계: 파스 트리 → AST 변환. 파스 트리는 문법 규칙을 그대로 반영(중간 E, T, F 노드 포함)하지만 AST는 의미에 필요한 노드만 추출(+ 노드, 자식 2* 노드, *의 자식 3, 4)하여 단순화하는 과정을 살펴봅니다.
  • Implement: 파이썬 재귀 하강 파서(Recursive Descent Parser). parse_expr(), parse_term(), parse_factor() 함수로 산술식 문자열을 파싱하여 AST(딕셔너리 트리) 구축. "2+3*4"{op:'+', left:2, right:{op:'*', left:3, right:4}} AST 출력 + 재귀 eval로 14 계산 검증.

Practical

Core Topic 03: 한 스텝씩 실행의 수학, 연산 의미론 (Operational Semantics)

  • Why to Learn: 프로그래밍 언어 설계자가 "이 언어에서 while 루프가 정확히 어떻게 실행되는가"를 자연어 설명이 아니라 수학적 추론 규칙으로 명세(Specification)하는 방법을 이해하고, 인터프리터 구현의 정확성을 높이기 위해서입니다.
  • What to Learn:
    • Concepts: Small-step SOS(Structural Operational Semantics, 한 스텝 실행), Big-step(Natural Semantics, 전체 실행 결과), 구성(Configuration ⟨S, σ⟩), 전이 관계().
    • Skills: if-then-else, while 루프의 추론 규칙 읽기/쓰기.
  • How to Learn:
    • 1단계: Small-step SOS while 규칙: ⟨while b do S, σ⟩ → ⟨if b then (S; while b do S) else skip, σ⟩. 단 한 스텝에서 while을 if문으로 풀어냅니다. 이 규칙을 반복 적용하여 while의 실행을 시뮬레이션하는 방식을 분석합니다.
    • 2단계: Big-step(Natural) 의미론: ⟨S, σ⟩ ⇓ σ' (명령 S가 상태 σ에서 실행되어 최종 상태 σ'을 만든다). 대입문 규칙 ⟨x:=a, σ⟩ ⇓ σ[x↦⟦a⟧σ]를 읽는 방법을 익힙니다.
  • Implement: 파이썬 간단 인터프리터(Toy Language). Expr: Num | Add | Mul | Var. Stmt: Assign | Seq | IfElse | While. eval_expr(expr, env) + exec_stmt(stmt, env) 구현. x=1; while x<10: x=x*2env={'x':16} 실행 후 상태 검증. 각 스텝 출력으로 Small-step SOS 시뮬레이션.

Advanced

Core Topic 04: 수학으로 코드를 증명하다, Hoare Logic (Hoare Triple & Program Verification)

  • Why to Learn: NASA 우주선 소프트웨어, 의료 기기 펌웨어, 금융 핵심 계산 모듈처럼 높은 신뢰성이 필요한 코드에서 프로그램 검증(Program Verification)의 이론적 기반으로 Hoare Logic이 어떻게 쓰이는지 이해하기 위해서입니다.
  • What to Learn:
    • Concepts: Hoare Triple {P} C {Q} (P: 사전 조건, C: 명령, Q: 사후 조건), 대입 공리, 순차 합성 규칙, while 루프 불변식(Loop Invariant).
    • Skills: 루프 불변식 도출, 약화(Weakening)/강화(Strengthening) 규칙.
    • Tools: Frama-C(C 코드 검증), Dafny, Z3 SMT Solver.
  • How to Learn:
    • 1단계: {x=n} x:=x+1 {x=n+1} Hoare Triple 검증. 대입 공리: {Q[x←a]} x:=a {Q} 를 역방향으로 Q={x=n+1}, Q[x←x+1]={x+1=n+1} = {x=n}. 사전 조건이 정확히 {x=n}이므로 Triple이 성립하는 과정을 확인합니다.
    • 2단계: 루프 불변식(Loop Invariant)으로 while 증명. sum = 0; i = 0; while i < n: sum += i; i += 1. 불변식 I: sum = i*(i-1)/2. 진입 전 I 성립 검증, 루프 본체 후 I 유지 검증, 종료 후 I ∧ ¬(i<n)sum = n*(n-1)/2 최종 결론 도출을 살펴봅니다.
  • Implement: 파이썬 verify_hoare_triple(pre, stmt, post, test_cases). 사전 조건, 명령(람다 함수), 사후 조건을 받아 test_cases 값들에서 사전→실행→사후 검증 자동화. {x>0} x:=x*2 {x>0} 100개 랜덤 테스트로 소프트웨어 테스트 기반 검증 모사.

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry) Flags(core)
Syntax 프로그래밍 언어에서 코드가 어떻게 작성되어야 하는지를 규정하는 겉보기 형태이자 텍스트의 구조적 규칙입니다. 기본 언어 설계 Grammar / Parsing Semantics 구문이 맞다고 해서 의미가 올바른 것은 아님 P1:CS2023 core
Semantics 구문에 맞게 작성된 코드가 실행될 때 어떤 상태 변화와 의미를 갖는지를 정의하는 수학적/논리적 규칙입니다. 권장 프로그램 의미 해석 Operational Semantics Syntax 겉보기에 같아도 언어마다 Semantics는 다를 수 있음 P5:SFIA core
Scope 변수나 함수가 정의되었을 때, 그 이름(Identifier)이 프로그램 텍스트 내의 어느 구간에서 유효한지를 결정하는 공간적 범위입니다. 기본 식별자 바인딩 Closure / Lexical Scope Dynamic Scope 런타임 호출 순서가 아닌 작성된 텍스트 위치가 중요함 (Lexical) Industry core
Abstract Syntax Tree (AST) 소스 코드의 문자열을 구문 분석(Parsing)하여, 괄호나 세미콜론 같은 구문 장식을 제거하고 프로그램의 논리 구조만 트리 형태로 남긴 자료구조입니다. 실무 컴파일러 프론트엔드 Parser / Token Parse Tree (CST) 모든 구문 문자가 다 들어있는 CST와는 달리 의미 단위로 압축됨 Industry core

8. References

Primary

  • [P1] CS2023 - Programming Languages (PL) - Syntax and Semantics
  • [P5] SFIA - Software Design (SWDN) - Language Formalism

Secondary

  • [Types and Programming Languages] Benjamin C. Pierce - Operational Semantics and Formalization
  • [Programming Language Pragmatics] Michael L. Scott - Scope, Binding, and Evaluation

Industry

  • [ECMAScript Language Specification] - Lexical Environment and Scope Rules
  • [Python Language Reference] - Execution Model and Naming

9. Final Checklist

Primary

  • 정규 표현식(Regular Expression)으로 변수명을 식별하고, 문맥 자유 문법(CFG)으로 if-else 블록의 중첩 구조를 해석하는 차이를 설명할 수 있는가?
  • 구문 에러(Syntax Error)와 런타임 의미 에러(Semantic Error)의 발생 시점과 원인을 명확히 구분할 수 있는가?

Secondary

  • 렉시컬 스코프(Lexical Scope)를 사용하는 언어에서 클로저(Closure)가 형성될 때, 자유 변수(Free Variable)가 메모리에 바인딩되는 메커니즘을 설명할 수 있는가?
  • 소스 코드가 Abstract Syntax Tree (AST)로 변환되는 과정을 묘사하고, 이것이 인터프리터 패턴(Interpreter Pattern)에서 어떻게 순회(Traversal)되는지 설명할 수 있는가?

Industry

  • 특정 언어의 조작적 의미론(Operational Semantics) 명세가 실제 가상 머신(VM)의 레지스터나 스택 상태 변화로 어떻게 매핑되는지 아키텍처 관점에서 논증할 수 있는가?
  • 프로그래밍 언어의 평가 전략(Call-by-value vs Call-by-reference/Call-by-need) 차이가 대규모 객체 전달 시 발생하는 메모리/CPU 부하에 미치는 영향을 설계할 수 있는가?

Languages Compilers · Language Theory & Type Systems

2 / 5