프로그래밍 언어의 어떤 속성으로 컴파일이 불가능합니까?


71

질문:

"프로그래밍 언어의 특정 속성은 코드로 작성된 코드를 실행하는 유일한 방법은 해석에 의한 것입니다. 즉, 전통적인 CPU의 네이티브 머신 코드로 컴파일 할 수 없습니다. 이러한 속성은 무엇입니까?"

컴파일러 : Parag H. Dave와 Himanshu B. Dave의 원칙과 실습 (2012 년 5 월 2 일)

이 책은 그 답에 대한 단서를 제공하지 않습니다. SEBESTA (Concepts of Programming Languages)에 대한 답을 찾으려고했지만 아무 소용이 없습니다. 웹 검색도 거의 쓸모가 없었습니다. 실마리가 있습니까?


31
유명한 펄은 파싱조차 할 수 없다 . 그 외에는 추가 가정없이 주장이 사소하게 잘못 된 것 같습니다. 인터프리터가있는 경우 항상 하나의 실행 파일 인 voila에 인터프리터와 코드를 묶을 수 있습니다.
Raphael

4
@Raphael : 좋은 생각이지만 ... 1) 코드를 실행하기 전에 사용할 수 있다고 가정합니다. 대화식으로 사용하는 것은 아닙니다. 물론 적시 컴파일을 사용하여 bash 문 또는 PostScript 스택 내용의 네이티브 코드를 사용할 수는 있지만 정말 미친 생각입니다. 2) 귀하의 아이디어는 실제로 코드를 컴파일하지 않습니다. 번들은 컴파일 된 코드 버전이 아니라 코드의 해석기입니다.
reinierpost

7
좋은 옛날에는 gwbasic 프로그램 (gwbasic은 기본 프로그램을 일종의 바이트 코드로 저장합니다)을 자체 편집했습니다. 나는 현재 자신을 편집 할 수있는 능력을 유지하면서 네이티브 머신 코드로 컴파일하는 현명한 방법을 생각할 수 없습니다.
PlasmaHH

15
@PlasmaHH : 자체 수정 코드 는 1948 년으로 거슬러 올라갑니다. 첫 번째 컴파일러는 1952 년에 작성되었습니다. 자체 수정 코드의 개념은 기본 기계 코드에서 발명 되었습니다.
Mooing Duck

10
@reinierpost Raphael은이 문제에 대한 이론적 입장을 취하고 있습니다. 그것은 질문의 개념적 한계를 보여주는 장점이 있습니다. 컴파일은 언어 S에서 언어 T 로의 변환입니다. 언어 T는 다른 언어의 해석 코드를 추가 할 수있는 S의 확장 일 수 있습니다. 번들링 S와 그 해석기는 언어 T의 프로그램입니다. 엔지니어에게는 어리석은 것처럼 보이지만 질문을 의미있게 공식화하는 것은 쉽지 않음을 보여줍니다. 엔지니어링 관점에서 수용 가능한 컴파일 프로세스와 허용되지 않는 프로세스 (예 : Raphael)를 어떻게 구별합니까?
babou

답변:


61

해석 된 코드와 컴파일 된 코드의 차이점은 아마도 Raphael의 주석에서 강조한 것처럼 허구 일 것입니다 .

the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...

사실 코드는 항상 소프트웨어, 하드웨어 또는 두 가지의 조합으로 해석되며 컴파일 과정에서 코드가 어떤 것인지 알 수 없습니다.

컴파일로 인식하는 것은 한 언어 (소스)에서 다른 언어 (대상) 로의 변환 프로세스입니다 . 그리고 의 해석기 는 일반적으로 의 해석기와 다릅니다 .STST

컴파일 된 프로그램은 하나의 구문 형태에서 번역 다른 구문 형태에 , 등, 그 언어의 의도 된 의미를 부여 와 , 및 당신이 일반적으로하려고하는 몇 가지까지 같은 연산 동작을 복잡성 또는 단순한 효율성 (시간, 공간, 표면, 에너지 소비)과 같은 최적화를 위해 변경 될 수 있습니다. 정확한 정의가 필요하기 때문에 기능적 동등성에 대해 이야기하지 않습니다.PSPTSTPSPT

일부 컴파일러는 실제로 "실행"을 향상시키지 않고 코드 크기를 줄이기 위해 단순히 사용되었습니다. 이것은 플라톤 시스템 에서 사용되는 언어의 경우입니다 (컴파일 링이라고 부르지는 않았지만).

