needmoreeasy EN 한국어
배우기

NME 네이티브 백엔드: 조사와 설계

상태: v0이 구현되었습니다(nme 네이티브 실행/nme 네이티브 빌드). 정적 타입 코어 부분집합 — 불리언·정수·유한 실수 값과 문자열·산술, 비교와 논리 and/or 조건의 문장형 while/if/else/else if, 초급 번: 반복과 한 줄 NME 출력 또는 break 본문을 쓰는 문장형 반복, then/그러면 뒤의 한 줄 NME say/show/말해 또는 break 본문, break, 정수 스칼라 매개변수와 모든 경로에서 정수 return을 쓰는 함수(재귀 동작), say/말해 — 을 C로 내리고 시스템 C 컴파일러로 네이티브 실행 파일을 만듭니다. 코어 밖의 모든 것은 명확한 진단으로 거부되며 CPython으로 그대로 실행됩니다. 이 문서는 전체 백엔드를 위한 정직한 기술 계획이며, 현재의 Python 파이프라인이나 Nuitka를 "NME 네이티브 컴파일러"라고 부르지 않습니다.

NME가 오늘 컴파일하는 대상

NME 컴파일러(nme-core)는 소스 대 소스 변환기입니다. 문장·초급·고급 단계를 일반 Python으로 내리고(줄마다 하나의 물리 줄 유지), CPython이 그 결과를 실행합니다. nme 컴파일(영어 nme compile)은 설치된 Nuitka로 그 Python을 실행 파일로 만듭니다. Nuitka는 성숙한 Python-to-C 컴파일러이지만 NME 네이티브 백엔드가 아닙니다: 이 경로는 전체 Python 호환 파이프라인을 위한 것이므로 결과물도 Python 런타임 의미를 유지합니다. 별도의 nme 네이티브(영어 nme native) 백엔드는 문서에 정의한 제한된 NME 부분집합을 C와 시스템 컴파일러 실행 파일로 컴파일하지만, 모든 NME나 모든 Python을 컴파일한다고 주장하지 않습니다.

이 문서의 목적은 전체 언어는 CPython에 맡기면서 제한된 백엔드를 정직하게 정의하고 확장하는 것입니다.

CLI는 macOS와 Linux에서 cc를 사용하고 Windows에서는 Microsoft의 cl을 사용합니다. Windows에서 nme 네이티브(영어 nme native)를 사용하기 전에는 Developer PowerShell for Visual Studio(또는 cl.exePATH에 있는 다른 셸)를 시작하세요. NME는 /utf-8을 전달하여 생성된 C의 한국어·영어 문자열이 UTF-8 의미를 유지하게 합니다. CPython 명령에는 이 컴파일러 셸이 필요하지 않습니다.

현재 CLI에서 nme 네이티브 실행 <파일>은 임시 실행 파일을 컴파일해 실행하고 결과물을 저장하지 않습니다. 실행 파일과 생성된 C 소스를 보관하려면 nme 네이티브 빌드 <파일> -o <경로>를 사용하세요. 실행-o를 붙이면 E9031으로 거부됩니다. -o 없이 빌드하면 원본 전체 줄기 뒤에 .c를 붙이므로 count.ko.nmecount.c와 충돌하지 않는 count.ko.c를 만듭니다. Windows의 기본 출력은 원본 줄기가 .ko로 끝나도 .exe를 붙입니다. count.c.nme 같은 원본은 Unix에서 기본 실행 파일 count.c, Windows에서 count.c.exe를 사용하고, 생성 C 소스는 count.c.c입니다. 명시적인 -o count.c만 C 소스 충돌로 거부합니다. 동작 단어는 하나만 선택해야 하며 실행빌드를 함께 적으면 E9032로 거부됩니다.

핵심 설계 결정: "모든 NME"가 아니라 제한된 네이티브 코어

NME의 의미는 Python의 의미입니다. 네이티브 컴파일러는 어느 의미를 목표로 할지 골라야 합니다. 완전한 Python 객체 모델(동적 디스패치, 임의 속성 접근, 메타프로그래밍, 전체 표준 라이브러리)은 거대한 런타임이기 때문입니다. Python 계열 네이티브 컴파일러는 모두 같은 선택을 합니다: 정적으로 분석할 수 있는 코어를 컴파일하고 나머지는 CPython에 맡깁니다.

