인터프리터는 기계 코드를 생성합니까?


42

나는 컴파일러와 인터프리터의 주제를 집중적으로 연구합니다. 내 기본 이해가 올바른지 확인하고 싶으므로 다음을 가정 해 봅시다.

"Foobish"라는 언어가 있으며 키워드는

<OUTPUT> 'TEXT', <Number_of_Repeats>;

콘솔에 10 번 인쇄하고 싶다면

OUTPUT 'Hello World', 10;

안녕하세요 World.foobish-file.

이제 선택한 언어로 통역사를 작성합니다.이 경우 C # :

using System;

namespace FoobishInterpreter
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            analyseAndTokenize(Hello World.foobish-file)//Pseudocode
            int repeats = Token[1];
            string outputString = Token[0];
            for (var i = 0; i < repeats; i++)
            {
                Console.WriteLine(outputString);
            }
        }
    }
}

인터프리터는 매우 쉬운 인터프리터 수준에서 스크립트 파일 등을 분석하고 인터프리터 구현 방식으로 foobish-language를 실행합니다.

컴파일러가 실제 하드웨어에서 직접 실행되는 기계어를 작성합니까?

따라서 인터프리터는 기계 언어를 생산하지 않지만 컴파일러가 입력을 위해 그것을 수행합니까?

컴파일러와 인터프리터의 기본 작동 방식에 대한 오해가 있습니까?


21
C # "컴파일러"는 무엇을 생각하십니까? 힌트로, 머신 코드를 생성 하지 않습니다 .
Philip Kendall

3
Java 컴파일러는 JVM에 대한 코드를 생성합니다. 따라서 컴파일러의 대상 시스템은 하드웨어에서 직접 실행하지 않는 가상 시스템 일 수 있습니다. 인터프리터와 컴파일러의 주요 차이점은 컴파일러가 먼저 전체 소스 코드를 확인하고 대상 기계 언어로 변환한다는 것입니다. 이 컴파일 된 코드는 의도 한 머신에 의해 실행됩니다. 반면, 통역사는 프로그램의 청크를 즉시 번역하고 실행합니다.
Giorgio

@Giorgio : JIT처럼 말이죠?
Robert Harvey

2
@RobertHarvey : Java 컴파일러 (javac)를 의미했습니다. 내가 알고있는 한 JVM의 바이트 코드를 생성합니다. 그리고 다시 AFAIK 인 JIT (나중에)는 매우 자주 사용되는 일부 바이트 코드를 기본 기계 언어로 컴파일합니다.
Giorgio

4
컴파일러는 번역을 의미합니다. c, 어셈블리, 자바 스크립트, 기계어 코드와 같은 모든 종류의 언어를 방출 할 수 있습니다.
Esben Skov Pedersen

답변:


77

"통역사"와 "컴파일러"라는 용어는 예전보다 훨씬 모호합니다. 수년 전에 컴파일러가 나중에 실행할 머신 코드를 생성하는 것이 일반적 이었지만, 통역사는 소스 코드를 직접 "실행"했습니다. 그래서 그 두 용어는 당시 잘 이해되었습니다.

그러나 오늘날 "컴파일러"와 "통역사"사용에 대한 많은 변형이 있습니다. 예를 들어, VB6은 바이트 코드 ( 중간 언어 형식)로 "컴파일" 한 다음 VB 런타임에 의해 "해석"됩니다. 비슷한 프로세스가 C #에서 발생하며, CIL 을 생성 한 다음 JIT (Just-In-Time Compiler)에 의해 실행되어 과거에는 인터프리터로 간주되었을 것입니다. NGen.exe 를 사용하여 JIT 출력을 실제 바이너리 실행 파일로 "고정-건조"할 수 있습니다 .이 제품은 옛날 컴파일러 의 결과였습니다 .

따라서 귀하의 질문에 대한 답변은 예전만큼 간단하지 않습니다.

위키 백과에 대한 추가 읽기
컴파일러와 통역사


6
@Giorgio : 요즘 대부분의 통역사는 실제로 소스 코드를 실행하지 않고 AST 또는 이와 비슷한 결과를 출력합니다. 컴파일러도 비슷한 과정을 거칩니다. 그 구별은 생각만큼 명확하지 않습니다.
Robert Harvey

5
"이전 제품은 컴파일러의 결과였던 NGen.exe를 사용하여 JIT 출력을 실제 바이너리 실행 파일로"고정-건조 "할 수 있습니다." 컴파일러 (즉, JIT (just-in-time) 컴파일러). 컴파일러가 언제 실행되는지는 중요하지 않지만 수행하는 작업은 중요합니다. 컴파일러는 코드 조각의 표현을 입력으로 받아서 새로운 표현을 출력합니다. 인터프리터는 해당 코드를 실행 한 결과를 출력합니다. 이 프로세스는 혼합 방식과 실행시기에 관계없이 서로 다른 두 가지 프로세스입니다.
Giorgio