컴파일 프로세스 후에 더 이상 의 인터프리터가 필요하지 않으면 코드가 완전히 컴파일 된 것으로 간주 할 수 있습니다 . 적어도 이것이 이론적 질문보다는 공학적 질문 으로 귀하의 질문을 읽을 수있는 유일한 방법입니다 (이론적으로는 항상 통역사를 재구성 할 수 있기 때문에).S

문제를 일으킬 수있는 것 중 하나는 메타 순환 입니다. 프로그램이 자체 소스 언어 로 구문 구조를 조작 하여 프로그램 프래그먼트를 작성하여 마치 원래 프로그램의 일부인 것처럼 해석되는 경우입니다. 당신이 언어에 임의의 프로그램 조각을 생성 할 수 있기 때문에 의미 구문 조각을 조작하는 임의의 계산의 결과로, 당신이 (공학적인 관점에서)이 거의 불가능 만들 수있는 언어로 프로그램을 컴파일 할 거라 생각 있도록, 이제 조각을 생성 합니다. 따라서 대한 인터프리터 가 필요하거나 적어도 에서 컴파일러 로SSTTSST 에서 생성 된 프래그먼트의 온더 플라이 컴파일경우 입니다 ( 이 문서 참조 ).S

그러나 이것이 어떻게 올바르게 공식화 될 수 있는지 잘 모르겠습니다 (지금은 시간이 없습니다). 그리고 공식화되지 않은 문제에 대한 큰 단어는 불가능 합니다.

추가 사항

36 시간 후에 추가되었습니다. 이 매우 긴 속편을 건너 뛸 수 있습니다.

이 질문에 대한 많은 의견은 문제에 대한 두 가지 견해를 보여줍니다. 문제를 의미가없는 것으로 보는 이론적 견해와 불행히도 그렇게 쉽게 공식화되지 않은 공학적 견해.

해석과 편집을 보는 방법에는 여러 가지가 있으며 몇 가지를 스케치하려고합니다. 내가 관리 할 수있는 한 비공식적으로 노력할 것이다

묘비 다이어그램

초기 공식화 (1990 년대 후반에 1960 년대 초) 중 하나는 T 또는이다 묘비도 . 이 다이어그램은 인터프리터 또는 컴파일러의 구현 언어, 해석 또는 컴파일되는 소스 언어 및 컴파일러의 경우 대상 언어를 작성 가능한 그래픽 요소로 표시합니다. 보다 정교한 버전은 속성을 추가 할 수 있습니다. 이 그래픽 표현은 공리, 추론 규칙으로 볼 수 있으며, 공리에서 존재한다는 증거, 즉 Curry-Howard에서 프로세서 생성을 기계적으로 도출하는 데 사용할 수 있습니다.

부분 평가

또 다른 흥미로운 관점은 부분 평가 패러다임입니다. 입력 데이터가 주어지면 답을 계산하는 일종의 함수 구현으로 프로그램을 간단하게 볼 수 있습니다. 통역자는 언어에 대한 프로그램 수행하는 프로그램이다 작성된 와 데이터 해당 프로그램을, 그리고 의미에 따른 계산 결과 . 일부 평가는 두 인수의 프로그램을 전문으로하는 기술이다 및 , 단 하나의 인수는, 말할 때 , 알려져있다. 의도는 마지막으로 두 번째 인수를 얻을 때 더 빠른 평가를하는 것입니다ISSpSSdSa1a2a1a2 . 경우에 특히 유용합니다 보다 더 자주 변경 와 부분 평가의 비용으로 경우에만 모든 계산을 상각 할 수있다 변화하고있다.a2a1a1a2

이는 데이터의 좀 더 정적 인 부분이 사전 처리되는 알고리즘 설계 (종종 SE-CS에 대한 첫 번째 의견 주제)에서 빈번한 상황이므로 모든 응용 프로그램에서 사전 처리 비용을 상각 할 수 있습니다. 입력 데이터의 가변 부분이 많은 알고리즘

첫 번째 인수는 실행될 프로그램이며 일반적으로 다른 데이터로 여러 번 실행되거나 하위 데이터가 다른 데이터로 여러 번 실행되므로 이는 인터프리터의 상황이기도합니다. 따라서 주어진 프로그램을 첫 번째 논거로이 프로그램에서 부분적으로 평가함으로써 주어진 프로그램의 빠른 평가를 위해 통역사를 전문화하는 것이 당연한 생각이되었습니다. 이것은 프로그램을 컴파일하는 방법으로 여겨 질 수 있으며 첫 번째 (프로그램) 논거에서 통역사의 부분 평가를 통해 컴파일에 대한 중요한 연구 작업이 진행되었습니다.