따라서 NME 네이티브 백엔드는 제한된, 정적 타입의 코어 부분집합을 목표로 하며, 그 의미는 CPython과 무관하게 정의합니다. 지금까지 구현된 것:

  • + - * % 산술을 하는 정수·유한 실수(검사하는 네이티브 정수는 -2147483648부터 2147483647까지의 부호 있는 32비트 범위이며, 정수 나머지만 허용하고 오버플로와 0 제수는 영어·한국어 네이티브 런타임 오류로 보고합니다. 실수 리터럴은 유한해야 하고, 실수 산술은 C double을 쓰며 산술 결과가 유한하지 않으면 이중 언어 네이티브 런타임 오류로 멈춥니다. 정수처럼 보이는 실수는 C %g로 출력되어 Python의 5.0과 표기가 다를 수 있음);
  • 변수에 + 이어붙이기가 되는 문자열 변수(검사하는 8192바이트 고정 버퍼), 이스케이프 문자열 출력, strcmp를 쓰는 문자열 ==/!= 비교, 유니코드 문자 수를 세는 len 내장; 중첩 이어붙이기·내부 NUL 문자·문자열 순서 비교는 잘못 컴파일하지 않고 거부합니다. 저장하거나 이어붙인 값이 UTF-8 8191바이트를 넘으면 버퍼를 넘치게 하지 않고 영어·한국어 네이티브 런타임 오류로 중단합니다;
  • 정수·실수·문자열 비교(<, >, <=, >=, ==, !=, 자연어 "or equal" 연결사 포함)와 정수·유한 실수 참·거짓(if ready, while turns; 0은 거짓), 불리언 리터럴·바인딩(if ready, while ready; False는 거짓), 불리언 같음·다름 비교, 지원되는 조건을 Python 우선순위와 단락 평가로 묶는 논리 and/or를 쓰는 문장형 while/if/else/else if, 초급 번:(times:) 반복과 문장형 반복의 한 줄 NME 출력 또는 break 본문, 이 조건문과 분기에서 then/그러면 뒤에 오는 한 줄 NME say/show/말해 본문, 네이티브 반복문 안의 한 줄 NME break 본문과 break입니다. 일반 Python for 반복문, Python 인라인 본문과 인라인 값 변경은 네이티브 코어 밖입니다. 반복문 안에 중첩한 if에서도 break를 쓸 수 있고, 반복문 밖의 break는 C를 만들기 전에 E0102로 거부합니다;
  • 정수 스칼라 매개변수와 필요한 최상위 정수 return을 쓰는 함수(재귀 동작)를 지원하며, 파일 뒤쪽에 정의된 함수도 호출할 수 있고 선언된 매개변수 개수와 위치 인자를 맞춰야 합니다. 분기 안의 이른 return이나 그 분기를 감싼 네이티브 반복문을 빠져나가는 break는 해당 경로를 끝낼 수 있지만, 블록 뒤로 계속되는 모든 경로는 최상위 return에 도달해야 합니다. 이 분석은 재귀적이므로, 중첩 조건문에 도달 가능한 계속 경로가 하나도 없어도 바깥 분기를 종료된 것으로 봅니다. 헤더는 단순한 위치 기반 정수 매개변수만 허용하며, 중복 정의·기본값·가변 인자·키워드 인자, 실수나 문자열 함수 값, 분기에서만 반환하는 함수, 최상위 return(E0106)은 변환하지 않고 거부합니다;
  • 함수 안의 스칼라 대입은 해당 함수 범위에 남습니다;
  • 불리언 바인딩은 정수와 구별되는 네이티브 타입입니다. 대입하고 같음·다름을 비교하고 참·거짓 조건과 True/False 출력에 사용할 수 있지만, 불리언 산술·값 변경·함수 인자와 반환은 네이티브 코어 밖입니다;
  • 값 변경은 이미 대입된 정수·실수 바인딩에만 가능하며, 대입으로 네이티브 이름의 타입을 바꿀 수 없습니다;
  • 실행되지 않을 수 있는 제어 블록에서 처음 대입한 이름은 블록 전에 대입하거나 블록 안에서 대입한 뒤 사용해야 합니다. 리터럴 if true 블록은 실행된다고 판단하지만, 실행되지 않는 else/else if 대안에서만 대입한 이름은 밖으로 내보내지 않습니다. if/else 사슬에서 블록 뒤로 계속될 수 있는 모든 경로에 대입한 이름은 블록 뒤에 사용할 수 있고, 일찍 반환하거나 반복문을 빠져나가는 분기는 그 이름을 대입할 필요가 없으며, 종료 경로 안에 중첩 조건문이 있어도 같은 규칙을 따릅니다. 계속되는 분기 중 한 곳에서만 대입했거나 실행되지 않을 수 있는 반복문 안에서 만든 이름은 조건부로 남으며 형제 분기는 서로의 새 바인딩을 읽지 못합니다;
  • 정수·실수·불리언·문자열 표현식의 say/show/말해;
  • 한국어와 영어 표기가 같은 C로 내려갑니다;
  • C 키워드, C 구현 예약 형태, 생성된 런타임 이름과 충돌하는 식별자는 조용히 바꾸지 않고 거부합니다. __로 시작하는 이름, _ 다음에 대문자가 오는 이름, 파일 범위 함수의 _ 시작 이름은 예약됩니다. _value 같은 일반적인 블록 지역 이름은 계속 사용할 수 있습니다. 런타임 이름에는 nme_copy, nme_cat, NME_STRING_CAPACITY, NME_UNUSED, _nme_i, 검사하는 정수 도우미, 생성된 헤더가 노출하는 C 라이브러리 심볼도 포함됩니다.
  • 소스 주석은 동작하지 않는 C 주석으로 내리므로 주석 내용이 C 전처리기 지시문이 되거나 네이티브 함수 이동에 영향을 주지 않습니다.
  • 네이티브 식은 리터럴, 먼저 대입된 바인딩이나 선언된 함수 호출을 사용할 수 있습니다. 함수 값을 그대로 쓰거나, 매개변수를 중복해서 적거나, 변수·매개변수가 네이티브 함수 이름을 가리는 경우에는 C를 만들기 전에 거부합니다.

