[compilation] 컴파일러는 어떻게 스스로 컴파일 할 수 있습니까?

웹 사이트 http://coffeescript.org/ 에서 CoffeeScript를 연구 중이며 텍스트가 있습니다.

CoffeeScript 컴파일러 자체는 CoffeeScript로 작성되었습니다

컴파일러는 어떻게 스스로 컴파일 할 수 있습니까? 아니면이 문장은 무엇을 의미합니까?



답변

컴파일러의 첫 번째 버전은 특정 프로그래밍 언어에서 기계로 생성 할 수 없습니다. 당신의 혼란은 이해할 수 있습니다. 더 많은 언어 기능을 가진 최신 버전의 컴파일러 (새 언어의 첫 번째 버전에서 소스가 다시 작성된)는 첫 번째 컴파일러에 의해 빌드 될 수 있습니다. 그런 다음 해당 버전은 다음 컴파일러 등을 컴파일 할 수 있습니다. 예를 들면 다음과 같습니다.

  1. 첫 번째 CoffeeScript 컴파일러는 Ruby로 작성되어 CoffeeScript 버전 1을 생성합니다.
  2. CS 컴파일러의 소스 코드는 CoffeeScript 1로 다시 작성되었습니다.
  3. 원래 CS 컴파일러는 CS 1로 작성된 새 코드를 컴파일러의 버전 2로 컴파일합니다.
  4. 새로운 언어 기능을 추가하기 위해 컴파일러 소스 코드가 변경되었습니다.
  5. 두 번째 CS 컴파일러 (CS로 작성된 첫 번째)는 수정 된 새 소스 코드를 컴파일러의 버전 3으로 컴파일합니다.
  6. 각 반복에 대해 4 단계와 5 단계를 반복하십시오.

참고 : CoffeeScript 버전의 번호가 정확히 어떻게 사용되는지 잘 모르겠습니다. 이는 단지 예일뿐입니다.

이 과정을 일반적으로 부트 스트랩 이라고 합니다. 부트 스트랩 컴파일러의 또 다른 예 rustcRust 언어 의 컴파일러입니다 .


답변

신뢰 신뢰에 관한 논문 에서 유닉스의 창시자 중 하나 인 켄 톰슨 (Ken Thompson)은 C 컴파일러가 어떻게 자신을 컴파일하는지에 대한 흥미롭고 읽기 쉬운 개요를 작성합니다. CoffeeScript 또는 다른 언어에도 비슷한 개념을 적용 할 수 있습니다.

자체 코드를 컴파일하는 컴파일러의 아이디어 는 원본 소스 코드를 출력으로 생성 하는 quine : 소스 코드 와 모호 합니다. 다음은 CoffeeScript quine의 입니다. 톰슨은 다음과 같은 C 퀴인 예제를 제공했습니다.

char s[] = {
    '\t',
    '0',
    '\n',
    '}',
    ';',
    '\n',
    '\n',
    '/',
    '*',
    '\n',
    … 213 lines omitted …
    0
};

/*
 * The string s is a representation of the body
 * of this program from '0'
 * to the end.
 */

main()
{
    int i;

    printf("char\ts[] = {\n");
    for(i = 0; s[i]; i++)
        printf("\t%d,\n", s[i]);
    printf("%s", s);
}

다음으로, 이스케이프 시퀀스와 같은 이스케이프 시퀀스가 '\n'ASCII 코드 10을 나타내는 것으로 컴파일러에게 어떻게 배울 지 궁금 할 것입니다 . 답은 C 컴파일러 어딘가에 문자 리터럴을 해석하는 루틴이 있으며, 이와 같은 조건을 포함하여 백 슬래시 시퀀스를 인식합니다.

…
c = next();
if (c != '\\') return c;        /* A normal character */
c = next();
if (c == '\\') return '\\';     /* Two backslashes in the code means one backslash */
if (c == 'r')  return '\r';     /* '\r' is a carriage return */
…

위 코드에 하나의 조건을 추가 할 수 있습니다.

if (c == 'n')  return 10;       /* '\n' is a newline */

'\n'ASCII 10 을 나타내는 것을 알고있는 컴파일러를 만드는 것 . 흥미롭게도, 그 컴파일러 와 그에 의해 컴파일 된 모든 후속 컴파일러는 그 매핑을 “알고”있으므로 다음 세대의 소스 코드에서 마지막 행을 다음과 같이 변경할 수 있습니다.