Smn 정리

부분 평가 접근법에 대한 좋은 점은 이론, 특히 Kleene의 Smn 정리에 뿌리를두고 있다는 것 입니다. 나는 순수한 이론가들을 화나게하지 않기를 희망하면서 그것을 직관적으로 제시하려고 노력하고 있습니다.

재귀 함수 의 Gödel 번호 매기기 가 주어지면 를 하드웨어로 볼 수 있으므로 프로그램 의 Gödel 번호 (읽기 객체 코드 ) 가 주어지면 는 의해 정의 된 함수입니다 (예 : 객체 코드로 계산) 하드웨어).φφpφpp

가장 간단한 형태로 정리는 다음과 같이 위키피디아에 표시됩니다 (소수의 표기법이 약간 변경됨).

괴델 넘버링 감안 재귀 함수의 원시적 재귀 함수가 추종성 두 인자는 : 모든 괴델 번호에 대한 일부 계산 가능한 함수 두 인수의 표정 및 는 자연수 와 의 동일한 조합에 대해 정의 되며 해당 값은 해당 조합에 대해 동일합니다. 다시 말해, 모든 대해 다음과 같은 확장 기능이 동일합니다 . φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)λy.φq(x,y).

이제 를 인터프리터 , 를 프로그램 의 소스 코드로 , 를 해당 프로그램 의 데이터 로 사용하면 다음 과 같이 쓸 수 있습니다. qISxpSydφσ(IS,pS)λd.φIS(pS,d).

φIS 는 하드웨어 에서 인터프리터 를 실행하는 것으로 , 즉 언어 작성된 프로그램을 해석 할 수있는 블랙 박스로 볼 수 있습니다 .ISS

함수 통역자 전문 함수로 간주 될 수있다 프로그램의 부분적으로 평가. 따라서 Gödel) 번호 는 프로그램 의 컴파일 된 버전 인 객체 코드를 가지고 있음을 알 수 있습니다 .σISPSσ(IS,pS)pS

따라서 함수 는 언어 작성된 프로그램 의 소스 코드를 인수로 해당 프로그램의 객체 코드 버전을 반환 하는 함수로 볼 수 있습니다 따라서 는 보통 컴파일러라고합니다.CS=λqS.σ((IS,qS)qSSCS

몇 가지 결론

그러나 내가 말했듯이 "이론은 거짓말 쟁이가 될 수있다"거나 실제로는 하나 인 것처럼 보인다. 문제는 함수를 전혀 모른다는 것입니다 . 실제로 그러한 기능이 많이 있으며, 내 생각에 정리 증명은 매우 간단한 정의를 사용할 수 있으며 엔지니어링 관점에서 Raphael이 제안한 솔루션보다 더 나을 수도 있습니다. 소스 코드 인터프리터와 . 이 작업은 항상 수행 할 수 있으므로 컴파일이 항상 가능합니다.q S I SσqSIS

컴파일러에 대한보다 제한적인 개념을 공식화하려면보다 미묘한 이론적 접근이 필요합니다. 나는 그 방향으로 무엇을했는지 모른다. 부분 평가에 대한 실제 작업은 엔지니어링 관점에서보다 현실적입니다. 물론 Curry-Howard isomorphism을 기반으로 형식 이론의 맥락에서 개발 된 사양 증명에서 프로그램 추출을 포함하여 컴파일러를 작성하는 다른 기술도 있습니다 (그러나 저는 제 도메인 외부로 나가고 있습니다) .

여기에서의 나의 목적은 라파엘의 말이 "미친"것이 아니라 일이 명백하지 않고 단순하지도 않다는 것을 상기시켜 준다는 것입니다. 불가능하다고 말하는 것은 그것이 어떻게 그리고 왜 불가능한 지를 정확하게 이해하기 위해서는 정확한 정의와 증거를 요구하는 강력한 진술입니다 . 그러나 그러한 증거를 표현하기 위해 적절한 공식화를 구축하는 것은 매우 어려울 수 있습니다.

이는 특정 기능을 컴파일 할 수 없더라도 엔지니어가 이해한다는 의미에서 Gilles의 답변에서 언급 한 것처럼 이러한 기능을 사용하지 않는 프로그램의 일부에는 표준 컴파일 기술을 항상 적용 할 수 있습니다.

언어에 따라 컴파일 타임에 어떤 작업이 수행되고 런타임에 다른 작업이 수행되어야하므로 특정 코드가 필요하다는 Gilles의 주요 설명을 따르기 위해 컴파일 개념이 실제로 있음을 알 수 있습니다 잘못 정의되어 있으며 아마도 만족스러운 방식으로 정의 할 수 없을 것입니다. 컴파일은 일부 알고리즘에서 정적 데이터 전처리와 비교할 때 부분 평가 섹션 에 표시하려고 시도한 최적화 프로세스 일뿐 입니다.

복잡한 최적화 프로세스로서 컴파일 개념은 실제로 연속체에 속합니다. 언어 또는 프로그램의 특성에 따라 일부 정보를 정적으로 사용할 수 있으며 더 나은 최적화가 가능합니다. 다른 것들은 런타임에 연기되어야합니다. 상황이 정말 나빠지면 적어도 프로그램의 일부 부분에 대해 런타임에 모든 작업을 수행해야하며 소스 코드를 인터프리터와 번들링하면됩니다. 따라서이 번들링은이 컴파일 연속체의 최저 수준 일뿐입니다. 컴파일러에 대한 많은 연구는 동적으로 수행되었던 작업을 정적으로 수행하는 방법을 찾는 것입니다. 컴파일 타임 가비지 콜렉션이 좋은 예입니다.

컴파일 과정에서 머신 코드를 생성해야한다는 말은 도움이되지 않습니다. 인터프리터가 기계 코드이기 때문에 번들링으로 할 수있는 일입니다 (물론, 크로스 컴파일로 조금 더 복잡해질 수 있습니다).


3
" 불가능한 것은 큰 단어입니다" 매우 큰 단어입니다. =)
Brian S

