나는 문맥을 분석하고 의도 한 것을 추론하여 구문 오류를 수정하려고 시도하는 컴파일러가 있다고 들었습니다.
그러한 컴파일러가 실제로 존재합니까? 분명히 그것은 실질적인 가치는 거의 없지만, 놀고 배우는 것은 매우 흥미로울 것입니다.
나는 문맥을 분석하고 의도 한 것을 추론하여 구문 오류를 수정하려고 시도하는 컴파일러가 있다고 들었습니다.
그러한 컴파일러가 실제로 존재합니까? 분명히 그것은 실질적인 가치는 거의 없지만, 놀고 배우는 것은 매우 흥미로울 것입니다.
답변:
어떤 의미에서 컴파일하는 행위가 되는 특정 구문을 수행하는 것을 의미하고, 컴파일러는 그것을 알아낼 수없는 경우에 따라서 구문 오류가 있는지 추론. 더 많은 "추측"을 추가하여 컴파일러가 더 많은 것을 추론하고 구문을 더 유연하게 만들 수 있지만 특정 규칙 세트로 추론해야합니다. 그런 다음 이러한 규칙은 언어의 일부가되며 더 이상 오류가 아닙니다.
따라서 아닙니다. 질문은 이해가되지 않기 때문에 실제로 그러한 컴파일러는 없습니다. 어떤 규칙 집합에 따라 구문 오류가 무엇인지 추측하면 구문의 일부가됩니다.
그런 의미에서이를 수행하는 컴파일러의 좋은 예가 있습니다. 모든 C 컴파일러. 그들은 종종 그렇지 않은 것에 대한 경고를 인쇄 한 다음 X를 의미한다고 가정하고 계속합니다. 이것은 실제로 명확하지 않은 코드 (대부분 구문 자체는 아니지만)를 "추측"하는 것입니다. 오류로 인해 컴파일을 중지했을 수도 있으므로 오류로 간주됩니다.
정말 위험한 것 같습니다. 컴파일러가 의도를 추론하려고 시도하고, 잘못 추론하고, 코드를 수정 한 다음, 알려주지 않거나 (또는 모두와 마찬가지로 무시하라는 경고 메시지를 표시하면) 코드를 실행하려고합니다. 심각하게 약간의 손상을 입 힙니다.
이와 같은 컴파일러는 의도적으로 생성되지 않은 것일 수 있습니다.
오늘날 프로그래밍 언어를위한 IDE는 일반적으로 백그라운드에서 컴파일러를 실행하고 있으므로 구문 색상, IntelliSense, 오류 등과 같은 분석 서비스를 제공 할 수 있습니다. 분명히 그러한 컴파일러는 코드가 심하게 손상 될 수 있어야합니다. 편집 할 때 대부분 코드가 올바르지 않습니다. 그러나 우리는 여전히 그것을 이해해야합니다.
그러나 일반적으로 오류 복구 기능은 편집 중에 만 사용됩니다. "주요"시나리오에서 실제로 컴파일 할 수 있도록하는 것은 이치에 맞지 않습니다.
흥미롭게도, 우리는이 기능을 JScript.NET 컴파일러에 구축했습니다. 기본적으로 컴파일러가 오류가 발생하더라도 IDE가 복구 된 경우 컴파일러가 진행할 수있는 모드로 컴파일러를 넣을 수 있습니다. Visual Basic 코드를 입력 하고 JScript.NET 컴파일러를 실행하면 작동중인 프로그램이 다른 쪽에서 나올 수 있습니다.
이 데모는 재미있는 데모이지만 여러 가지 이유로 "주요"시나리오에 적합한 기능은 아닙니다. 전체 설명은 꽤 길다. 간단한 설명은 작업 프로그램을 만드는 것입니다 예측할 과 사고 , 그리고 힘든 여러 컴파일러, 또는 같은 컴파일러의 여러 버전을 통해 동일한 코드를 실행할 수 있습니다. 기능이 추가하는 큰 비용은 작은 혜택으로 정당화되지 않습니다.
이날 PM의 기능을 담당했던 Peter Torr 는 2003 년 블로그 게시물에서이 기능에 대해 간략하게 설명합니다 .
우리는 JScript .NET 엔진의 스크립트 호스팅 API를 통해이 기능을 공개하지만이 기능을 사용한 실제 고객은 모릅니다.
내 생각에 가장 먼저 오는 것은 Javascript의 자동 세미콜론 삽입 입니다. 언어로 들어 가지 않은 끔찍하고 끔찍한 기능.
그것은 더 나은 일을 할 수 없었다고 말하는 것이 아닙니다. 다음 줄을 살펴보면 프로그래머의 의도에 대해 더 잘 추측 할 수 있지만 하루가 끝날 때 구문 이 사라질 수있는 여러 가지 유효한 방법이 있다면 실제로 대체 할 방법 이 없습니다. 프로그래머가 명시 적입니다.
컴파일러가 잘못된 구문을 수정할 수 있으면 해당 구문을 언어로 문서화해야한다는 말이 들립니다.
구문 오류의 이유는 구문 분석기가 프로그램에서 추상 구문 트리를 작성할 수 없기 때문입니다. 이것은 토큰이 제자리에 없을 때 발생합니다. 해당 토큰이 어디에 있어야하는지, 토큰을 제거해야하는지, 또는 오류를 수정하기 위해 다른 토큰을 추가해야한다면 프로그래머의 의도를 추측 할 수있는 일종의 컴퓨터가 필요합니다. 기계가 어떻게 추측 할 수 있습니까?
int x = 5 6;
하기로되어 있었다 :
int x = 5 + 6;
그냥 쉽게 다음 중 하나가 될 수있다 : 56
, 5 - 6
, 5 & 6
. 컴파일러가 알 방법이 없습니다.
그 기술은 아직 존재하지 않습니다.
똑같은 것은 아니지만 HTML이 재난으로 변한 이유입니다. 브라우저는 잘못된 마크 업을 견뎌냈으며 다음으로 아는 것, 브라우저 A는 브라우저 B와 같은 방식으로 렌더링 할 수 없었습니다. ).
에릭 리퍼 (Eric Lippert)가 유추 한 것처럼, 이러한 것들 중 다수는 컴파일러가 아닌 IDE에 의해 가장 잘 처리됩니다. 자동 비트가 당신을 위해 무엇을 시도하고 있는지 보자.
필자가 지금 생각하는 전략은 컴파일러를 풀지 않고 지속적으로 언어를 수정하는 것입니다. 만약 그것이 컴파일러가 자동으로 알아낼 수있는 것이라면 잘 정의 된 언어 구성을 소개합니다.
기억해야 할 즉각적인 예는 C #의 자동 속성입니다 (유사한 언어는 아님). 어떤 앱에서든 getter / setter의 대부분은 실제로 필드 주위를 감싸는 것이므로 개발자가 의도하고 컴파일러가 나머지를 주입하게하십시오.
그러면 생각하게됩니다. 대부분의 C 스타일 언어는 이미 어느 정도 이것을 수행합니다. 자동으로 알아낼 수있는 것이라면 구문을 수정하십시오.
if (true == x)
{
dothis();
}
else
{
dothat();
}
다음과 같이 줄일 수 있습니다 :
if (true == x)
dothis();
else
dothat();
결국, 나는 이것이 다음과 같은 것이라고 생각합니다. 트렌드는 컴파일러를 "더 똑똑하게"만들거나 "더 느슨하게"만들지 않는 것입니다. 그것은의 언어 영리 또는 패자가된다.
게다가 고전적인 "if"버그와 같이 너무 많은 "도움말"은 위험 할 수 있습니다.
if (true == x)
if (true == y)
dothis();
else
dothat();
if (x && y) dothis(); else dothat();
약간 나아 보일 것입니다.
true
또는 false
.
DEC와 IBM 미니 컴퓨터 및 메인 프레임 시스템에서 80 년대 후반과 90 년대 초반에 FORTRAN과 PL / I를 다시 코딩 할 때 컴파일러는 "blah blah 오류; blah blah를 가정하고 계속한다고 가정 할 때"라는 메시지를 정기적으로 로그 아웃한다는 것을 기억합니다. ". 그 당시에는 배치 처리와 펀칭 카드의 며칠 전의 유산이 있었으며 코드를 제출하고 결과를 다시 얻는 것 사이에 막대한 대기가 있었을 때 카드가 펀치되었습니다. 따라서 컴파일러가 프로그래머를 추측하고 첫 번째 실수를 중단하지 않고 계속 수행하려고 시도하는 것이 합리적이었습니다. "정정"이 특히 정교하다는 것을 기억하지 못합니다. 대화식 Unix 워크 스테이션 (Sun, SGI 등)으로 이동했을 때,
컴파일러의 목표는 원하는대로 작동하는 실행 파일을 생성하는 것입니다. 프로그래머가 유효하지 않은 것을 쓰면 컴파일러가 의도 한 것을 90 % 확률로 추측 할 수 있더라도 일반적으로 프로그래머가 의도를 명확하게하기 위해 프로그램을 수정하여 컴파일러가 계속해서 실행 파일을 생성하도록하는 것이 좋습니다. 버그를 숨길 가능성이 높습니다.
물론 언어는 의도를 명확하게 나타내는 코드가 합법적 이도록 의도적으로 설계되어야하며, 의도를 명확하게 나타내지 않는 코드는 금지되어야하지만 언어가 그런 의미는 아닙니다. 다음 코드를 고려하십시오 [Java 또는 C #]
const double oneTenth = 0.1;
const float oneTenthF = 0.1f;
...
float f1 = oneTenth;
double d1 = oneTenthF;
f1
프로그래머가 f1
포함 하고 싶은 논리적 인 것 ( float
1/10에 가장 가까운 값)이 하나뿐이기 때문에 할당에 암시 적 타입 캐스트를 컴파일러에 추가하면 도움이됩니다 . 그러나 컴파일러가 부적절한 프로그램을 받아들이도록 권장하는 대신 스펙 이 일부 컨텍스트에서 암시 적 이중 부동 변환을 허용 하는 것이 좋습니다 . 반면에 d1
, 프로그래머가 실제로 의도 한 것이거나 아닐 수도 있지만,이를 금지하는 언어 규칙은 없습니다.
최악의 종류의 언어 규칙은 무언가 합법적으로 컴파일 할 수없는 경우에 컴파일러가 추론하는 규칙이지만 추론이 의도 된 경우 프로그램이 "실수로"유효한 경우입니다. 암시 적 진술 종료와 관련된 많은 상황이이 범주에 속합니다. 두 개의 개별 명령문을 작성하려는 프로그래머가 명령문 종결자를 생략하면 컴파일러는 일반적으로 명령문 경계를 유추 할 수 있지만 때로는 두 개의 것으로 처리되어야하는 것으로 하나의 명령문으로 간주 할 수 있습니다.
구문 오류는 특히 수정하기가 어렵습니다. 잃어버린 권리의 경우를 생각해 보자 )
: 우리는 코드를 삽입함으로써 코드를 고칠 수 있지만, 일반적으로 코드를 삽입하고 구문 적으로 올바른 프로그램을 얻을 수있는 곳이 많다.
훨씬 쉬운 점은 철자가 잘못된 식별자입니다 (단, 구문 오류는 아닙니다). 해결할 수없는 식별자와 범위 내의 모든 식별자들 사이의 편집 거리를 계산할 수 있고, 해결할 수없는 단어를 사용자가 가장 의미하는 단어로 대체함으로써, 많은 경우에 올바른 프로그램을 만들 수있다. 그러나 여전히 오류를 표시하고 IDE가 올바른 대체를 제안하도록하는 것이 좋습니다.
여러 번 시도했지만 HAL 9000 또는 GlaDOS라고 생각하면 원하는 효과를 얻지 못하는 경우가 종종 있습니다.
C에서는 값으로 배열을 전달할 수 없지만 컴파일러에서는 다음을 작성할 수 있습니다.
void foo(int array[10]);
그런 다음 자동으로 다음과 같이 다시 작성됩니다.
void foo(int* array);
얼마나 어리석은가요? 이 특별한 규칙으로 인해 많은 프로그래머가 배열과 포인터가 기본적으로 동일한 것으로 믿게 되었기 때문에 자동 재 작성 대신 하드 오류를 선호합니다. 그들은 아닙니다.