현재 허용되는 불리언 표면은 네이티브 코어 레퍼런스에 정리했습니다. 생성된 C에서는 0/1을 담는 int를 사용하지만, 이것은 표현 방식일 뿐 NME에서 불리언 산술이나 값 변경을 허용한다는 뜻은 아닙니다.

코어 밖의 모든 것 — 동적 Python, 클래스, import, 패키지, use random/ use file 어댑터 — 은 Python 호환 백엔드에 남습니다. 두 백엔드는 별도의 컴파일러 경로이고 명시적인 경계가 있습니다. 이것은 "모든 Python 패키지가 네이티브로 동작한다"는 약속이 아니라 언어에 필요한 정직한 분리입니다.

백엔드 후보

1. C 백엔드 (C 생성 + 시스템 C 컴파일러)

코어 부분집합을 C로 내리고 macOS·Linux에서는 cc/clang을, Windows에서는 Microsoft의 cl을 호출합니다. Cython(타입 영역), Nuitka, 많은 작은 언어 실험이 쓰는 방식입니다.

성숙도gcc·clang의 최적화기는 현존하는 가장 성숙한 컴파일러
의존성시스템 C 컴파일러 외에 없음(Nuitka에도 이미 필요); Windows에서는 개발자 셸의 cl 사용
빌드 모델고전 AOT: C 소스가 학습자가 읽을 수 있는 산출물
런타임작음: 문자열·컬렉션 도우미, 정수 정책; 한 번 작성
검증이 환경에서 완전히 테스트 가능(gcc 존재)
위험C는 큰 표면에 미정의 동작이 있음; 생성 C는 단순·검토돼야 함;
빌드에 C 컴파일러 필요

2. inkwell / llvm-sys를 통한 LLVM

Rust에서 LLVM IR을 구동합니다.

성숙도가장 강한 최적화기; Rust, Swift, Clang이 사용
의존성llvm-sys/inkwell은 시스템 libLLVM 버전과 맞아야 함; 버전
불안정이 알려진 문제; 이 환경에 평가할 libLLVM 없음
빌드 모델AOT 또는 JIT
런타임여전히 필요 — LLVM은 문자열/컬렉션 런타임을 제공하지 않음
위험작은 코어에 무거운 의존성; 코어가 작을 때 최적화기 차이는 런타임보다
훨씬 작음; 여기서 테스트 불가

3. Cranelift

Rust 네이티브 코드 생성(Wasmtime/Wasmer 백엔드).

성숙도활발히 유지, WebAssembly 엔진에서 실사용
의존성순수 Rust — 외부 C++/LLVM 없음, 버전 고정 고통 없음
빌드 모델JIT 중심; AOT는 cranelift-object로 가능하지만 덜 다져진 길
런타임여전히 필요
위험AOT 최적화기는 gcc/LLVM보다 훨씬 약함; 생태계 작음; C 백엔드가
이미 주는 것을 위해 크레이트 그래프 하나를 더 추가

4. 직접 기계어 생성