3
실행중인 프로그램이 첫 번째 입력을 받기 전에 완전히 수행되는 일련의 단계 및 프로그램의 추상 기계 모델의 일부가 아닌 수단을 통해 데이터 제어 프로그램 흐름을 갖는 프로세스로 해석 하는 "컴파일"을 정의하는 경우 그런 다음 언어를 컴파일하려면 컴파일러가 실행을 시작하기 전에 언어 구조가 가질 수있는 모든 가능한 의미를 식별 할 수 있어야합니다. 언어 구성이 무한한 의미를 가질 수있는 언어에서는 컴파일이 작동하지 않습니다.
supercat

@BrianS 아니요, 그렇지 않습니다. 그렇지 않으면 증명할 수 없습니다.)
Michael Gazonda

@supercat 그것은 여전히 ​​정의가 아닙니다. 언어 구성의 '의미'는 무엇입니까?
Rhymoid

나는 컴파일러 / 인터프리터를 일종의 부분 실행으로 보는 개념을 좋아합니다!
Bergi

17

문제는 실제로 컴파일이 불가능하다는 것이 아닙니다 . 언어를 해석 할 수 있다면 ¹, 통역사와 소스 코드를 묶어 사소한 방식으로 컴파일 할 수 있습니다. 문제는 어떤 언어 기능이 기본적으로 유일한 방법인지 묻는 것입니다.

인터프리터는 소스 코드를 입력으로 사용하고 해당 소스 코드의 의미로 지정된대로 동작하는 프로그램입니다. 인터프리터가 필요한 경우 언어에 소스 코드를 해석하는 방법이 포함되어 있음을 의미합니다. 이 기능을이라고 eval합니다. 통역사는 언어의 런타임 환경의 일부로서 요구되는 경우, 그것은 언어가 포함되어 있음을 의미합니다 eval: 하나 eval원시적으로 존재하거나이 어떤 방법으로 인코딩 할 수 있습니다. 스크립팅 언어로 알려진 언어에는 일반적으로 eval대부분의 Lisp 언어 와 마찬가지로 기능이 포함됩니다 .

언어에 포함되어 eval있다고해서 대부분의 언어 를 네이티브 코드로 컴파일 할 수 없다는 의미는 아닙니다. 예를 들어, 좋은 네이티브 코드를 생성하고 그럼에도 불구하고 지원하는 최적화 된 Lisp 컴파일러가 있습니다 eval. eval'코드는 해석되거나 즉시 컴파일 될 수 있습니다.

eval통역사가 필요로하는 최고의 기능이지만 통역사가 부족한 다른 기능도 있습니다. 컴파일러의 몇 가지 일반적인 단계를 고려하십시오.

  1. 파싱
  2. 타입 검사
  3. 코드 생성
  4. 연결

