스캐너리스 파싱은 "Dangling Else Problem"과 어떤 관련이 있습니까?


13

Dangling Else 문제에 대한 Wikipedia 기사 에서이 문장을 이해하지 못합니다 .

[Dangling Else 문제]는 종종 컴파일러 구성, 특히 스캐너리스 구문 분석에서 발생하는 문제입니다.

스캐너리스 파싱 기술이 어떻게이 문제를 악화시킬 수 있는지 설명해 줄 수 있습니까? 문법에 문제가있는 것 같습니다. 모호하기 때문에 구문 분석 기술을 선택하는 것이 아닙니다. 내가 무엇을 놓치고 있습니까?


2
내가 생각할 수있는 유일한 것은 스캐너가없는 파서가 더 복잡한 문법을 ​​필요로하므로 모호성을 해결하기위한 휴리스틱을 제공하기가 더 어렵다는 것입니다.
조르지오

3
@Robert Harvey : 요점은이 가정이 구문 트리에 의해 반영되어야한다는 것입니다. 문법이 문자열에 대해 두 개의 다른 구문 트리를 파생시킬 수 있다면 if a then if b then s1 else s2문법은 모호합니다.
조르지오

1
@RobertHarvey 언어를 정의하는 일반적인 방법은 컨텍스트가없는 문법과 필요한 경우 문법을 명확하게하는 규칙을 사용하는 것입니다.

2
모든 스캐너리스 파서가 동일한 것은 아닙니다. 예를 들어, PEG 또는 GLR의 경우, 매달려있는 다른 동작은 항상 예측 가능합니다.
SK- 로직

1
[Dangling Else 문제]는 스캐너없는 구문 분석과 관련이 없습니다. [Dangling Else 문제]는 LR (bottom up) 파서의 shift-reduce 작업과 관련이 있습니다. AFAIK
ddur

답변:


6

가장 좋은 추측은 Wikipedia 기사의 문장이 E. Visser 작업에 대한 오해에서 비롯된 것입니다.

스캐너가없는 파서의 문법 (즉, 문자열을 개별적으로 문자열로 설명하는 토큰이있는 일련의 토큰 대신 언어를 일련의 문자로 설명하는 문법)은 모호한 경향이 있습니다. E. 스캐너리스 일반화 LR 파서 (*) Visser paper 명확성 필터 (*)는 모호성을 해결하기위한 몇 가지 메커니즘을 제안하며, 그 중 하나는 매달려있는 다른 문제를 해결하는 데 유용합니다. 그러나이 논문은 "dangling else problem"이라는 정확한 모호성이 스캐너리스 파서와 관련이 있다고 말하지는 않는다 (또한이 메커니즘은 스캐너리스 파서에 특히 유용하지도 않다).

또 다른 모호성 해결 메커니즘 (연산자 우선 순위 및 우선 순위)이 고려 된 파서의 스캐너없는 특성과 전혀 관련이없는 것처럼 보이기 때문에이를 해결하기위한 메커니즘을 암시 적으로 제공하는 것은 아닙니다 (예를 들어, 이러한 모호성은 해당 될 수 없음을 고려하십시오) 중첩으로 인해 일반 문법으로 표시되지만 가장 긴 일치 규칙으로 처리되는 문법은 가능합니다.


(*) 아마도 그들이 E. 프랜트에 의해 또 다른 하나를 참조하는 경우에도 scannerless 파서의 위키 문서의 기재로서의 종이 어느 Scannerless 일반화-LR 파싱 .


13

문제를 설명하기 위해 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 #에서), " pusheval 스택에 conditionA를 의미 하고 brfalse그것이 사실이 아닌 경우 다음 세미콜론 후에 문으로 이동하도록 호출 합니다." 지금은 세미콜론이 표시되지 않으므로 지금은이 명령 후 다음 공간으로 점프 오프셋을 설정하고 세미콜론이 나타날 때까지 더 많은 명령을 삽입 할 때 해당 오프셋을 증가시킵니다. 계속 파싱 ...

if(conditionB)

자, 이것은 유사한 IL 작업 쌍으로 구문 분석하고 방금 구문 분석 한 명령 직후에 진행됩니다. 세미콜론이 표시되지 않으므로 이전 명령의 점프 오프셋을 두 명령의 길이만큼 늘리고 (누름을위한 명령과 중단을위한 명령) 계속 찾아야합니다.