어셈블리나 미니 코드젠을 직접 작성.

판단언어 규칙이 거부하는 것과 같은 이유로 기각: 성숙한 C/LLVM 최적화기가

있을 때 유지보수·정확성 비용이 이득을 압도. C 툴체인이 없는 이식성 요구가 생길 때만 재검토. |

구현된 v0 백엔드의 권장안

v0 NME 네이티브 백엔드는 C를 생성하고 시스템 C 컴파일러로 빌드하며, LLVM/Cranelift는 나중에 측정된 업그레이드로 남겨 둡니다.

근거(중요도 순):

  1. 가장 작은 정직한 단계. 제한된 코어는 작습니다. C 백엔드는 작은 런타임 하나와 새 Rust 의존성이 필요 없습니다. 인터프리터가 아니라 성숙한 최적화기(gcc)를 통해 진짜 네이티브 기계어를 만듭니다.
  2. 여기서 테스트 가능. gcc가 있습니다. 네이티브 스모크 테스트(say· 산술·반복을 실행 파일로 컴파일해 실행하고 출력을 비교)를 워크스페이스 게이트에 즉시 추가할 수 있습니다.
  3. 읽을 수 있는 산출물. nme 네이티브 빌드 hellohello.c와 실행 파일을 만듭니다. 초보자는 C를 보고 기계어가 어디서 오는지 배웁니다. "문장 → 초급 → 고급 → 네이티브 코어 → C" 이야기와 맞습니다.
  4. 코어에 대해 LLVM/Cranelift가 더해 줄 것이 적음. 둘 다 같은 문자열/컬렉션 런타임이 필요하고, 스칼라 중심 초급 프로그램에서는 최적화기 이점이 무시할 만합니다. 코어가 크게 자라면 C 백엔드의 구조 (런타임 + 하강)가 프론트엔드 수정 없이 어느 백엔드로든 이식됩니다.

정직한 경계를 분명히 말합니다: 네이티브 빌드는 문서화된 코어 부분집합만 다룹니다. 코어 밖 기능을 쓰는 프로그램은 "네이티브 백엔드 미지원 — CPython 으로 실행하세요"라는 명확한 오류로 거부되며 절대 조용히 잘못 컴파일되지 않습니다. 성능 주장은 측정 전까지 금지입니다.

구조

            nme-core (프론트엔드: lexer/parser/AST/Python 하강)
                          │
        ┌─────────────────┴──────────────────┐
        │                                    │
  Python 호환 백엔드                    nme-native 백엔드
  (하강 + CPython, 그대로)        (코어 부분집합 → C → 실행 파일)

프론트엔드는 공유됩니다. 네이티브 백엔드는 별도 크레이트(nme-native), 별도 컴파일러 경로, DiagnosticCode 레지스트리를 재사용하는 별도 진단 집합입니다. 모든 문장이 문서화된 코어에 속할 때만 네이티브 경로가 프로그램을 받아들입니다.

현재 상태와 다음 마일스톤

v0 기준선은 구현되었습니다. 제한된 nme-native 컴파일러, UTF-8 문자열과 검사하는 정수 런타임, nme 네이티브 실행/nme 네이티브 빌드 CLI 진입점, 이중 언어 진단, 종단 간 테스트가 모두 워크스페이스에 있습니다. 다음 마일스톤은 다음과 같습니다.

  1. 공유 의미 정의, 이중 언어 커버리지, 네이티브·CPython 비교, 메모리 안전성 테스트를 갖춘 기능만 코어에 추가합니다.
  2. 더 넓은 성능·이식성 주장을 하기 전에 지원 운영체제와 컴파일러에서 코어를 측정합니다.

2026-08-11 이 머신에서 측정: 정수 5,000만 회 증가 반복문이 네이티브 -O2 바이너리로 약 0.03초, CPython으로 약 2.0초 — 이 한 개의 마이크로벤치마크에서 약 60배(네이티브 숫자에 컴파일 시간 포함). 모든 프로그램에 대한 일반적인 주장이 아니라 빡빡한 정수 반복 하나의 측정값입니다.

참고

  • Nuitka와 Cython은 모두 Python의 타입 영역에서 C 백엔드의 유효성을 보여 주지만, 어느 쪽도 NME 네이티브 컴파일러가 아닙니다.
  • LLVM과 Cranelift는 문서화된 성숙한 코드 생성기입니다. 위의 선택은 그 능력이 아니라 의존성 무게와 코어 크기에 관한 것입니다.

GitHub에서 이 문서 보기