eval이러한 모든 단계는 런타임에 수행되어야합니다. 네이티브 컴파일을 어렵게하는 다른 기능이 있습니다. 맨 아래에서 가져 오면 일부 언어는 함수 (메서드, 프로 시저 등) 및 변수 (오브젝트, 참조 등)가 로컬이 아닌 코드 변경에 의존 할 수있는 방법을 제공하여 늦은 연결을 권장합니다. 따라서 효율적인 네이티브 코드를 생성하기가 어렵지만 불가능하지는 않습니다. 가상 머신에서 객체 참조를 호출로 유지하고 VM 엔진이 바인딩을 즉시 처리 할 수 ​​있습니다.

일반적으로 리플렉션 은 언어를 네이티브 코드로 컴파일하기 어렵게 만드는 경향이 있습니다. 평가 프리미티브는 극단적 인 리플렉션입니다. 많은 언어가 방법을 나열 이름으로 클래스를 검색의 상속을 검사하는 예제 코드를 허용, 멀리 가고 있지만, 그럼에도 불구하고 가상 머신의 관점에서 정의 된 의미가없는 등의 방법, 전화 자바JVM을C 번호.NET은 두 유명한 예이다. 이러한 언어를 구현하는 가장 간단한 방법은 해당 언어를 바이트 코드 로 컴파일하는 것입니다. 그럼에도 불구하고 고급 리플렉션 기능을 사용하지 않는 최소한의 프로그램 조각을 컴파일하는 기본 컴파일러 (많은 적시 )가 있습니다.

유형 검사는 프로그램이 유효한지 여부를 결정합니다. 언어마다 컴파일 타임과 런타임에 수행되는 분석 양에 대한 표준이 다릅니다. 코드를 실행하기 전에 많은 검사를 수행하는 언어는 "정적 유형"으로, 그렇지 않은 경우 "동적 유형"이라고합니다. 일부 언어에는 동적 캐스트 기능 또는 비 정렬 및 유형 검사 기능이 있습니다. 이러한 기능을 사용하려면 런타임 환경에 유형 검사기를 포함해야합니다. 이는 런타임 환경에 코드 생성기 또는 인터프리터를 포함해야하는 요구 사항과 직교합니다.

¹ 연습 : 해석 할 수없는 언어를 정의하십시오.


(1) 소스 코드를 컴파일하는 것으로 인터프리터를 번들링하는 것에 동의하지 않지만 나머지 게시물은 훌륭합니다. (2) 평가에 전적으로 동의합니다. (3) 리플렉션이 언어를 원시 코드로 컴파일하기 어려운 이유를 모르겠습니다. Objective-C에는 반영이 있으며 일반적으로 컴파일됩니다. (4) 모호하게 관련된 참고 사항 인 C ++ 템플릿 메타 매직은 일반적으로 컴파일 된 후 실행되는 것이 아니라 해석됩니다.
Mooing Duck

방금 나에게 일어난 루아는 컴파일되었습니다. 는 eval단순히 바이트 코드를 컴파일하고 별도의 단계로서 바이너리 바이트 코드를 실행한다. 그리고 컴파일 된 바이너리에 분명히 반영되어 있습니다.
Mooing Duck

Harvard Architecture 시스템에서 컴파일은 "데이터"로 액세스 할 필요가없는 코드를 생성해야합니다. 코드가 아닌 데이터로 저장해야하는 소스 파일의 정보가 실제로 "컴파일"되지 않았다는 사실을 알고 싶습니다. int arr[] = {1,2,5};[1,2,5]를 포함하는 초기화 된 데이터 섹션을 생성하고 생성하는 것과 같은 선언을 취하는 컴파일러에는 아무런 문제가 없지만 [1,2,5]를 기계 코드로 변환하는 것으로 그 동작을 설명하지는 않습니다. 거의 모든 프로그램을 데이터로 저장해야하는 경우 실제로 "컴파일"되는 부분은 무엇입니까?
supercat

2
@supercat 그게 수학자와 컴퓨터 과학자들이 사소한 의미입니다. 수학적 정의에 적합하지만 흥미로운 것은 없습니다.
Gilles

@Gilles : "컴파일"이라는 용어가 기계 명령어로의 번역을 위해 예약되어 있고 (관련된 "데이터"를 유지하지 않고) 컴파일 언어에서이를 수용하는 경우, 어레이 선언의 동작은 어레이를 "컴파일"하지 않습니다. 코드의 의미있는 부분을 컴파일 할 수없는 언어가 있습니다.
supercat

13

저자들은 컴파일이 의미 한다고 가정합니다

  • 소스 프로그램은 런타임에 존재할 필요가 없으며
  • 런타임에 컴파일러 나 인터프리터가 없어도됩니다.