if (c == 'n')  return '\n';

… 그리고 옳은 일을 할 것입니다! 는 10컴파일러에서 온다, 더 이상 명시 적으로 컴파일러의 소스 코드에 정의 될 필요가있다. 1

이것이 C 코드로 구현 된 C 언어 기능의 한 예입니다. 이제 모든 단일 언어 기능에 대해이 프로세스를 반복하면 “셀프 호스팅”컴파일러 (C로 작성된 C 컴파일러)가 있습니다.


1 이 논문에서 묘사 한 플롯 트위스트는 컴파일러가 이와 같은 사실을 “학습”할 수 있기 때문에 탐지하기 어려운 방식으로 트로이 목마 실행 파일을 생성하는 실수를 범할 수 있으며, 이러한 방해 행위는 지속될 수 있습니다. 오염 된 컴파일러가 생성 한 모든 컴파일러에서


답변

당신은 이미 매우 좋은 대답을 얻었지만, 나는 당신에게 다른 관점을 제공하고 싶습니다. 먼저 두 가지 모두에 동의 할 수있는 두 가지 사실을 설정합시다.

  1. CoffeeScript 컴파일러는 CoffeeScript로 작성된 프로그램을 컴파일 할 수있는 프로그램입니다.
  2. CoffeeScript 컴파일러는 CoffeeScript로 작성된 프로그램입니다.

# 1과 # 2가 모두 사실임을 동의 할 수 있습니다. 이제 두 진술을보십시오. CoffeeScript 컴파일러가 CoffeeScript 컴파일러를 컴파일 할 수있는 것은 완전히 정상적인 것입니까?

컴파일러는 상관하지 않는다 어떤 것이 컴파일합니다. CoffeeScript로 작성된 프로그램이라면 컴파일 할 수 있습니다. 그리고 CoffeeScript 컴파일러 자체는 그러한 프로그램입니다. CoffeeScript 컴파일러는 자신이 컴파일하고있는 CoffeeScript 컴파일러 자체에 신경 쓰지 않습니다. 보이는 것은 일부 CoffeeScript 코드입니다. 기간.

컴파일러는 어떻게 스스로 컴파일 할 수 있습니까? 아니면이 문장은 무엇을 의미합니까?

그렇습니다. 그것이 바로 그 진술이 의미하는 바이며, 이제 그 진술이 어떻게 진실인지 알 수 있기를 바랍니다.


답변

컴파일러는 어떻게 스스로 컴파일 할 수 있습니까? 아니면이 문장은 무엇을 의미합니까?

그것은 정확히 의미합니다. 우선, 고려해야 할 사항이 있습니다. 살펴 봐야 할 네 가지 객체가 있습니다.

  • 임의의 CoffeScript 프로그램의 소스 코드
  • 임의의 CoffeScript 프로그램의 (생성 된) 어셈블리
  • CoffeScript 컴파일러의 소스 코드
  • CoffeScript 컴파일러의 (생성 된) 어셈블리

이제 CoffeScript 컴파일러의 생성 된 어셈블리 (실행 파일)를 사용하여 임의의 CoffeScript 프로그램을 컴파일하고 해당 프로그램에 대한 어셈블리를 생성 할 수 있습니다.

이제 CoffeScript 컴파일러 자체는 임의의 CoffeScript 프로그램이므로 CoffeScript 컴파일러로 컴파일 할 수 있습니다.

당신의 혼란은 당신이 당신의 자신의 새로운 언어를 만들 때, 당신은하지 않는 것이 사실에서 비롯된 것으로 보인다 가지고 컴파일러를 아직 당신이 당신의 컴파일러를 컴파일하는 데 사용할 수 있습니다. 이것은 분명히 닭 계란 문제 처럼 보입니다 .

bootstrapping 이라는 프로세스를 소개하십시오 .

  1. 새로운 언어의 일부를 컴파일 할 수있는 기존 언어로 컴파일러를 작성합니다 (CoffeScript의 경우 원래 컴파일러가 Ruby로 작성 됨).
  2. 새 언어 자체에서 새 언어의 하위 집합을 컴파일 할 수있는 컴파일러를 작성합니다. 위 단계에서 컴파일러가 컴파일 할 수있는 언어 기능 만 사용할 수 있습니다.
  3. 1 단계의 컴파일러를 사용하여 2 단계의 컴파일러를 컴파일합니다. 그러면 원래 새 언어의 하위 집합으로 작성되고 새 언어의 하위 집합을 컴파일 할 수있는 어셈블리가 남게됩니다.

