답변:
높은 수준에서 LL 구문 분석과 LR 구문 분석의 차이점은 LL 구문 분석기가 시작 기호에서 시작하여 대상 문자열에 도달하기 위해 프로덕션을 적용하려고하는 반면, LR 구문 분석기는 대상 문자열에서 시작하여 다시 시작합니다. 상징.
LL 구문 분석은 왼쪽에서 오른쪽으로, 가장 왼쪽에서 파생됩니다. 즉, 입력 심볼을 왼쪽에서 오른쪽으로 고려하여 가장 왼쪽 파생을 구성하려고 시도합니다. 이것은 시작 기호에서 시작하여 대상 문자열에 도달 할 때까지 가장 왼쪽의 비 터미널을 반복적으로 확장하여 수행됩니다. LR 구문 분석은 왼쪽에서 오른쪽으로 가장 오른쪽 파생입니다. 즉, 왼쪽에서 오른쪽으로 스캔하여 가장 오른쪽 파생을 구성하려고합니다. 파서는 지속적으로 입력의 하위 문자열을 선택하여 비 터미널로 되돌리려 고 시도합니다.
LL 구문 분석 중에 구문 분석기는 두 가지 조치 중에서 지속적으로 선택합니다.
예를 들어,이 문법을 보면 :
int
그런 다음 string이 주어지면 int + int + int
LL (2) 파서 (두 개의 lookahead 토큰을 사용)는 다음과 같이 문자열을 구문 분석합니다.
Production Input Action
---------------------------------------------------------
S int + int + int Predict S -> E
E int + int + int Predict E -> T + E
T + E int + int + int Predict T -> int
int + E int + int + int Match int
+ E + int + int Match +
E int + int Predict E -> T + E
T + E int + int Predict T -> int
int + E int + int Match int
+ E + int Match +
E int Predict E -> T
T int Predict T -> int
int int Match int
Accept
각 단계에서 우리는 프로덕션에서 가장 왼쪽의 심볼을 봅니다. 터미널 인 경우 터미널과 일치하고 터미널이 아닌 경우 규칙 중 하나를 선택하여 터미널이 무엇인지 예측합니다.
LR 파서에는 두 가지 동작이 있습니다.
예를 들어, LR (1) 파서 (하나의 미리보기 토큰이있는)는 다음과 같은 문자열을 구문 분석 할 수 있습니다.
Workspace Input Action
---------------------------------------------------------
int + int + int Shift
int + int + int Reduce T -> int
T + int + int Shift
T + int + int Shift
T + int + int Reduce T -> int
T + T + int Shift
T + T + int Shift
T + T + int Reduce T -> int
T + T + T Reduce E -> T
T + T + E Reduce E -> T + E
T + E Reduce E -> T + E
E Reduce S -> E
S Accept
언급 한 두 가지 파싱 알고리즘 (LL 및 LR)은 서로 다른 특성을 갖는 것으로 알려져 있습니다. LL 파서는 손으로 직접 작성하는 것이 쉽지만 LR 파서보다 강력하지 않으며 LR 파서보다 훨씬 작은 문법을 받아들입니다. LR 파서는 많은 맛 (LR (0), SLR (1), LALR (1), LR (1), IELR (1), GLR (0) 등)으로 제공되며 훨씬 강력합니다. 또한 훨씬 더 복잡한 경향이 있으며 거의 항상 yacc
또는 같은 도구로 생성됩니다 bison
. LL 파서는 또한 ANTLR
도구에 의해 사용되는 LL (*)를 포함하여 많은 풍미 가 있습니다. 실제로 LL (1)이 가장 널리 사용됩니다.
뻔뻔스러운 플러그 인으로서 LL 및 LR 파싱에 대해 더 배우고 싶다면 컴파일러 과정을 가르치고 과정 웹 사이트 에서 파싱 에 대한 유인물과 강의 슬라이드를 마쳤습니다 . 유용하다고 생각되면 그 중 하나를 자세히 설명해 드리겠습니다.
Josh Haberman은 그의 기사 LL과 LR Parsing Demystified 에서 LL 파싱은 폴란드 표기법 과 직접적으로 일치하고 LR은 역 폴란드 표기법 과 일치한다고 주장합니다 . PN과 RPN의 차이점은 방정식의 이진 트리를 순회하는 순서입니다.
+ 1 * 2 3 // Polish (prefix) expression; pre-order traversal.
1 2 3 * + // Reverse Polish (postfix) expression; post-order traversal.
Haberman에 따르면 이는 LL과 LR 파서의 주요 차이점을 보여줍니다.
LL 및 LR 파서의 작동 방식의 주요 차이점은 LL 파서가 구문 분석 트리의 사전 주문 순회를 출력하고 LR 파서가 주문 후 순회를 출력한다는 것입니다.
LL 구문 분석은 LR과 비교할 때 장애가 있습니다. 다음은 LL 파서 생성기의 악몽 인 문법입니다.
Goal -> (FunctionDef | FunctionDecl)* <eof>
FunctionDef -> TypeSpec FuncName '(' [Arg/','+] ')' '{' '}'
FunctionDecl -> TypeSpec FuncName '(' [Arg/','+] ')' ';'
TypeSpec -> int
-> char '*' '*'
-> long
-> short
FuncName -> IDENTIFIER
Arg -> TypeSpec ArgName
ArgName -> IDENTIFIER
FunctionDef는 ';'까지 정확히 FunctionDecl처럼 보입니다. 또는 '{'가 발견되었습니다.
LL 구문 분석기는 두 규칙을 동시에 처리 할 수 없으므로 FunctionDef 또는 FunctionDecl을 선택해야합니다. 그러나 어느 것이 올바른지 알기 위해서는 ';' 또는 '{'. 문법 분석시, 예측 (k)은 무한한 것으로 보입니다. 구문 분석 시간에는 유한하지만 클 수 있습니다.
LR 파서는 두 규칙을 동시에 처리 할 수 있으므로 미리 볼 필요가 없습니다. 따라서 LALR (1) 파서 생성기는이 문법을 쉽게 처리 할 수 있습니다.
입력 코드가 주어지면 :
int main (int na, char** arg);
int main (int na, char** arg)
{
}
LR 파서는
int main (int na, char** arg)
';'가 나타날 때까지 어떤 규칙이 인식되는지 신경 쓰지 않고; 또는 '{'.
LL 구문 분석기는 인식되는 규칙을 알아야하기 때문에 'int'에서 끊어집니다. 따라서 ';'을 미리 봐야합니다. 또는 '{'.
LL 파서의 또 다른 악몽은 문법의 재귀입니다. 왼쪽 재귀는 문법에서 일반적인 것으로 LR 파서 생성기에는 문제가 없지만 LL은 처리 할 수 없습니다.
따라서 LL을 사용하여 문법을 부 자연스럽게 작성해야합니다.
가장 왼쪽 파생 예 : 문맥이없는 문법 G는
z → xXY (규칙 : 1) X → Ybx (규칙 : 2) Y → bY (규칙 : 3) Y → c (규칙 : 4)
가장 왼쪽으로 파생 된 문자열 w = 'xcbxbc'를 계산합니다.
z ⇒ xXY (규칙 : 1) ⇒ xYbxY (규칙 : 2) ⇒ xcbxY (규칙 : 4) ⇒ xcbxbY (규칙 : 3) ⇒ xcbxbc (규칙 : 4)
가장 오른쪽 파생 예 : K → aKK (규칙 : 1) A → b (규칙 : 2)
가장 오른쪽 파생 된 문자열 w = 'aababbb'을 계산하십시오.
K ⇒ aKK (규칙 : 1) ⇒ aKb (규칙 : 2) ⇒ aaKKb (규칙 : 1) ⇒ aaKaKKb (규칙 : 1) ⇒ aaKaKbb (규칙 : 2) ⇒ aaKabbb (규칙 : 2) ⇒ aababbb (규칙 : 2)