이러한 구성표에 대해 "불가능"하지 않은 경우 문제가되는 몇 가지 샘플 기능은 다음과 같습니다.

  1. 변수 이름 (문자열)으로 변수를 참조하여 런타임에 변수 값을 조사 할 수 있으면 런타임에 변수 이름이 있어야합니다.

  2. 런타임에 함수 / 프로 시저를 호출 할 수있는 경우 해당 이름 (문자열)으로 함수 / 프로 시저를 참조하면 런타임시 함수 / 프로 시저 이름이 필요합니다.

  3. 런타임에 문자열을 사용하여 프로그램을 구성하거나 다른 프로그램을 실행하거나 네트워크 연결 등에서 프로그램을 읽을 수있는 경우 런타임에 인터프리터 또는 컴파일러가 필요합니다. 이 프로그램을 실행하십시오.

Lisp에는 세 가지 기능이 모두 있습니다. 따라서 Lisp 시스템에는 항상 런타임에 인터프리터가로드되어 있습니다. 언어 Java 및 C #에는 런타임시 사용 가능한 함수 이름과 그 의미를 찾아 볼 수있는 테이블이 있습니다. 아마도 Basic 및 Python과 같은 언어는 런타임에 변수 이름을 갖습니다. (나는 그것에 대해 100 % 확신하지 못한다).


"통역사"가 코드로 컴파일되면 어떻게됩니까? 예를 들어 디스패치 테이블을 사용하여 가상 메서드를 호출하면 해석 또는 컴파일의 예입니까?
Erwin Bolwidt

2
"런타임에 컴파일러 나 인터프리터가 없어도됩니다." 그것이 사실이라면, 깊은 의미에서 C는 대부분의 플랫폼에서도 "컴파일"될 수 없습니다. C 런타임은 할 일이 많지 않습니다 : 시작, 스택 설정 등, atexit처리를 위한 종료 . 그러나 여전히 거기에 있어야합니다.
가명

1
"Lisp 시스템에는 항상 런타임에 인터프리터가로드되어 있습니다." – 반드시 그런 것은 아닙니다. 많은 Lisp 시스템에는 런타임에 컴파일러가 있습니다. 어떤 사람들은 심지어 통역을하지도 않습니다.
Jörg W Mittag

2
좋은 시도지만, en.wikipedia.org/wiki/Lisp_machine#Technical_overview . Lisp를 컴파일하고 결과를 효율적으로 실행하도록 설계되었습니다.
피터 A. 슈나이더

@ 가명 : C 런타임은 라이브러리이며 컴파일러 나 인터프리터가 아닙니다.
Mooing Duck

8

현재 답변이 진술 / 답을 "과도하게 생각하고"있을 수 있습니다. 아마도 저자가 언급 한 것은 다음과 같은 현상입니다. 많은 언어에는 "eval"과 같은 명령이 있습니다. 예를 들어 자바 스크립트 평가를 참조하면 그 행동은 일반적으로 CS 이론의 특수한 부분으로 연구됩니다 (예 : Lisp). 이 명령의 기능은 언어 정의 컨텍스트에서 문자열을 평가하는 것입니다. 따라서 사실상 "컴파일러 내장"과 유사합니다. 컴파일러는 런타임까지 문자열의 내용을 알 수 없습니다. 따라서 컴파일시 eval 결과를 기계 코드로 컴파일 할 수 없습니다.

다른 답변에 따르면, 해석 된 언어와 컴파일 된 언어의 구별은 많은 경우에 "정시 컴파일러"( 일명 "핫스팟") (예를 들어, V8 과 같은 자바 스크립트 엔진 이 점점 더 동일한 기술을 사용함)와 같은 Java와 같은 더 현대적인 언어에서는 크게 흐려질 수 있다고 지적 합니다. "유사한"기능은 확실히 그 중 하나입니다.


2
V8이 좋은 예입니다. 그것은 순수한 컴파일러이며, 어떤 해석도 진행되지 않습니다. 그러나 여전히 무제한을 포함하여 ECMAScript의 전체 의미를 지원합니다 eval.
Jörg W Mittag

1
루아도 같은 일을합니다.
Mooing Duck

3

LISP는 "실제"언어의 기초로 일종의 상위 수준의 "기계"언어로 생각 되었기 때문에 끔찍한 예입니다. "실제"언어는 결코 실현되지 않았다. LISP 머신은 하드웨어에서 (대부분의) LISP를 수행한다는 아이디어를 바탕으로 구축되었습니다. LISP 인터프리터는 단순한 프로그램이므로 원칙적으로 회로에서 구현할 수 있습니다. 아마도 실용적이지 않다. 그러나 불가능한 것과는 거리가 멀다.

