needmoreeasy EN 한국어
배우기

25 — 네이티브: 기계어로 컴파일하기

★★★★☆ (4/5)네이티브 컴파일
선수 지식
23 — Modules, 07 — While
결과물
CPython 없이 기계어로 실행하기

지금까지 모든 프로그램은 CPython 위에서 실행됐습니다. NME가 Python으로 컴파일하고 Python이 그것을 실행했습니다. 언어의 작은 부분 — 네이티브 코어 — 은 더 나아가 바로 기계어가 될 수 있습니다. nme 네이티브 실행은 그 코어를 C로 바꾸고 시스템 C 컴파일러로 네이티브 실행 파일을 만듭니다.

단계

  1. 네이티브 코어 안에 머무는 프로그램을 쓰세요. 코어는 불리언·정수·유한 실수 값과 문자열 리터럴, while/if/else, 논리 and/or, break, 정수 return 함수, say/show/말해를 다룹니다. 초급 times:/번: 반복과 문장형 반복도 한 줄 NME 출력 본문을 쓸 수 있습니다. 네이티브 if/while과 분기 본문은 then/그러면 뒤에 한 줄 NME 출력 문장이나 break를 이어 쓸 수도 있습니다:

    score = 0
    while score is less than 10
        score add 1
    end
    show score
    
    실행해 보기 →
    준비는 참
    만약 준비 그러면 말해 "준비됨"
    
    실행해 보기 →

    한 줄 반복은 다음처럼 씁니다:

    repeat 2 times and show Hi
    2번 반복해서 안녕 말해줘
    2 times: say "beginner"
    2번: 말해 "초급"
    
    실행해 보기 →

    한 줄 break는 네이티브 반복문을 멈추며 반복문 안에서만 쓸 수 있습니다:

    횟수 = 0
    동안 참
        횟수에 1 더해
        만약 횟수가 2와 같으면 여기서 멈춰
    끝
    횟수 말해줘
    
    실행해 보기 →

    문장형 반복도 같은 한 줄 본문을 사용할 수 있습니다:

    3번 반복해서 여기서 멈춰
    말해 "끝"
    
    실행해 보기 →

    반복문 자체도 한 줄 본문을 사용할 수 있습니다:

    동안 참 그러면 여기서 멈춰
    말해 "끝"
    
    실행해 보기 →

    일반 Python for 반복문, Python 인라인 본문과 인라인 값 변경은 이 제한된 네이티브 부분집합 밖입니다. 그런 형식에는 CPython 경로를 사용하세요.

    네이티브 문자열 변수는 검사하는 8192바이트 버퍼를 사용합니다. 저장하거나 이어붙인 값이 UTF-8 8191바이트보다 크면 네이티브 프로그램이 영어·한국어 런타임 오류와 함께 중단됩니다. 이스케이프한 줄바꿈과 탭은 지원하지만 내부 NUL 문자는 거부합니다. 제한 없는 텍스트에는 CPython 경로를 쓰세요. 저장 한도는 UTF-8 바이트 기준이지만 len은 유니코드 문자 수를 셉니다. 네이티브 정수는 -2147483648부터 2147483647까지의 부호 있는 32비트 값입니다. 오버플로와 0 제수 나머지 연산은 영어·한국어 런타임 오류로 중단됩니다. 네이티브 함수는 현재 정수만 매개변수로 받고 반환하며, 각 함수에는 최상위 정수 return이 필요합니다. 한 분기에서 일찍 반환할 수 있지만, 제어 블록 뒤로 계속되는 모든 경로는 그 마지막 return에 도달해야 합니다. 도달 가능한 계속 경로가 없는 중첩 조건문도 바깥 분기를 종료한 것으로 취급합니다. 반복문을 빠져나가는 분기도 종료 경로로 취급합니다. 같은 파일의 뒤쪽에 정의된 함수도 호출할 수 있으며 선언된 위치 인자 개수를 맞춰야 합니다. 헤더에는 단순한 정수 매개변수만 쓰며 기본값·가변 인자·키워드 인자는 네이티브 코어 밖에 있고 중첩 함수 정의도 지원하지 않습니다. 실수 리터럴은 유한해야 합니다. 네이티브 실수 산술은 C double을 쓰며, 계산 결과가 유한 범위를 벗어나면 이중 언어 런타임 오류로 멈춥니다. %g 출력 때문에 5.05로 보일 수 있고 -0.0의 부호는 -0으로 유지됩니다. 네이티브 식에서는 리터럴, 먼저 대입한 이름, 선언된 함수를 사용할 수 있습니다. 함수 값을 사용하거나 매개변수를 중복해서 적거나 함수 이름을 변수·매개변수로 다시 쓰는 경우에는 거부되므로 동적인 Python 이름 동작에는 CPython 경로를 사용하세요. if true 뒤에서 실행되지 않는 elseelse if에만 대입한 이름은 블록 밖에서 사용할 수 없습니다. if/else 사슬에서 블록 뒤로 계속되는 모든 경로에 대입한 이름은 그 뒤에 사용할 수 있고, 일찍 반환하거나 감싼 반복문을 빠져나가는 분기는 그 이름을 대입할 필요가 없으며, 그 종료 경로 안에 중첩 조건문이 있어도 같은 규칙을 따릅니다. 계속되는 분기 중 한 곳에서 처음 대입한 이름을 형제 분기에서 읽을 수도 없고 실행되지 않을 수 있는 반복문 안에서 만든 이름은 조건부로 남습니다.

    불리언 이름은 생성된 C에서 둘 다 int에 저장되더라도 정수 이름과 구별됩니다. 대입하고 ==/!=로 비교하고 조건에 바로 쓰며 True 또는 False로 출력할 수 있습니다:

    준비 = True
    말해 준비
    만약 준비
        말해 "준비됨"
    끝
    준비 = False
    말해 준비
    
    실행해 보기 →

    같은 값은 더 쉬운 영어·한국어 표기로도 적을 수 있습니다:

    ready save true
    set ready to True
    준비는 참
    저장 준비 True
    
    실행해 보기 →

    불리언 산술, 더해/ 값 변경, 불리언 함수 매개변수·반환은 CPython 경로에서 사용하세요.

  2. 네이티브로 컴파일해 실행하세요:

    nme 네이티브 실행 count
    
    10
    

    영어 명령 nme 네이티브 count도 같은 방식입니다.

  3. C 소스와 실행 파일을 남기려면:

    nme 네이티브 빌드 count -o count
    

    nme 네이티브 빌드는 실행 파일 옆에 count.c를 씁니다. -o 없이 .ko 파일을 빌드하면 C 이름에도 접미사를 유지한 count.ko.c를 만들어 영어판과 한국어판을 한 폴더에서 빌드할 수 있습니다. Windows에서는 원본 줄기가 .ko로 끝나도 기본 출력에 .exe가 붙습니다. C를 읽으면 프로그램이 실제로 무엇이 되는지 볼 수 있습니다. count.c.nme 같은 원본은 Unix에서 기본 실행 파일 count.c, Windows에서 count.c.exe를 사용하고, 생성 C 소스는 count.c.c입니다. 명시적인 -o count.c만 C 소스 충돌로 거부됩니다. -o 옵션은 빌드에만 해당합니다. nme 네이티브 실행 count -o count실행이 결과 파일을 저장하지 않으므로 E9031으로 거부됩니다. 동작 단어는 하나만 선택하세요. 실행빌드를 함께 적으면 마지막 단어를 조용히 적용하지 않고 E9032로 거부합니다.

  4. 함수와 재귀는 코어 안에서 동작합니다. 한국어판 examples/native-factorial.ko.nme(영어판 examples/native-factorial.nme)는 두 백엔드에서 모두 팩토리얼을 계산합니다:

    # part of examples/native-factorial.ko.nme
    def fact(n):
        만약에 n이 2보다 작으면
            return 1
        끝
        return n * fact(n - 1)
    
    
    show fact(5)
    
    실행해 보기 →
    nme 실행 examples/native-factorial.ko
    nme 네이티브 실행 examples/native-factorial.ko
    

    둘 다 120을 출력합니다.

  5. 코어 밖의 것은 명확한 오류로 거부되며 CPython으로 그대로 실행됩니다. 네이티브 백엔드는 절대 조용히 잘못 컴파일하지 않습니다:

    nme 네이티브 실행 ask-demo    # "지원하지 않습니다" 진단 출력
    nme 실행 ask-demo         # CPython으로는 그대로 동작
    