4
"컴파일러"는 GCC에 첨부하기로 선택한 용어입니다. 그들은 기계 코드를 생성하더라도 NGen을 컴파일러로 호출하지 않기로 선택했습니다.이 단계를 이전 단계에 첨부하는 것을 선호합니다.이 코드는 기계 코드를 생성하더라도 인터프리터라고 할 수 있습니다 (일부 인터프리터도 그렇게 함). 요점은 요즘에는 "언제나 항상 그렇게 부르는 것"이 ​​아닌 컴파일러 나 인터프리터를 결정하기 위해 호출 할 수있는 바인딩 원칙이 없다는 것입니다.
Robert Harvey

4
매우 제한적인 이해로, 요즘 x86 CPU는 어쨌든 하드웨어 기반 JIT 엔진의 절반에 이르렀으며, 어셈블리는 정확하게 실행되는 것과 관계가 희미 해졌습니다.
Leushenko

4
@RobertHarvey 인터프리터와 컴파일러에 사용되는 기술 사이에 명확한 구분선이 없다는 데 동의하지만 기능에 명확한 구분이 있습니다. 프로그램 코드로 주어진 도구를 입력으로 실행 한 결과가 실행되는 경우 프로그램, 도구는 통역사입니다. 결과가 프로그램을 덜 추상적 인 형식으로 변환 한 결과라면 컴파일러입니다. 결과가 더 추상적 인 형식으로 변환되면 디 컴파일러 인 경우입니다. 그러나 이러한 결과 중 하나 이상이 모호한 경우입니다.
Jules

34

아래에 요약 한 내용은 "컴파일러, 원칙, 기법 및 도구", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), 1, 2 페이지에 나 자신의 아이디어가 추가 된 내용을 기반으로합니다.

프로그램을 처리하는 두 가지 기본 메커니즘은 컴파일해석 입니다.

컴파일은 주어진 언어로 소스 프로그램을 입력하고 대상 언어로 대상 프로그램을 출력합니다.

source program --> | compiler | --> target program

대상 언어가 기계어 코드 인 경우 일부 프로세서에서 직접 실행할 수 있습니다.

input --> | target program | --> output

컴파일에는 전체 입력 프로그램 (또는 모듈)을 스캔하고 번역하는 작업이 포함되며 실행하지 않습니다.

해석은 소스 프로그램과 입력을 입력으로 받아서 소스 프로그램의 출력을 생성합니다

source program, input --> | interpreter | --> output

해석에는 일반적으로 한 번에 한 명령문 씩 프로그램을 처리 (분석 및 실행)해야합니다.

실제로 많은 언어 프로세서가 두 가지 접근 방식을 혼합하여 사용합니다. 예를 들어, Java 프로그램은 먼저 중간 프로그램 (바이트 코드)으로 변환 (컴파일)됩니다.

source program --> | translator | --> intermediate program

이 단계의 출력은 가상 머신에 의해 실행 (해석)됩니다.

intermediate program + input --> | virtual machine | --> output

더 복잡하게하기 위해 JVM은 런타임에 적시 컴파일을 수행하여 바이트 코드를 다른 형식으로 변환 한 다음 실행합니다.

또한 기계 언어로 컴파일하더라도 기본 프로세서에 의해 구현되는 이진 파일을 실행하는 인터프리터가 있습니다. 따라서이 경우에도 컴파일 + 해석의 하이브리드를 사용합니다.

따라서 실제 시스템은 두 가지를 혼합하여 사용하므로 주어진 언어 프로세서가 컴파일러 또는 해석기인지 여부를 말하기가 어렵습니다. 처리의 다른 단계에서 두 메커니즘을 모두 사용하기 때문일 수 있습니다. 이 경우 다른 중립적 인 용어를 사용하는 것이 더 적절할 것입니다.

그럼에도 불구하고, 컴파일과 해석은 위의 다이어그램에서 설명한 것처럼 두 가지 종류의 처리입니다.

초기 질문에 대답합니다.

컴파일러는 실제 하드웨어에서 직접 실행되는 기계어를 작성합니까?

반드시 컴파일러가 기계 M1 용으로 작성된 프로그램을 기계 M2 용으로 작성된 동등한 프로그램으로 변환 할 필요는 없습니다. 대상 머신은 하드웨어로 구현되거나 가상 머신 일 수 있습니다. 개념적으로 차이는 없습니다. 중요한 점은 컴파일러가 코드를보고이를 실행하지 않고 다른 언어로 번역한다는 것입니다.