또한 실리콘으로 프로그래밍 된 많은 통역사가 있으며 일반적으로 "CPU"라고합니다. 그리고 기계 코드를 해석하는 것이 유용합니다 (아직 존재하지 않거나 현재는 아님). 예를 들어 Linux의 x86_64는 에뮬레이터에서 처음 작성되고 테스트되었습니다. 칩이 시장에 나왔을 때, 심지어 얼리 어답터 / 테스터에게조차도 전체 배포판이있었습니다. Java는 종종 JVM 코드로 컴파일되는데, 이는 실리콘으로 작성하기가 어렵지 않은 인터프리터입니다.

대부분의 "해석 된"언어는 내부 형식으로 컴파일되어 최적화 된 후 해석됩니다. 이것은 펄과 파이썬이하는 일입니다. 유닉스 쉘과 같이 해석 대상 언어에 대한 컴파일러도 있습니다. 반면에 전통적으로 컴파일 된 언어를 해석 할 수 있습니다. 내가 본 한 다소 극단적 인 예는 사용되는 편집자 해석 확장 언어로 C를. C는 문제없이 정상적인 프로그램이지만 간단하게 실행할 수 있습니다.

다른 한편으로, 현대 CPU는 심지어 "기계 언어"입력을 가져 와서이를 하위 수준의 명령어로 번역 한 다음 실행을 위해 전달하기 전에 순서를 다시 정하고 최적화 (즉, "컴파일")합니다.

이 "컴파일러"와 "인터프리터"의 구별은 정말 어리석은 일 입니다. 스택 어딘가에 "코드"를 가져 와서 "직접"실행하는 궁극적 인 인터프리터가 있습니다. 프로그래머의 입력은 선을 따라 변형을 겪습니다. 그 중 "컴파일"은 모래에 임의의 선을 그립니다.


1

실제로 일부 기본 프로그램 해석과 어셈블러 실행 간에는 큰 차이가 있습니다. 그리고 P-code / byte-code에는 (적시에) 컴파일러가 있거나없는 영역이 있습니다. 이 현실의 맥락에서 몇 가지 요점을 요약하려고합니다.

  • 소스 코드를 구문 분석하는 방법이 런타임 조건에 따라 달라지면 컴파일러 작성이 불가능 해 지거나 아무도 신경 쓰지 않을 수 있습니다.

  • 자체를 수정하는 코드는 일반적으로 컴파일이 불가능합니다.

  • eval과 유사한 함수를 사용하는 프로그램은 일반적으로 사전에 완전히 컴파일 할 수 없습니다 (프로그램의 일부로 제공된 문자열을 고려하는 경우) eval의 코드를 반복적으로 실행하는 경우 여전히 eval-like 함수가 컴파일러를 호출하도록하는 데 유용합니다. 일부 언어는이를 쉽게하기 위해 컴파일러에 API를 제공합니다.

  • 이름으로 사물을 참조하는 기능은 컴파일을 방해하지 않지만 언급 된 테이블이 필요합니다. IDispatch와 같은 이름으로 함수를 호출하려면 많은 수의 배관이 필요합니다. 대부분의 사람들이 함수 호출 인터프리터에 대해 효과적으로 이야기하고 있다는 데 동의 할 것입니다.

  • 타이핑이 약하면 (정의가 무엇이든) 컴파일이 더 어려워지고 결과가 덜 효율적이지만, 다른 값이 다른 구문 분석을 트리거하지 않는 한 종종 불가능하지는 않습니다. 여기에 슬라이딩 스케일이 있습니다. 컴파일러가 실제 유형을 추론 할 수 없다면 분기, 함수 호출 등을 내 보내야합니다.


1

나는 언어에 대한 컴파일러를 불가능하게 만드는 프로그래밍 언어의 주요 기능 (엄격한 의미에서 자체 호스팅 참조 )이 자체 수정 기능이라고 가정합니다. 언어는 런타임 중에 소스 코드를 변경할 수 있음을 의미 합니다 (컴파일러 생성, 고정 및 정적, 객체 코드는 수행 할 수 없음). 전형적인 예는 Lisp입니다 ( Homoiconicity 참조 ). 많은 언어 (예 : javaScript)에 포함 된 eval 과 같은 언어 구성을 사용하여 유사한 기능이 제공됩니다 . Eval은 실제로 런타임에 인터프리터를 함수로 호출합니다 .