어떻게 동작하나요

nme-native(Rust 크레이트)는 Python 경로와 같은 프론트엔드 AST를 받아 모든 문장을 문서화된 코어와 대조한 뒤 C를 만듭니다. macOS와 Linux에서는 cc가 그것을 -O2로 기계어로 만들고, Windows에서는 Visual Studio Developer PowerShell의 Microsoft cl/O2/utf-8로 같은 일을 합니다. 네이티브 코어 레퍼런스는 허용되는 표면을 정리하고, 구조 메모는 이 C 백엔드를 LLVM·Cranelift와 비교하여 왜 C가 첫 백엔드인지 설명합니다.

성능은 정직하게 측정됩니다: 이 머신에서 정수 5,000만 회 반복문이 네이티브에서 CPython보다 약 60배 빠릅니다. 빡빡한 반복 하나의 마이크로벤치마크이지, 모든 프로그램에 대한 주장이 아닙니다.

직접 해보기

카운트다운을 100까지 세도록 바꾸거나, square(n) 함수를 만들고 square(7)을 출력한 뒤 nme 네이티브 실행으로 실행해 보세요.

배운 것

  • 네이티브 코어는 C로, macOS·Linux에서는 cc로, Windows에서는 MSVC cl로 네이티브 실행 파일로 컴파일됩니다.
  • nme 네이티브 실행이 실행하고 nme 네이티브 빌드가 C와 실행 파일을 남깁니다. 영어 명령은 nme 네이티브 runnme 네이티브 build입니다.
  • 함수, 반복, 조건, say/show/말해 출력이 모두 코어 안에서 동작하며, 조건과 반복에 정한 한 줄 NME 출력 형식과 반복 안의 한 줄 break도 포함합니다.
  • 코어 밖에서는 잘못 컴파일하는 대신 프로그램을 거부합니다.

GitHub에서 이 문서 보기