이제 새로운 기능을 추가해야합니다. while-loops 만 구현 했지만 for-loops 를 원한다고 가정하십시오. for-loop와 같은 방식으로 -loop를 다시 작성할 수 있으므로 이것은 문제가되지 않습니다 while. 즉, 사용 while중인 어셈블리는 컴파일 만 할 수 있으므로 컴파일러의 소스 코드에는 -loops 만 사용할 수 있습니다. 그러나 컴파일러 내에서 for루프를 실행 하고 컴파일 할 수있는 함수를 작성할 수 있습니다 . 그런 다음 이미 가지고있는 어셈블리를 사용하고 새 컴파일러 버전을 컴파일하십시오. 그리고 이제 for루프를 구문 분석하고 컴파일 할 수있는 컴파일러 어셈블리가 있습니다 ! 이제 컴파일러의 소스 파일 whilefor돌아가서 원하지 않는 -loops를 -loops에 다시 쓸 수 있습니다 .

원하는 모든 언어 기능을 컴파일러로 컴파일 할 수있을 때까지 헹구고 반복하십시오.

while그리고 for분명히 단지 예시했지만, 이것은 당신이 원하는 새로운 언어 기능이 작동합니다. 그런 다음 CoffeScript가 현재 상황에 있습니다. 컴파일러가 자체 컴파일됩니다.

거기에 많은 문헌이 있습니다. 신뢰에 대한 고찰 신뢰 는 그 주제에 관심이있는 모든 사람들이 최소한 한 번은 읽어야하는 고전입니다.


답변

작지만 중요한 설명

여기서 컴파일러 라는 용어 는 두 개의 파일이 관련되어 있다는 사실을 강조 합니다. 하나는 CoffeScript로 작성된 입력 파일로 사용하고 다른 실행 파일, 링크 가능한 객체 파일 또는 공유 라이브러리를 출력 파일로 생성하는 실행 파일입니다. 다른 하나는 CoffeeScript 컴파일 절차를 설명하는 CoffeeScript 소스 파일입니다.

첫 번째 파일을 두 번째 파일에 적용하여 첫 번째 파일을 첫 번째 파일과 동일한 컴파일 동작을 수행 할 수있는 세 번째 파일을 생성합니다 (두 번째 파일이 첫 번째 파일에 의해 구현되지 않은 기능을 정의하는 경우 더 많을 수 있음). 욕망.


답변

  1. CoffeeScript 컴파일러는 Ruby로 처음 작성되었습니다.
  2. CoffeeScript 컴파일러는 CoffeeScript로 다시 작성되었습니다.

CoffeeScript 컴파일러의 Ruby 버전이 이미 존재하기 때문에 CoffeeScript 컴파일러의 CoffeeScript 버전을 작성하는 데 사용되었습니다.

여기에 이미지 설명을 입력하십시오
이를 자체 호스팅 컴파일러라고 합니다.

매우 흔하며 일반적으로 언어의 성장을 유지하기 위해 자신의 언어를 사용하려는 저자의 소망에서 비롯됩니다.


답변

컴파일러는 특정 언어로 작성된 프로그램이기 때문에 컴파일러의 문제가 아니라 표현력의 문제입니다.

“언어가 작성 / 구현된다”고 할 때 실제로는 해당 언어에 대한 컴파일러 또는 인터프리터가 구현됨을 의미합니다. 언어를 구현하는 프로그램을 작성할 수있는 프로그래밍 언어가 있습니다 (동일한 언어의 컴파일러 / 통역사). 이 언어를 보편적 언어 라고 합니다 .

이것을 이해하려면 금속 선반에 대해 생각하십시오. 금속을 성형하는 데 사용되는 도구입니다. 해당 도구 만 사용하여 부품을 작성하여 동일한 동일한 다른 도구를 작성할 수 있습니다. 따라서이 도구는 범용 기계입니다. 물론 첫 번째 방법은 다른 방법 (다른 도구)을 사용하여 만들어졌으며 아마도 품질이 떨어졌을 수 있습니다. 그러나 첫 번째 것은 더 높은 정밀도로 새로운 것을 만드는 데 사용되었습니다.

3D 프린터는 거의 보편적 인 기계입니다. 3D 프린터를 사용하여 전체 3D 프린터를 인쇄 할 수 있습니다 (플라스틱을 녹이는 팁을 만들 수 없음).