그래서 인터프리터는 기계 언어를 생산하지 않지만 컴파일러는 입력을 위해 그것을 수행합니까?

로하면 생산 이 출력에 참조하고, 다음 컴파일러는 기계어에있을 수있는 대상 프로그램을 생산, 통역하지 않습니다.


7
다시 말해, 인터프리터는 프로그램 P를 가져 와서 출력 O를 생성하고, 컴파일러는 P를 받아 O를 출력하는 프로그램 P '를 생성합니다. 인터프리터는 종종 컴파일러 (예를 들어, 바이트 코드, 중간 표현 또는 JIT 기계 명령어) 인 컴포넌트를 포함하고, 컴파일러는 인터프리터 (예를 들어, 컴파일 타임 계산을 평가하기 위해)를 포함 할 수있다.
Jon Purdy

"컴파일러는 (예를 들어, 컴파일 타임 계산을 평가하기위한) 인터프리터를 포함 할 수 있습니다": 좋은 지적. 나는 Lisp 매크로와 C ++ 템플릿이 이런 식으로 사전 처리 될 수 있다고 생각합니다.
Giorgio

더 간단하게 C 프리 프로세서는 CPP 지시문을 사용하여 C 소스 코드를 일반 C로 컴파일하고와 같은 부울 표현식에 대한 인터프리터를 포함합니다 defined A && !defined B.
Jon Purdy

@JonPurdy 나는 그것에 동의 할 것이지만, 아마도 토큰 화 된 버전의 소스를 넘어서는 중간 표현을 사용하지 않는 "전통적인 통역사"클래스를 추가 할 것이다. 쉘, 많은 BASIC, 클래식 Lisp, 8.0 이전의 Tcl 및 bc가 그 예입니다.
홉스

1
@naxa-컴파일러 유형에 대한 Lawrence의 답변과 Paul Draper의 의견을 참조하십시오. 어셈블러는 (1) 출력 언어가 머신 또는 가상 머신에 의해 직접 실행되도록되어 있고 (2) 입력 명령문과 출력 명령간에 매우 간단한 일대일 대응이있는 특수한 종류의 컴파일러입니다.
Jules

22

컴파일러는 기계 언어를 만들 것입니다

컴파일러는 단순히 언어 A로 작성된 프로그램을 입력 으로 사용하고 언어 B 에서 의미 적으로 동등한 프로그램을 출력으로 생성하는 프로그램입니다 . 언어 B 는 무엇이든 가능하며 기계 언어 일 필요는 없습니다.

컴파일러는 고급 언어에서 다른 고급 언어 (예 : Java를 ECMAScript로 컴파일하는 GWT), 고급 언어에서 하위 언어 (예 : Scheme을 C로 컴파일하는 Gambit)로 컴파일 할 수 있습니다. 고급 언어에서 기계어 코드 (예 : Java를 원시 코드로 컴파일하는 GCJ), 하위 언어에서 고급 언어 (예 : C를 Java, Lua, Perl, ECMAScript 및 Common로 컴파일하는 Clue) 하위 수준 언어에서 다른 하위 수준 언어 (예 : JVML 바이트 코드를 Dalvik 바이트 코드로 컴파일하는 Android SDK)와 같은 하위 수준 언어에서 기계 코드 (예 : HotSpot의 일부인 C1X 컴파일러, JVML 바이트 코드를 기계 코드로 컴파일), 기계 코드를 고급 언어 (소위 "디 컴파일러", LLVM 기계 코드를 ECMAScript로 컴파일하는 Emscripten)로 변환,저수준 언어의 머신 코드 (예 : x86 기본 코드를 JVML 바이트 코드로 컴파일하는 JPC의 JIT 컴파일러) 및 기본 코드 (예 : PowerPC 기본 코드를 x86 기본 코드로 컴파일하는 PearPC의 JIT 컴파일러).

또한 "머신 코드"는 몇 가지 이유로 매우 희미한 용어입니다. 예를 들어, JVM 바이트 코드를 기본적으로 실행하는 CPU가 있으며 x86 기계 코드에 대한 소프트웨어 인터프리터가 있습니다. 그렇다면 하나의 "네이티브 머신 코드"를 만들지 만 다른 것은 아닙니다. 또한 모든 언어는 해당 언어에 대한 추상 기계의 코드입니다.

