최근 수정 시각 : 2024-07-04 00:19:35

파싱

구문 분석에서 넘어옴

'''이론 컴퓨터 과학
{{{#!wiki style="display: inline-block; font-family:Times New Roman, serif;font-style:italic"'''
{{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px)"
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"
<colbgcolor=#a36> 이론
기본 대상 수학기초론{수리논리학(논리 연산) · 계산 가능성 이론 · 범주론 · 집합론} · 이산수학(그래프 이론) · 수치해석학 · 확률론통계학 · 선형대수학
다루는 대상과 주요 토픽
계산 가능성 이론 재귀함수 · 튜링 머신 · 람다대수 · 처치-튜링 명제 · 바쁜 비버
오토마타 이론 FSM · 푸시다운 · 튜링 머신(폰노이만 구조) · 정규 표현식 · 콘웨이의 생명 게임 · 형식언어
계산 복잡도 이론 점근 표기법 · 튜링 기계^고전, 양자, 비결정론적, 병렬 임의접근 기계^ · 알고리즘 · 자료구조 · 알고리즘 패러다임(그리디 알고리즘, 동적 계획법)
정보이론 데이터 압축(무손실 압축 포맷 · 손실 압축 포맷) · 채널 코딩(채널 용량) · 알고리즘 정보 이론(AIT) · 양자정보과학
프로그래밍 언어이론 프로그래밍 언어(함수형 언어 · 객체 지향 프로그래밍 · 증명보조기) · 메타 프로그래밍 · 유형 이론 · 프로그래밍 언어 의미론 · 파싱 · 컴파일러 이론
주요 알고리즘 및 자료구조
기초 정렬 알고리즘 · 순서도 · 탐색 알고리즘
추상적 자료형 및 구현 배열^벡터^ · 리스트^연결 리스트^ · 셋(set)^레드-블랙 트리, B-트리^ · 우선순위 큐^, 피보나치 힙^
수학적 최적화 조합 최적화 외판원 순회 문제 · 담금질 기법 · 유전 알고리즘 · 기계학습
볼록 최적화 내부점 방법 · 경사하강법
선형계획법 심플렉스법
계산 수론 및 암호학 밀러-라빈 소수판별법 · Pollard-rho 알고리즘 · 쇼어 알고리즘 · LLL 알고리즘 · 해시(MD5 · 암호화폐 · 사전 공격(레인보우 테이블) · SHA) · 양자 암호
대칭키 암호화 방식 블록 암호 알고리즘(AES · ARIA · LEA · Camellia) · 스트림 암호 알고리즘(RC4)
공개키 암호화 방식 공개키 암호 알고리즘(타원 곡선 암호 · RSA) · 신원 기반 암호 알고리즘(SM9)
계산기하학 볼록 껍질 · 들로네 삼각분할 및 보로노이 도형^Fortune의 line-sweeping 알고리즘^ · 범위 탐색^vp-tree, R-tree^ · k-NN
그래프 이론 탐색^BFS, DFS, 다익스트라 알고리즘, A* 알고리즘^ · 에드몬드-카프 · 크루스칼 알고리즘 · 위상 정렬 · 네트워크 이론
정리
정지 문제대각선 논법 · 암달의 법칙 · P-NP 문제미해결 · 콜라츠 추측미해결
틀:이산수학 · 틀:수학기초론 · 틀:컴퓨터공학 }}}}}}}}}



[[컴퓨터공학|컴퓨터 과학 & 공학
Computer Science & Engineering
]]
[ 펼치기 · 접기 ]
||<tablebgcolor=#fff,#1c1d1f><tablecolor=#373a3c,#ddd><colbgcolor=#0066DC><colcolor=white> 기반 학문 ||수학(해석학 · 이산수학 · 수리논리학 · 선형대수학 · 미적분학 · 미분방정식 · 대수학(환론 · 범주론) · 정수론) · 이론 컴퓨터 과학 · 암호학 · 전자공학 · 언어학(형태론 · 통사론 · 의미론 · 화용론 · 음운론) · 인지과학 ||
하드웨어 구성 SoC · CPU · GPU(그래픽 카드 · GPGPU) · ROM · RAM · SSD · HDD · 참조: 틀:컴퓨터 부품
기술 기계어 · 어셈블리어 · C/C++ · C# · Java · Python · BIOS · 절차적 프로그래밍 · 객체 지향 프로그래밍 · 해킹 · ROT13 · 일회용 비밀번호 · 사물인터넷 · 와이파이 · GPS · 임베디드 · 인공신경망 · OpenGL · EXIF · 마이크로아키텍처 · ACPI · UEFI · NERF · gRPC · 리버스 엔지니어링 · HCI · UI · UX · 대역폭 · DBMS · NoSQL · 해시(SHA · 브루트 포스 · 레인보우 테이블 · salt · 암호화폐) · RSA 암호화 · 하드웨어 가속
연구

