컴파일러는 정확히 유형 오류를 어떻게 복구합니까?


10

필자는 컴파일러의 기본 원리, 기술 및 도구 (제 2 판) ( 제 2 판)의 여러 문서, 기사 및 4.1.4 절 (제 2 판) (일명 "드래곤 북 (Dragon Book)")을 읽었으며 구문 컴파일러 오류 복구 주제에 대해 설명했다. 그러나 현대의 여러 컴파일러를 실험 한 결과 시맨틱 오류뿐만 아니라 구문 오류 도 복구 합니다.

문법적으로 관련된 오류를 복구하는 컴파일러의 알고리즘과 기술을 상당히 잘 이해하고 있지만 컴파일러가 의미 론적 오류를 복구하는 방법을 정확하게 이해하지 못합니다.

저는 현재 추상 구문 트리에서 코드를 생성하기 위해 약간의 방문자 패턴을 사용하고 있습니다. 컴파일러가 다음 표현식을 컴파일하는 것을 고려하십시오.

1 / (2 * (3 + "4"))

컴파일러는 다음과 같은 추상 구문 트리를 생성합니다.

      op(/)
        |
     -------
    /       \ 
 int(1)    op(*)
             |
          -------
         /       \
       int(2)   op(+)
                  |
               -------
              /       \
           int(3)   str(4)

그런 다음 코드 생성 단계에서는 방문자 패턴을 사용하여 추상 구문 트리를 재귀 적으로 탐색하고 유형 검사를 수행합니다. 컴파일러가 표현식의 가장 안쪽에 올 때까지 추상 구문 트리가 순회됩니다. (3 + "4"). 그런 다음 컴파일러는 표현식의 각 측면을 확인하고 의미 상 동등하지 않은 것을 확인합니다. 컴파일러에서 형식 오류가 발생합니다. 여기에 문제가 있습니다. 이제 컴파일러는 무엇을해야합니까 ?

컴파일러가이 오류를 복구하고 표현식의 외부 부분을 계속 유형 검사하려면 표현식 의 가장 안쪽 부분을 평가하여 표현식의 다음 가장 안쪽 부분으로 일부 유형 ( int또는 str) 을 리턴해야합니다 . 그러나 단순히 반환 할 유형이 없습니다 . 타입 에러가 발생했기 때문에 타입이 추론되지 않았습니다.

내가 가정 한 한 가지 가능한 해결책은 유형 오류가 발생하면 오류가 발생하고 유형 오류가 발생했음을 나타내는 특수 값이 이전 추상 구문 트리 탐색 호출로 반환되어야한다는 것입니다. 이전 순회 호출에이 값이 있으면 추상 구문 트리에서 유형 오류가 더 많이 발생했음을 알고 유형을 추론하려고 시도하지 않아야합니다. 이 방법은 효과가있는 것처럼 보이지만 매우 비효율적 인 것 같습니다. 표현식의 가장 안쪽 부분이 추상 구문 트리에 깊은 경우 컴파일러는 실제 작업을 수행 할 수 없다는 것을 깨닫기 위해 많은 재귀 호출을 수행해야하며 각 작업에서 간단히 반환해야합니다.

위에서 설명한 방법이 사용 되었습니까? 그렇다면 효율적이지 않습니까? 그렇지 않은 경우 컴파일러가 의미 적 오류에서 복구 할 때 사용되는 방법은 정확히 무엇입니까?


3
그것이 사용되는 것을 확신하십시오. 왜 그것이 충분히 효율적이라고 생각하지 않습니까? 형식 검사를 수행하려면 컴파일러는 어쨌든 전체 트리를 걸어야합니다 . 의미 적 실패는 오류가 발견되면 컴파일러가 분기를 제거 할 수있게하므로보다 효율적입니다.
Telastyn

답변:


8

제안한 아이디어는 본질적으로 정확합니다.

핵심은 AST 노드의 유형이 한 번만 계산 된 다음 저장된다는 것입니다. 유형이 다시 필요할 때마다 저장된 유형을 검색합니다. 해결이 오류로 끝나면 오류 유형이 대신 저장됩니다.


3

흥미로운 접근법 중 하나는 특별한 유형의 오류를 갖는 것입니다. 이러한 오류가 처음 발생하면 진단이 기록되고 오류 유형이 표현식 유형으로 리턴됩니다. 이 오류 유형에는 몇 가지 흥미로운 속성이 있습니다.

  • 수행 된 모든 조작이 성공합니다 (동일한 원래 결함으로 인해 발생하는 일련의 오류 메시지를 방지하기 위해)
  • 오류 유형의 객체에서 수행 된 모든 작업의 ​​결과에도 오류 유형이 있습니다.
  • 오류 유형이 코드 생성에 도달하면 코드 생성기는 사용을 파악하고 실패한 코드를 생성합니다 (예 : 예외 발생, 중단 또는 사용자 언어에 적합한 것)

이 조합을 사용하면 실제로 유형 오류가 포함 된 코드를 성공적으로 컴파일 할 수 있으며 해당 코드가 실제로 사용되지 않는 한 런타임 오류가 발생하지 않습니다. 예를 들어, 영향을받지 않는 코드 부분에 대해 단위 테스트를 실행하는 데 유용 할 수 있습니다.


답변 Jules에 감사드립니다. 충분히 웃긴, 이것이 내가 사용한 정확한 방법입니다. 좋은 생각은 다 비슷해 ;-)
Christian Dean

2

의미 오류가있는 경우이를 나타내는 컴파일 오류 메시지가 사용자에게 발행됩니다.

입력이 완료되면 입력 프로그램에 오류가있어 컴파일을 중단해도됩니다. 언어로 된 법적 프로그램이 아니기 때문에 간단히 거부 할 수 있습니다.

그러나 그것은 매우 가혹하기 때문에 더 부드러운 대안이 있습니다. 코드 생성 및 출력 파일 생성을 중단하면서 더 많은 오류가 있는지 계속 확인하십시오.

예를 들어, 현재 표현식 트리에 대한 추가 유형 분석을 중단하고 후속 명령문에서 표현식 처리를 계속할 수 있습니다.


2

언어에서 정수를 추가 할 수 있고 문자열을 +연산자 와 연결할 수 있다고 가정 해 봅시다 .

때문에 int + string허용되지되면, 평가하는 것은 +오류가보고되고 발생합니다. 컴파일러 는 유형으로 반환 할 수 있습니다 error. 또는 더 현명 int + int -> int하고 string + string -> string허용 될 수 있으므로 "오류, int 또는 string 일 수 있음"을 반환 할 수 있습니다.

그런 다음 *운영자 가 와서 int + int허용되는 것으로 가정합니다 . 컴파일러는 다음을 결정할 수 +실제로 반환했는데 int,하고 반환 유형은 *다음이 될 것이다 int오류 메시지없이.


@gnasher 님을 팔로우한다고 생각하지만 ""연산자 가 정확히 무슨 뜻 입니까? 오타 였어?
Christian Dean

@ChristianDean 따옴표에는 별표가있어 렌더링 대신 마크 다운 마크 업으로 해석됩니다.
JakeRobb

내 편집 내용을 검토하는 즉시 문제를 해결할 수 있도록 답변에 수정 사항을 제출했습니다.
JakeRobb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.