doFoo();

좋아, 쉽다. " calldoFoo"입니다. 그리고 내가 보는 세미콜론입니까? 글쎄, 그게 끝이야. 이 두 명령의 길이만큼 두 블록의 점프 오프셋을 늘리고 내가 걱정했던 것을 잊어 버릴 것입니다. 좋아, 계속 ...

else

... 어 오. 보이는 것처럼 간단하지 않습니다. 좋아, 내가 방금하고있는 일을 잊어 버렸지 만, else이미 본 어딘가에 조건부 브레이크 선언이 있다는 것을 의미합니다. 다시 살펴 보도록하겠습니다. 그렇습니다 brfalse. 그게 뭐든간에 자, 이제 break다음 진술로 무조건 필요합니다 . 그 다음에 나오는 진술은 이제 분명히 조건부 브레이크의 목표이므로, 내가 제대로했는지 확인하고 무조건적인 브레이크를 늘릴 것입니다.

doBar();

쉽습니다. " callDoBar". 세미콜론이 있는데 중괄호를 본 적이 없습니다. 따라서 무조건 조건 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명령문 과 관련된 중단 breakinner 끝의 명령문으로 이동 if하여 전체 명령문을 넘어 실행 포인터를 가져 오기 때문에 세렌디피티로 수행됩니다. 이것은 불필요한 불필요한 점프이며,이 예제가 더 복잡하면 더 이상 구문 분석하고 토큰 화하면 더 이상 작동하지 않을 수 있습니다.

또한 언어 사양에 매달려 매달려있는 else것이 첫 번째 if이고 조건 A가 거짓이면 doBar가 실행되고 조건 A가 참이지만 조건 B가 아닌 경우 doBar가 실행되면 어떻게됩니까?

if(conditionA)
    if(conditionB)
       doFoo();
else
   doBar();

파서는 처음 if존재했던 것을 잊었으므로이 간단한 파서 알고리즘은 효율적인 코드를 말하지 않는 올바른 코드를 생성하지 않습니다.

이제 파서 는 더 오래 동안 ifs와 elses 를 기억하기에 충분히 영리 할 수 있지만, 언어 스펙 이 첫 번째와 else두 개가 if일치 한 후 단 하나만 일치 if하면 두 개와 if일치하는 두 개에 문제가 발생합니다 else.

if(conditionA)
    if(conditionB)
       doFoo();
    else
       doBar();
else
    doBaz();

파서는 첫 번째를보고 첫 번째 else와 일치 if한 다음 두 번째를보고 "도대체 내가 다시 한 일"모드로 패닉 상태가됩니다. 이 시점에서 파서는 변경 가능한 상태의 많은 코드를 얻었으며 이미 출력 파일 스트림으로 푸시되었습니다.

이러한 모든 문제와 가정에 대한 해결책이 있습니다. 그러나 코드가 똑똑해야 파서 알고리즘의 복잡성이 증가하거나 파서 가이 바보가되도록 허용하는 언어 사양은와 같은 종료 명령문을 요구하는 등의 언어 소스 코드의 상세도를 증가 시키 end if거나 중첩을 나타내는 괄호를 증가시킵니다. if명령문에 else(다른 언어 스타일에서 일반적으로 볼 수 있는) 문장이 있으면 차단합니다 .

이것은 두 if문장 의 간단한 예일 뿐이며 컴파일러가 내려야 할 모든 결정과 어쨌든 매우 쉽게 엉망이 될 수있는 부분을 살펴 봅니다. 이것은 귀하의 질문에 Wikipedia의 무해한 진술의 세부 사항입니다.


1
흥미롭지 만 Wikipedia 기사의 의도와는 거리가 멀습니다. 스캐너가없는 항목을 통해 첫눈에 내용이 귀하의 설명과 호환되지 않는 Eelco Visser의 보고서를 참조합니다.
AProgrammer

3
답변 주셔서 감사하지만 실제로 OP를 다루지는 않습니다. 나는 스캐너리스 파서의 목표가 무엇이며 어떻게 구현되는지에 대한 게시물의 가정에 동의하지 않습니다. 스캐너없는 파서를 구현하는 방법은 여러 가지가 있으며이 게시물은 제한된 하위 집합 만 다루는 것 같습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.