73 — Capstone: a language that compiles to Python
Guide 34 compiled NME words into Python and ran them in
memory. Guide 58 turned instructions into data steps. The
capstone finishes the whole path: read a small custom language, compile it to
Python source, write that source to out.py, and run the file — a real
compiler project in one NME program.
Steps
The input is a small custom language with five verbs.
sayprints,setstores a number,addadds,while name Nrepeats whilename < N, andendcloses the loop. The program lives as a list of lines:
run it →[ "set count 0", "while count 3", " add count 1", " say count", "end", "say done", ]This is source text, not yet run — exactly what a compiler reads.
The compiler turns each line into one Python line.
split()splits a line into words; the first word is the verb, the rest are arguments.indenttracks block depth:whilegrows it by 4,endshrinks it, and" " * indentwrites the leading spaces Python needs — the same string multiplication that drew the bars in guide 71. The full compiler also writes the finished source toout.pywithfile_write(guide 13) and runs it by reading the file back withexec— guide 34's runner, now reading a real file. Savecapstone.nme:
run it →# capstone.nme — a language that compiles to Python. # Run: nme r capstone # Reads the mini language, compiles it to Python source, # writes out.py, then runs out.py with exec. use file latest program = [ "set count 0", "while count 3", " add count 1", " say count", "end", "say done", ] known = [] lines = [] indent = 0 for raw in program: parts = raw.split() verb = parts[0] if verb == "say": text = parts[1] if text in known: lines.append(" " * indent + f"print({text})") else: lines.append(" " * indent + f'print("{text}")') elif verb == "set": known.append(parts[1]) lines.append(" " * indent + f"{parts[1]} = {parts[2]}") elif verb == "add": lines.append(" " * indent + f"{parts[1]} += {parts[2]}") elif verb == "while": lines.append(" " * indent + f"while {parts[1]} < {parts[2]}:") indent = indent + 4 elif verb == "end": indent = indent - 4 else: lines.append(" " * indent + "# unknown: " + raw) source = "\n".join(lines) file_write("out.py", source) show "compiled mini language:" show source show "" show "running out.py:" exec(open("out.py").read())The
knownlist is a symbol table:setrecords its targets, andsaychecks it, sosay countbecomesprint(count)whilesay donebecomesprint("done")— a tiny version of the name lists you imported in guide 72.Run it — no server and no input needed:
nme r capstonecompiled mini language: count = 0 while count < 3: count += 1 print(count) print("done") running out.py: 1 2 3 doneThe generated Python is real Python — indent-safe, saved as
out.py, and runnable on its own. The loop counts 1, 2, 3, thendoneprints.Korean writes the same compiler with
파일 사용 최신,말해, and Korean report lines; the mini language keeps its English verbs. The full Korean program is in the Korean guide.
Try it yourself
Add a sub verb that lowers to -=, and a say text N form that prints
text N times — guide 34 hinted at the loop translation.
Then open out.py: it is plain Python, runnable on its own with
python out.py.
What you learned
- A compiler maps each instruction of a source language to a target line.
indenttracking turnswhile/endinto indented Python blocks.- A
knownlist is a symbol table:saytells variables from plain words. file_writethenexec(open(...))finishes the compile-and-run path.- Five verbs, one pipeline — the whole compiler-project path in one program.