특수 기능을 수행하는 컴파일러에는 많은 특수화 된 이름이 있습니다. 이것들은 특수한 이름이라는 사실에도 불구하고, 이들은 모두 여전히 컴파일러이며 특별한 종류의 컴파일러입니다.

  • 언어 A 가 언어 B 와 거의 동일한 추상화 레벨로 인식되는 경우 컴파일러를 변환기로 변환 할 수 있습니다 (예 : Ruby-to-ECMAScript-transpiler 또는 ECMAScript2015-to-ECMAScript5-transpiler)
  • 언어 A 가 언어 B 보다 낮은 레벨의 추상화 레벨 인 것으로 인식 되면 컴파일러를 디 컴파일러 (예 : x86-machine-code-to-C-decompiler)라고합니다.
  • language A == language B 인 경우 컴파일러는 최적화 프로그램 , obfuscator 또는 축소 기 라고 할 수 있습니다 (컴파일러의 특정 기능에 따라 다름)

실제 하드웨어에서 직접 실행되는 것은 무엇입니까?

반드시 그런 것은 아닙니다. 인터프리터 또는 VM에서 실행될 수 있습니다. 다른 언어로 더 컴파일 될 수 있습니다.

그래서 인터프리터는 기계 언어를 생산하지 않지만 컴파일러는 입력을 위해 그것을 수행합니까?

통역사는 아무 것도 생산하지 않습니다. 그냥 프로그램을 실행합니다.

컴파일러는 무언가를 생성하지만 반드시 기계 언어 일 필요는 없으며 어떤 언어라도 될 수 있습니다. 입력 언어와 동일한 언어 일 수도 있습니다! 예를 들어 Supercompilers, LLC에는 Java를 입력으로 사용하고 최적화 된 Java를 출력으로 생성하는 컴파일러가 있습니다. ECMAScript를 입력으로 사용하고 최적화, 축소 및 난독 처리 된 ECMAScript를 출력으로 생성하는 ECMAScript 컴파일러가 많이 있습니다.


당신은 또한 관심이있을 수 있습니다 :


16

"컴파일러 인터프리터" 라는 개념은 틀린 이분법이기 때문에 완전히 삭제해야한다고 생각합니다 .

  • 컴파일러 A는 변압기 : 그것은 작성된 컴퓨터 프로그램 변환 소스 언어 와의 등가 출력 타겟 언어 . 일반적으로 소스 언어는 대상 언어보다 높은 수준이며 다른 방식 인 경우 이러한 변환기를 종종 디 컴파일러라고 합니다.
  • 인터프리터는 입니다 실행 엔진 . 해당 언어의 사양에 따라 한 언어로 작성된 컴퓨터 프로그램을 실행합니다. 우리는 주로 소프트웨어라는 용어를 사용합니다 (그러나 고전적인 CPU는 기계 코드의 하드웨어 기반 "인터프리터"로 볼 수 있습니다).

실제 프로그래밍 언어를 유용하게 만들기위한 총체적인 단어는 구현 입니다.

과거에는 프로그래밍 언어 구현이 종종 컴파일러 (및 코드를 생성 한 CPU) 또는 인터프리터 로 구성되었으므로이 두 종류의 도구는 상호 배타적 인 것처럼 보일 수 있습니다 . 오늘날, 당신은 이것이 사실이 아니라는 것을 분명히 알 수 있습니다 (그리고 결코 시작해서는 안됩니다). 정교한 프로그래밍 언어 구현을 수행하고 "컴파일러"또는 "인터프리터"라는 이름을 입력하려고하면 종종 결정적이지 않거나 일관되지 않은 결과가 발생합니다.

단일 프로그래밍 언어 구현에는 종종 여러 형태 (독립형, 즉석에서), 정적 분석기옵티 마이저 와 같은 다른 도구 및 여러 단계로 된 수많은 컴파일러 및 인터프리터 가 포함될 수 있습니다 . 여기에는 임의의 수의 중간 언어 (구현되는 언어와 관련이 없음)의 전체 구현이 포함될 수 있습니다.