기타
논리 회로(보수기 · 가산기 · 논리 연산 · 불 대수 · 플립플롭) · 정보이론 · 임베디드 시스템 · 운영 체제 · 데이터베이스 · 프로그래밍 언어{컴파일러(어셈블러 · JIT) · 인터프리터 · 유형 이론 · 파싱 · 링커 · 난해한 프로그래밍 언어} · 메타데이터 · 기계학습 · 빅데이터 · 폰노이만 구조 · 양자컴퓨터 · 행위자 모델 · 인코딩(유니코드 · MBCS) · 네트워크 · 컴퓨터 보안 · OCR · 슈퍼컴퓨터 · 튜링 머신 · FPGA · 딥러닝 · 컴퓨터 구조론 · 컴퓨터 비전 · 컴퓨터 그래픽스 · 인공지능 · 시간 복잡도(최적화) · 소프트웨어 개발 방법론 · 디자인 패턴 · 정보처리이론 · 재귀 이론 · 자연어 처리(기계 번역 · 음성인식) · 버전 (버전 관리 시스템 · Git · GitHub)

1. 개요2. 과정
2.1. 어휘 분석(lexing)2.2. 구문 분석(parsing)
2.2.1. 예시
2.3. 파스 트리(parse tree)
3. 모호성(ambiguity)
3.1. 모호성 해결 방법
4. 파싱 방식
4.1. 하향식 파서(Top-down Parser)4.2. 상향식 파서(Bottom-up Parser)
5. 관련 문서

1. 개요

Parsing, 구문 분석

프로그래밍 언어로 짠 코드를 실행하거나, 프로그램으로 변환시키 위해서는 어휘 분석(lexing) 또는 토큰화(tokenize)를 통해 키워드, 식별자(Identifier), 연산자(Operator) 및 리터럴(Literal) 같은 개별 토큰으로 변환한다. 이후 이 코드가 프로그래밍 언어의 문법에 맞는지 확인하는 과정을 파싱(parsing)이라고 한다.

2. 과정

파일:HowToCheckSyntax.png
  • 어휘 분석(Lexing) 과정에서는 코드 문자열은 개별 토큰으로 분리되며, 각 토큰은 코드의 기본적인 의미 단위로써 자료형, 식별자, 리터럴, 연산자 및 기타 구문 기호를 나타낸다. 이러한 과정을 토큰화(tokenize)라고 한다.
  • 구문 분석(parsing) 과정에서는 이러한 토큰들이 파스 트리(parse tree)로 재구성되어 소스 코드의 구조적 계층을 표현한다. 파스 트리의 각 노드는 코드의 구문적 관계를 나타낸다. 이러한 결과로 나온 파스 트리에서 불필요한 요소[1]를 제외하고 인터프리터를 위한 직접 실행할 요소나, 컴파일을 위해 필요한 구조만 가져와 AST(추상 구문 트리)를 형성한다.

이러한 처리 부분을 front-end라고 하며, AST로 변환 후 back-end 부분에서 semantics에 대해 바로 해석을 수행하는 경우 인터프리터, 추가 분석, 최적화 및 다른 언어로의 변환을 수행하게 되면 컴파일러라고 한다.

2.1. 어휘 분석(lexing)

코드 문자열을 어휘 단위로 쪼개어 토큰화를 수행하는 과정이다. 어휘분석기로 진행되며, 정규표현식유한 상태 기계를 통해 검사한다. 예를 들어 다음 코드 같은 경우
#!syntax cpp
int x = 2 + 3;
int y = x - 5;