즉, 언어는 자체 메타 시스템을 나타낼 수 있습니다 (메타 프로그래밍 참조 )

참고언어 반사 특정 소스 코드의 메타 데이터에 대한 쿼리의 의미를, 그리고 아마도 단지는 (자바 나 PHP의 반사 메커니즘 같은 STH) 인 메타 데이터 수정 문제가되지 는 이미 사람들이 있기 때문에, 컴파일러를 들어 컴파일 타임에 메타 데이터를 작성하고 필요한 경우 컴파일 된 프로그램에서 메타 데이터를 사용할 수있게합니다.

컴파일을 어렵게 만들거나 최상의 옵션으로 만들 수없는 또 다른 기능은 언어에서 사용되는 타이핑 체계입니다 (예 : 동적 타이핑 vs 정적 타이핑 및 강력한 타이핑 vs 느슨한 타이핑). 이로 인해 컴파일러가 컴파일시 모든 의미를 갖기가 어려워 지므로 효과적으로 컴파일러의 일부 (즉, 인터프리터)가 런타임시 의미를 처리하는 생성 된 코드의 일부가 됩니다 . 다시 말해서 이것은 컴파일이 아니라 해석 입니다.


LISP는 sprt로 생각한 끔찍한 예입니다
vonbrand

@vonbrand는 호모 닉 개념과 균일 한 데이터 코드 이중성을 모두 표시합니다.
Nikos M.

-1

나는 원래의 질문이 잘 형성되지 않았다고 생각한다. 질문의 저자는 다소 다른 질문을하려고 할 수도 있습니다. 프로그램 언어의 어떤 속성이 컴파일러 작성을 용이하게합니까?

예를 들어 상황에 맞는 언어보다 상황에 맞는 언어의 컴파일러를 작성하는 것이 더 쉽습니다. 언어를 정의하는 문법에는 모호성과 같은 컴파일하기 어려운 문제가있을 수도 있습니다. 이러한 문제는 해결 될 수 있지만 추가 노력이 필요합니다. 마찬가지로 제한되지 않은 문법으로 정의 된 언어는 상황에 맞는 언어보다 구문 분석하기가 더 어렵습니다 ( Chomsky Hierarchy 참조 ). 내 지식으로는 가장 널리 사용되는 절차 적 프로그래밍 언어는 컨텍스트가 거의 없지만 컨텍스트에 민감한 요소가 많으므로 비교적 쉽게 컴파일 할 수 있습니다.


2
문제는 컴파일러와 인터프리터를 반대 / 비교하려는 의도입니다. 위의 @Raphael limit case를 제외하고는 다르게 작동하지만 일반적으로 구문 분석 및 모호성에 대해 정확히 동일한 문제가 있습니다. 따라서 구문은 문제가 될 수 없습니다. 또한 구문 문제는 오늘날 컴파일러 작성의 주요 관심사가 아니라고 생각하지만 과거에는 그랬습니다. 나는 downvoter가 아니다 : 나는 논평을 선호한다.
babou

-1

이 질문에는 정답이 있으므로 일반적으로 사소한 것으로 간과됩니다. 그러나 그것은 많은 상황에서 중요하며 해석되는 언어가 존재하는 주된 이유입니다.

아직 소스 코드가 없으면 소스 코드를 기계 코드로 컴파일 할 수 없습니다.

통역사는 유연성을 추가하고, 특히 기본 프로젝트를 컴파일 할 때 사용할 수 없었던 코드 실행의 유연성을 추가합니다.


2
"소스 코드를 잃어 버렸다"는 프로그래밍 언어의 속성이 아니라 특정 프로그램의 속성이므로 질문에 대답하지 않습니다. 그리고 소스 코드의 손실을 피하는 것이 "통역 된 언어가 존재하는 주된 이유"이거나 심지어 존재 하는 이유 라는 주장에 대한 인용이 반드시 필요 합니다.
David Richerby

1
@DavidRicherby tyleri가 생각하는 유스 케이스는 대화 형 해석, 즉 런타임에 입력 된 코드라고 생각합니다. 그러나 나는 그것이 언어의 특징이 아니기 때문에 질문의 범위를 벗어난 것에 동의합니다.
Raphael

@DavidRicherby와 라파엘, 난이 게시물의 작성자는 물론 언어 설계에 의한 구조가 아닌 일부 특정 프로그램의 유물 인 자기 수정 기능으로 (내가 내 대답에 무엇을 설명)을 의미 말
니 코스 M.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.