구현 체계의 예는 다음과 같습니다.

  • C를 x86 머신 코드로 변환하는 AC 컴파일러와 해당 코드를 실행하는 x86 CPU
  • C를 LLVM IR로 변환하는 AC 컴파일러, LLVM IR을 x86 기계 코드로 변환하는 LLVM 백엔드 컴파일러 및 해당 코드를 실행하는 x86 CPU.
  • C를 LLVM IR로 변환하는 AC 컴파일러와 LLVM IR을 실행하는 LLVM 인터프리터
  • Java를 JVM 바이트 코드로 변환하는 Java 컴파일러 및 해당 코드를 실행하는 인터프리터가있는 JRE
  • Java를 JVM 바이트 코드로 변환하는 Java 컴파일러 및 해당 코드의 일부를 실행하는 인터프리터와 해당 코드의 다른 부분을 x86 기계 코드로 변환하는 컴파일러 및 해당 코드를 실행하는 x86 CPU가있는 JRE
  • Java를 JVM 바이트 코드로 변환하는 Java 컴파일러 및 해당 코드를 실행하는 ARM CPU
  • C #을 CIL로 변환하는 AC # 컴파일러, CIL을 x86 기계 코드로 변환하는 컴파일러 및 해당 코드를 실행하는 x86 CPU가있는 CLR.
  • Ruby를 실행하는 Ruby 인터프리터
  • Ruby를 실행하는 인터프리터와 Ruby를 x86 기계 코드로 변환하는 컴파일러 및 해당 코드를 실행하는 x86 CPU가 모두 포함 된 Ruby 환경.

...등등.


중간 표현 (예 : 자바 바이트 코드)을 위해 설계된 인코딩조차도 하드웨어 구현을 가질 수 있음을 지적하기 위해 +1.
Jules

7

컴파일러와 인터프리터 사이의 선은 시간이 지남에 따라 희미 해졌지만 프로그램이 수행해야하는 작업과 컴파일러 / 인터프리터가 수행하는 작업의 의미를 살펴봄으로써 이들 사이에 선을 그릴 수 있습니다.

컴파일러는 다른 프로그램 (일반적으로 기계 코드와 같은 저수준 언어)을 생성하여 해당 프로그램이 실행되면 프로그램이 수행하는 작업을 수행합니다.

통역사는 프로그램이해야 할 일을합니다.

이러한 정의를 통해 퍼지되는 장소는 컴파일러 / 통역사가 사용자의 시각에 따라 다른 일을하는 것으로 생각할 수있는 경우입니다. 예를 들어, Python은 Python 코드를 가져와 컴파일 된 Python 바이트 코드로 컴파일 합니다. 이 파이썬 바이트 코드가 Python 바이트 코드 인터프리터를 통해 실행되면 프로그램이해야했던 일을 수행합니다. 그러나 대부분의 상황에서 Python 개발자는 두 단계 모두 하나의 큰 단계로 수행 된다고 생각하므로 CPython 인터프리터를 소스 코드 를 해석 하는 것으로 생각 하고 그 과정에서 컴파일 된 사실 은 구현 세부 사항으로 간주됩니다. . 이런 식으로, 그것은 모두 관점의 문제입니다.


5

다음은 컴파일러와 인터프리터 사이의 간단한 개념 명확성입니다.

3 개 언어 고려 : 프로그래밍 (프로그램이 작성된 것을) 언어, P를; 도메인 언어, D (실행중인 프로그램과 관련하여); 그리고 대상 언어, T (일부 제 3 언어).

개념적으로

  • 컴파일러는 당신이 T (D)을 평가할 수 있도록 T에 P 변환; 그런데, ...한데

  • 통역자 직접 평가하여 P의 (D).


1
대부분의 현대 통역사는 실제로 소스 언어를 직접 평가하는 것이 아니라 소스 언어의 일부 중간 표현을 평가합니다.
Robert Harvey

4
@RobertHarvey 그것은 용어들 사이의 개념적 구분을 바꾸지 않습니다.
Lawrence

1
따라서 실제로 통역사라고하는 것은 중간 표현을 평가하는 부분입니다. 중간 표현 을 만드는 부분은 정의에 따라 컴파일러 입니다.
Robert Harvey

6
@RobertHarvey 실제로는 그렇지 않습니다. 용어는 작업중인 추상화 수준에 따라 다릅니다. 아래를 보면 도구가 무엇이든 할 수 있습니다. 유추하여 외국에 가서 이중 언어 친구 인 Bob을 데려 오라고 가정하십시오. Bob과 대화하여 현지인들과 대화를 나눠 현지인들과 의사 소통을하면 Bob은 통역사 역할을합니다 (통화하기 전에 언어로 낙서하더라도). Bob에게 문구를 요청하고 Bob이 외국어로 문구를 작성하고 Bob이 아닌 해당 저술을 참조하여 현지인과 의사 소통하는 경우 Bob은 컴파일러의 역할을합니다.
Lawrence

1
훌륭한 답변입니다. 주목할 점 : 요즘에는 "변환기"가 들릴 수 있습니다. 그것은 P와 T가 비슷한 추상화 수준이며 비슷한 정의에 대한 컴파일러입니다. (예 : ES5에서 ES6 로의 트랜스 파일러)
Paul Draper
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.