25 — Native: compile to machine code
So far every program ran on CPython: NME compiles to Python and Python runs
it. A small part of the language — the native core — can go further and
become machine code directly. nme native turns that core into C and then
into a native executable with your system's C compiler.
Steps
Write a program that stays inside the native core. The core covers boolean, integer, and finite-float values, string literals,
while/if/else, logicaland/or,break, functions with integerreturn, andsay/show/말해. Beginnertimes:/번:loops and sentence repeat forms can also use one-line NME output bodies. A nativeif/whileor branch body may put one NME output orbreakstatement afterthen/그러면on the same line:
run it →score = 0 while score is less than 10 score add 1 end show score
run it →ready save true if ready then show "ready"Here are the one-line repeat forms:
run it →repeat 2 times and show Hi 2번 반복해서 안녕 말해줘 2 times: say "beginner" 2번: 말해 "초급"A one-line
breakstops a native loop and must stay inside that loop:
run it →count = 0 while True count add 1 if count == 2 then break here end show countThe sentence repeat spelling can use the same one-line body:
run it →repeat 3 times and break here show "after"A loop itself may also use a one-line body:
run it →while True then break here show "after"Ordinary Python
forloops, Python inline bodies, and inline value changes remain outside this restricted native subset; use the CPython path for those forms.Native string variables use a checked 8192-byte buffer. If a stored or concatenated value is larger than 8191 UTF-8 bytes, the native program stops with a bilingual runtime error; escaped newlines and tabs are supported, but embedded NUL characters are rejected. Use the CPython path for unrestricted text.
lencounts Unicode characters even though the storage limit is in UTF-8 bytes. Native integers are signed 32-bit values (-2147483648through2147483647); overflow and modulo by zero stop with a bilingual runtime error. Native functions currently accept and return integers only, and each function needs a top-level integerreturn; an arm may return early, but every path that continues after a control block must reach that final return. A nested conditional with no reachable fall-through arm also terminates its enclosing branch. A branch that breaks out of its enclosing native loop is also a terminating path. Calls may name a function defined later in the same file, with the declared number of positional arguments. Use simple integer parameters in the header; defaults, varargs, and keyword arguments are outside the native core, as are nested function definitions. Float literals must be finite. Native float arithmetic uses Cdouble; an arithmetic result outside the finite range stops with a bilingual runtime error.%goutput may print5.0as5;-0.0retains its sign as-0. Native expressions may use a literal, a name assigned earlier, or call a declared function. Function values, duplicate parameters, and reusing a function name for a variable or parameter are rejected; use the CPython path for dynamic Python name behavior. A name assigned only in an unreachableelseorelse ifafterif trueis not available after the block. A name assigned in every branch of anif/elsechain is available afterward; a branch that returns early or breaks out of its enclosing loop does not need to assign it, even when the terminating path contains a nested conditional. One continuing branch cannot read a name first assigned in a sibling branch, and a loop-created name remains conditional if the loop may not run.Boolean names are distinct from integer names even though the generated C stores both in an
int. They can be assigned, compared with==/!=, used directly as conditions, and shown asTrueorFalse:
run it →ready = True show ready if ready show "ready" end ready = False show readyThe same value can be written through the easier English and Korean forms:
run it →ready save true set ready to True 준비는 참 저장 준비 TrueBoolean arithmetic,
add/subtractupdates, and boolean function parameters or returns stay on the CPython path.Compile and run it natively:
nme native run count10The short form
nme native countruns the same way.Keep the C source and the executable:
nme native build count -o countnme native buildwritescount.cnext to the executable. Without-o, a.kosource keeps that suffix in its C name (count.ko.c), so English and Korean twins can be built in one folder. On Windows, implicit outputs also receive.exewhen the source stem ends in.ko. Reading the C is how you see what your program really becomes. A source namedcount.c.nmeusescount.con Unix orcount.c.exeon Windows as its default executable, andcount.c.cas its generated source; only an explicit-o count.cis rejected as a C-source collision. The-ooption belongs tobuild;nme native run count -o countis rejected with E9031 becauserundoes not save an artifact. Choose one action word: writing bothrunandbuildis rejected with E9032 instead of letting the last word silently win.Functions and recursion work inside the core — the example
examples/native-factorial.nme(Korean twinexamples/native-factorial.ko.nme) computes factorials on both backends:
run it →# part of examples/native-factorial.nme def fact(n): if n is less than 2 return 1 end return n * fact(n - 1) show fact(5)nme run examples/native-factorial nme native examples/native-factorialBoth print
120.Anything outside the core is rejected with a clear error and still runs on CPython. The native backend never silently miscompiles a program:
nme native ask-demo # prints a "not supported" diagnostic nme run ask-demo # still works on CPython
How it works
nme-native (a Rust crate) takes the same frontend AST as the Python path,
checks every statement against the documented core, and emits C. On macOS and
Linux, cc turns that into machine code with -O2; on Windows, Microsoft's
cl does the same with /O2 and /utf-8 from a Developer PowerShell for
Visual Studio. The native core reference lists the
accepted surface; the architecture memo compares this
C backend with LLVM and Cranelift and explains why C is the first backend.
Performance is honest and measured: on this machine, a 50-million-iteration integer loop runs about 60× faster natively than on CPython. That is one micro-benchmark of a tight loop, not a claim about every program.
Try it yourself
Change the countdown to count up to 100, or write a square(n) function and
print square(7), then run it with nme native.
What you learned
- The native core compiles to C and to a native executable with
ccon macOS/Linux or MSVCclon Windows. nme native runruns it;nme native buildkeeps the C and executable.- Functions, loops, branches, and
say/show/말해output all work inside the core, including the documented one-line NME output forms for conditions and repeats and one-linebreakbodies inside loops. - Outside the core, the backend rejects the program instead of miscompiling.