아래와 같이 토큰화 될 수 있다.
TYPE_INT IDENT("x") OP_EQ INT(2) OP_ADD INT(3) SEMICOLON
TYPE_INT IDENT("y") OP_EQ IDENT("x") OP_MIN INT(5) SEMICOLON

2.2. 구문 분석(parsing)

생성된 토큰들이 의미를 가질 수 있는 형태인지를 파스 트리(parse tree)로 구성하는 과정이다. 이때 문맥 자유 문법(Contextbf Free Grammar)을 사용한다.
RHS(right-hand side)를 LHS(left-hand side)로 치환하는 역 production을 통해 시작 non-terminal로 변환하는 과정에서, 어떤 문자열 s가 시작 non-terminal로 치환 가능하다면 문자열 s는 언어 [math(L(G))]에 속한다고 할 수 있다. ([math(s \in L(G))])

2.2.1. 예시

아래 문법에 대해 다음 문자열이 있다고 하자. 표기에 대한 자세한 설명은 배커스-나우르 표기법 참조.
#!syntax java
<expr> ::= <term> | <expr> '+' <term> // 시작 non-terminal
<term> ::= <factor> | <term> '*' <factor>
<factor> ::= <number>
<number> ::= <digit> | <digit> <number>
<digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
\

문자열 [math(1+2*3)]에 대해 해당 문법을 기반으로 파싱하면 다음과 같이 될 것이다. 굵은 글씨는 non-terminal, 일반 텍스트는 terminal 노드다. 간략화를 위해 수를 <digit>에서 <number>로 변환하는 부분은 생략한다.
[math(
\begin{array}{c|l}
1 + 2 * 3 &\Rightarrow_P 1 + 2 * \textbf{number} \\
&\Rightarrow_P 1 + 2 * \textbf{factor} \\
&\Rightarrow_P 1 + \textbf{number} * \textbf{factor} \\
&\Rightarrow_P 1 + \textbf{factor} * \textbf{factor} \\
&\Rightarrow_P 1 + \textbf{term} * \textbf{factor} \\
&\Rightarrow_P 1 + \textbf{term} \\
&\Rightarrow_P \textbf{number} + \, \textbf{term} \\
&\Rightarrow_P \textbf{factor} + \, \textbf{term} \\
&\Rightarrow_P \textbf{term} + \, \textbf{term} \\
&\Rightarrow_P \textbf{expr} + \textbf{term} \\
&\Rightarrow_P \textbf{expr}
\end{array}
)]

2.3. 파스 트리(parse tree)

파일:parse_tree_1.png
위는 파싱 예시를 기반으로 구성된 parse tree다. 파싱 과정을 나타내는 트리로, 트리의 부모와 자식은 서로 derive와 parse의 관계다. leaf 노드에서 루트 노드로 가면 parsing 과정, 루트 노드에서 leaf 노드로 가면 deriving 과정이다. 이때 leaf 노드는 반드시 terminal 노드여야 하며, 내부와 루트 노드는 non-terminal 노드로 구성된다. 이때 루트 노드는 시작 non-terminal이다.

3. 모호성(ambiguity)

아래 문법에 대해
#!syntax java
<expr> ::= <expr> '+' <expr> | <expr> '-' <expr> | <expr> '**' <expr> | <number> // 시작 non-terminal
<number> ::= <digit> | <digit> <number> // <number>는 예시 부분과 동일

문자열 [math(3 - 2 + 1)]을 유도한다고 하자. 위의 BNF를 기반으로 좌측 유도(Leftmost derivation)와 우측 유도(Rightmost derivation)을 수행하면 아래와 같이 다른 형태의 파스 트리가 구성되는 것을 확인할 수 있다.
좌측 유도 파스 트리
[math(
\begin{array}{c|l}
\textbf{expr} & \Rightarrow_D \textbf{expr} - \textbf{expr} \\
& \Rightarrow_D \textbf{number} - \textbf{expr} \\
& \Rightarrow_D3 - \textbf{expr} \\
& \Rightarrow_D 3 - \textbf{expr + expr}\\
& \Rightarrow_D 3 - \textbf{number} + \textbf{expr}\\
& \Rightarrow_D 3 - 2+\textbf{expr} \\
& \Rightarrow_D 3 - 2+\textbf{number} \\
& \Rightarrow_D 3 - 2+1 \\
\end{array})]
파일:leftmost_parse.png
우측 유도 파스 트리
[math(
\begin{array}{c|l}
\textbf{expr} & \Rightarrow_D \textbf{expr} + \textbf{expr} \\
& \Rightarrow_D \textbf{expr} + \textbf{number} \\
& \Rightarrow_D \textbf{expr} + 1 \\
& \Rightarrow_D \textbf{expr} - \textbf{expr} + 1 \\
& \Rightarrow_D \textbf{expr} - \textbf{number} + 1 \\
& \Rightarrow_D \textbf{expr} - 2 + 1 \\
& \Rightarrow_D \textbf{number} - 2 + 1 \\
& \Rightarrow_D 3 - 2 + 1 \\
\end{array}
)]
파일:rightmost_parsepng.png

