문제를 설명하기 위해 Dangling Else Problem은 코드 구문 사양에서 애매 모호하며 다음 if 및 else의 경우 명확하지 않을 수 있습니다.
가장 단순하고 고전적인 예 :
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
언어 사양의 세부 사항을 모르는 사람들에게는 분명하지 않습니다. if
을 얻는다 else
(이 특정 코드가 대여섯 언어에서 유효합니다, 그러나 각각 다르게 수행 할 수있다).
Dangling Else 구문은 스캐너를 사용하지 않는 구문 분석기 구현에 잠재적 인 문제를 제기합니다. 전략은 구문 분석기가 토큰 화하기에 충분할 때까지 (조회 또는 중간 언어로 컴파일 할 때까지) 파일 스트림을 한 번에 한 문자 씩 문지르는 것이기 때문입니다. . 이것은 파서가 최소한의 상태를 유지할 수있게한다. 파일에 구문 분석 된 토큰을 작성하기에 충분한 정보가 있다고 생각되면 그렇게됩니다. 이것이 스캐너리스 파서의 최종 목표입니다. 빠르고 간단하며 가벼운 컴파일.
구두점 전후에 줄 바꿈과 공백이 의미가 없다고 가정하면 (대부분의 C 스타일 언어에서와 같이)이 명령문은 컴파일러에 다음과 같이 나타납니다.
if(conditionA)if(conditionB)doFoo();else doBar;
컴퓨터로 완벽하게 구문 분석 할 수 있으므로 보도록하겠습니다. 내가 가질 때까지 한 번에 한 문자 씩 얻습니다.
if(conditionA)
오, 나는 그것이 의미하는 것을 알고 있습니다 (C #에서), " push
eval 스택에 conditionA를 의미 하고 brfalse
그것이 사실이 아닌 경우 다음 세미콜론 후에 문으로 이동하도록 호출 합니다." 지금은 세미콜론이 표시되지 않으므로 지금은이 명령 후 다음 공간으로 점프 오프셋을 설정하고 세미콜론이 나타날 때까지 더 많은 명령을 삽입 할 때 해당 오프셋을 증가시킵니다. 계속 파싱 ...
if(conditionB)
자, 이것은 유사한 IL 작업 쌍으로 구문 분석하고 방금 구문 분석 한 명령 직후에 진행됩니다. 세미콜론이 표시되지 않으므로 이전 명령의 점프 오프셋을 두 명령의 길이만큼 늘리고 (누름을위한 명령과 중단을위한 명령) 계속 찾아야합니다.
doFoo();
좋아, 쉽다. " call
doFoo"입니다. 그리고 내가 보는 세미콜론입니까? 글쎄, 그게 끝이야. 이 두 명령의 길이만큼 두 블록의 점프 오프셋을 늘리고 내가 걱정했던 것을 잊어 버릴 것입니다. 좋아, 계속 ...
else
... 어 오. 보이는 것처럼 간단하지 않습니다. 좋아, 내가 방금하고있는 일을 잊어 버렸지 만, else
이미 본 어딘가에 조건부 브레이크 선언이 있다는 것을 의미합니다. 다시 살펴 보도록하겠습니다. 그렇습니다 brfalse
. 그게 뭐든간에 자, 이제 break
다음 진술로 무조건 필요합니다 . 그 다음에 나오는 진술은 이제 분명히 조건부 브레이크의 목표이므로, 내가 제대로했는지 확인하고 무조건적인 브레이크를 늘릴 것입니다.
doBar();
쉽습니다. " call
DoBar". 세미콜론이 있는데 중괄호를 본 적이 없습니다. 따라서 무조건 조건 break
이 무엇이든 다음 진술로 넘어 가야합니다.
그래서 우리는 무엇을 가지고 있습니까 ... (참고 : 오후 10시이며 비트 오프셋을 16 진수로 변환하거나 이러한 명령으로 함수의 전체 IL 셸을 채우는 느낌이 들지 않으므로 이것은 단지 의사 -IL입니다 일반적으로 바이트 오프셋이있는 행 번호 사용) :
ldarg.1 //conditionA
brfalse <line 6> //jumps to "break"
ldarg.2 //conditionB
brfalse <line 7> //jumps to "call doBar"
call doFoo
break <line 8> //jumps beyond statement in scope
call doBar
<line 8 is here>
글쎄, 그것은 실제로 올바르게 실행됩니다. (대부분의 C 스타일 언어에서와 같이) 규칙 else
이 가장 가까울수록 if
. 실행 네 스팅을 따르도록 들여 쓰기하면 다음과 같이 실행됩니다. 여기서 conditionA가 false 인 경우 스 니펫의 전체 나머지를 건너 뜁니다.
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
... 그러나 외부 if
명령문 과 관련된 중단 break
이 inner 끝의 명령문으로 이동 if
하여 전체 명령문을 넘어 실행 포인터를 가져 오기 때문에 세렌디피티로 수행됩니다. 이것은 불필요한 불필요한 점프이며,이 예제가 더 복잡하면 더 이상 구문 분석하고 토큰 화하면 더 이상 작동하지 않을 수 있습니다.
또한 언어 사양에 매달려 매달려있는 else
것이 첫 번째 if
이고 조건 A가 거짓이면 doBar가 실행되고 조건 A가 참이지만 조건 B가 아닌 경우 doBar가 실행되면 어떻게됩니까?
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
파서는 처음 if
존재했던 것을 잊었으므로이 간단한 파서 알고리즘은 효율적인 코드를 말하지 않는 올바른 코드를 생성하지 않습니다.
이제 파서 는 더 오래 동안 if
s와 else
s 를 기억하기에 충분히 영리 할 수 있지만, 언어 스펙 이 첫 번째와 else
두 개가 if
일치 한 후 단 하나만 일치 if
하면 두 개와 if
일치하는 두 개에 문제가 발생합니다 else
.
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
else
doBaz();
파서는 첫 번째를보고 첫 번째 else
와 일치 if
한 다음 두 번째를보고 "도대체 내가 다시 한 일"모드로 패닉 상태가됩니다. 이 시점에서 파서는 변경 가능한 상태의 많은 코드를 얻었으며 이미 출력 파일 스트림으로 푸시되었습니다.
이러한 모든 문제와 가정에 대한 해결책이 있습니다. 그러나 코드가 똑똑해야 파서 알고리즘의 복잡성이 증가하거나 파서 가이 바보가되도록 허용하는 언어 사양은와 같은 종료 명령문을 요구하는 등의 언어 소스 코드의 상세도를 증가 시키 end if
거나 중첩을 나타내는 괄호를 증가시킵니다. if
명령문에 else
(다른 언어 스타일에서 일반적으로 볼 수 있는) 문장이 있으면 차단합니다 .
이것은 두 if
문장 의 간단한 예일 뿐이며 컴파일러가 내려야 할 모든 결정과 어쨌든 매우 쉽게 엉망이 될 수있는 부분을 살펴 봅니다. 이것은 귀하의 질문에 Wikipedia의 무해한 진술의 세부 사항입니다.