상위 문서: 파싱
'''이론 컴퓨터 과학 {{{#!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. 개요
하향식 파서(Top-down parser)는 파서 유형 중 하나로, 시작 symbol의 생성 규칙에서 terminal 노드까지 유도하며 진행되는 parser이다. 입력 문자열을 시작 기호부터 시작하는 문법의 생성 규칙과 일치시키는 과정을 수행한다.2. 고려 사항
2.1. Left recursion 문제
하향식 파서에서 left-recursion 문법을 활용하는 경우, 무한 루프가 발생할 수 있다. 예를 들어 다음 BNF를 고려해보자[math(
\begin{array}{l}
)]\textbf{A}::= \textbf{A} \text{b}\ |\ c \\
\end{array}여기서 A는 우선 자기 자신을 참조하고, 이로 인해 파서는 A를 파싱하려고 할 때 항상 A를 먼저 시도하게 되며, 이 과정이 반복되어 무한 루프에 빠지게 된다. 무한 루프에 빠지게 되면, 재귀 호출이 계속 쌓이게 되어 결국 스택 오버플로우가 발생할 수 있다.
조금 더 세분화된 과정은 다음과 같다.
- parser가 non-terminal [math(\textbf{A})] 생성 규칙을 적용하려 한다.
- 규칙 [math(\textbf{A}::= \textbf{A} \text{b})]를 적용 후 다시 non-terminal [math(\textbf{A})]를 만난다. 이 non-terminal A를 파싱하기 위해, 규칙 [math(\textbf{A}::= \textbf{A} \text{b})]를 다시 적용하려할 것이다. 이러한 과정이 계속 반복된다.
- parser가 [math(\textbf{A})]를 다시 구문 분석하려고 할 때마다 호출 스택에 재귀 호출이 계속 추가되므로, 스택 오버플로 오류가 발생하게 된다.
좌측 재귀 문제를 해결하기 위해, 문법을 right recursion 또는 recursion 자체[1]가 없도록 변환해야 한다. 예를 들어, 위의 문법은 다음과 같이 right recursion으로 변환 가능하다.
[math(
\begin{array}{l}
)]\textbf{A}::= \text{c}\textbf{A}' \\
\textbf{A}' ::= \text{b} \textbf{A}'\ \vert\ \epsilon
\end{array}\textbf{A}' ::= \text{b} \textbf{A}'\ \vert\ \epsilon
2.2. Left factoring
문법 규칙의 RHS의 첫부분에 공통적인 심볼이 존재하는 경우 문법이 비결정적일 수 있다. 특히 앞의 하나의 심볼로 다음 유도 방법을 결정하는 경우 이러한 문제는 더 자주 생긴다. 해당 부분에 새로운 non-terminal을 적용해 분리하는 방법도 있다.[math(
\textbf{A} ::= \alpha \beta\ |\ \alpha \gamma \Rightarrow
\begin{array}{l} \textbf{A} ::= \alpha\ \textbf{A}'\\
\textbf{A}' ::= \beta\ |\ \gamma
\end{array}
)]
\textbf{A} ::= \alpha \beta\ |\ \alpha \gamma \Rightarrow
\begin{array}{l} \textbf{A} ::= \alpha\ \textbf{A}'\\
\textbf{A}' ::= \beta\ |\ \gamma
\end{array}
)]
이러한 방법을 left factoring이라고 한다. 이 방식으로 파서가 α prefix를 통해 A non-terminal로 진입 후 다음 토큰을 기반으로 하여 β, γ 중 선택할 부분을 결정할 수 있다.
3. 재귀 하강 파서(Recursive Descent Parser)
재귀 하강 파서는 재귀 호출을 사용하여 입력 문자열을 분석한다. 각 non-terminal에 대해 하나의 함수가 존재하며, 이 함수들은 문법의 생성 규칙에 따라 입력을 분석한다. 구현이 비교적 간단하지만, left recursion 문법을 처리할 수는 없다.3.1. Pseudo code
[math( procedure\ A () \{\\ )] \quad \text{A에 대해 규칙}\ A\rarr X_1X_2\cdots X_n\ \text{선택}; \\ \quad \text{for}\ (i=1, 2, \cdots, k)\ \{ \\ \quad\quad\quad \text{if } (X_i \text{가 non-terminal}) \\ \quad\quad\quad\quad \text{call procedure } X_i(); \\ \quad\quad\quad \text{else if } (X_i\text{가 현재 input symbol a와 일치})\\ \quad\quad\quad\quad (i+1) \text{번째 symbol로 이동}; \\ \quad\quad\quad \text{else Syntax error}; \\ \quad \} \\ \} |
3.2. Python 구현 예시
예를 들어, 다음의 이진수와 이진수 간의 덧셈에 대한 BNF 문법 검증은 Python의 경우 다음과 같이 구현할 수 있다.[math(
\begin{array}{l}
)]\textbf{expr}::= \textbf{binary} + \textbf{binary}~|~\textbf{binary} \\
\textbf{binary} ::= \textbf{digit}~|~\textbf{digit}\ \textbf{binary} \\
\textbf{digit} ::= 0~|~1
\end{array}\textbf{binary} ::= \textbf{digit}~|~\textbf{digit}\ \textbf{binary} \\
\textbf{digit} ::= 0~|~1
#!syntax python
class RecursiveDescentParser:
def __init__(self, input_string):
self.input = input_string.replace(" ", "")
self.index = 0
def parse(self):
result = self.expr()
if self.index < len(self.input):
raise SyntaxError("Unexpected character at index {}".format(self.index))
return result
def expr(self):
# expr ::= binary + binary | binary
left = self.binary()
if self.index < len(self.input) and self.input[self.index] == "+":
self.index += 1 # skip '+'
right = self.binary()
return left and right # 두 binary의 문법 일치 여부 출력
else:
return left # 단일 binary의 경우
def binary(self):
# binary ::= digit | digit binary
if not self.digit():
return False
while self.index < len(self.input) and self.input[self.index] in "01":
self.index += 1
return True
def digit(self):
# digit ::= 0 | 1
if self.index < len(self.input) and self.input[self.index] in "01":
self.index += 1
return True
else:
return False
# 예시: 101 + 010
code = "101 + 010"
parser = RecursiveDescentParser(code)
result = parser.parse()
print(result) # True
4. Predictive Parser(예측 파서)
[1] 다만 간접적인 left recursion도 문제가 될 수 있다.