위와 같이 어떤 문법 [math(G)]에 대해 특정 문장이 2개 이상의 서로 다른 파스 트리를 가지면 문법 [math(G)]는 모호하다(ambiguous)고 한다.

3.1. 모호성 해결 방법

연산자의 우선순위와 결합 법칙을 기반으로 파스 트리가 모호하지 않게 수정할 수 있다.
  • 문법 수정을 위해서 각 연산자마다 새로운 non-terminal을 만들고 시작 non-terminal부터 가장 낮은 우선순위를 가진 연산자를 적용한다.
  • 결합 법칙 상으로는 좌측 결합이면 left recursion, 우측 결합이 되면 right recursion을 적용한다. 예를 들어 모호성 문단의 BNF는 +, - 연산자가 동일한 우선순위를 가지며 좌측 결합이고, **Python의 제곱 연산자라고 하면, +, -보다 높은 우선순위를 가지며 우측결합이다. 이를 기반으로 BNF를 다음과 같이 수정하면, 모호성이 해결된다.
    {{{#!syntax java
    <expr> ::= <expr> '+' <term> | <expr> '-' <term> | <term> // +, -은 좌측 결합이므로 left recursion 적용
<term> ::= <number> '**' <term> | <number> // 제곱 연산은 우측 결합이므로 right recursion 적용
<number> ::= <digit> | <digit> <number> // <number>는 예시 부분과 동일
}}}
위 수정된 문법에 대해 문자열 [math(3-2+1)]을 파스 트리로 구성하면, [math(\textbf{expr} \Rightarrow_D \textbf{expr} +\textbf{term})]으로 시작하는 경우만 해당 문자열로 유도되며 [math(\textbf{expr} \Rightarrow_D \textbf{expr} - \textbf{term})]으로 시작하는 경우는 유도되지 않는다. 식 [math(1+2 \text{**} 3)]도 이전 방법은 [math((1+2)\text{**}3)], [math(1+(2 \text{**} 3))] 두 가지로 파스 트리가 구성될 수 있었지만 위와 같이 결합 법칙을 기반으로 수정하면 모호성이 해결된다.

4. 파싱 방식

Top-down
파일:topdown-intro-1.jpg
Bottom-up
파일:bottomup-intro-0.jpg
파서의 구조는 크게 하향식(Top-down) 방식과 상향식(Bottom-up) 방식으로 나눌 수 있다. 상향식은 루트 노드에서부터 leaf 노드를 생성해나가는 방식이다. 좌측 유도 순서의 생성 규칙을 적용하며 이러한 순서로 구성된 리스트인 좌파스(left parse)를 생성하게 된다.

하향식은 leaf 노드들로부터 루트 노드 쪽으로 위로 생성해나가는 방식이다. 우측 유도의 역순의 생성규칙을 적용한다. 유도 자체는 우측부터 하지만 입력 문자열은 왼쪽부터 진행해 나가기 때문이다. 이렇게 우측 유도의 역순으로 구성되는 리스트를 우파스(right parse)라고 한다.

4.1. 하향식 파서(Top-down Parser)

  • Recursive Descent Parser
  • Predictive Parser
    • LL(1)
    • LL(k)
자세한 정보는 파싱/하향식 참조.

4.2. 상향식 파서(Bottom-up Parser)

  • Shift-Reduce Parser
    • LR(0)
    • SLR(1), Simple LR(1)
    • CLR(1), Canonical LR(1)
    • LALR(1), LookAhead LR(1)

5. 관련 문서


[1] 예를 들어 괄호, 세미콜론 등