79 — Compiler tier: functions in the mini language
Guide 73 compiled five verbs into Python. Real languages
have functions, so this compiler grows one: def name params opens a
function, return expr ends it, and say name(args) calls it. The new
piece is a signature table — the compiler remembers every function's
parameters so it can emit real Python def lines.
Steps
The mini language grows two verbs.
defnames a function and lists its parameters;returngives the answer; asaymay call a function:
run it →[ "def double n", " return n * 2", "say double(21)", "def add a b", " return a + b", "say add(2, 3)", "say done", ]double(21)means "call the double function with 21". The indentedreturnmarks the body, exactly like thewhilebody in guide 73.The compiler keeps two tables.
knownstill lists variables (guide 73); a new dictfunctionsmaps each function name to its parameter list:
run it →functions = {}On a
defline the compiler stores the signature and emits a Python header; onreturnit emits the return statement and leaves the body block:elif verb == "def": name = parts[1] params = parts[2:] functions[name] = params lines.append(" " * indent + f"def {name}({', '.join(params)}):") indent = indent + 4 elif verb == "return": expr = " ".join(parts[1:]) lines.append(" " * indent + "return " + expr) indent = indent - 4', '.join(params)turns["a", "b"]into the texta, b— the same join that made CSV rows in guide 45.saymust now recognize calls. The word before(is the function name; if it is infunctions, the whole call is an expression andprintreceives it without quotes:elif verb == "say": text = raw.split(None, 1)[1] name = text.split("(")[0] if name in functions or name in known: lines.append(" " * indent + f"print({text})") else: lines.append(" " * indent + f'print("{text}")')raw.split(None, 1)[1]takes everything after the first space, sosay add(2, 3)keeps its comma — a plainsplit()would cut it at the space. This is why real compilers do not just split every line into words.say donehas no(;doneis in neither table, so it prints as a quoted text — the same fallback as before.The full compiler writes
out.pyand runs it withexec, exactly like the capstone. Savefunctions.nme:
run it →# functions.nme — a mini language with functions, compiled to Python. # Run: nme r functions # Reads the mini language, compiles it to Python source, # writes out.py, then runs out.py with exec. use file latest program = [ "def double n", " return n * 2", "say double(21)", "def add a b", " return a + b", "say add(2, 3)", "say done", ] known = [] functions = {} lines = [] indent = 0 for raw in program: parts = raw.split() verb = parts[0] if verb == "def": name = parts[1] params = parts[2:] functions[name] = params lines.append(" " * indent + f"def {name}({', '.join(params)}):") indent = indent + 4 elif verb == "return": expr = " ".join(parts[1:]) lines.append(" " * indent + "return " + expr) indent = indent - 4 elif verb == "say": text = raw.split(None, 1)[1] name = text.split("(")[0] if name in functions or name in known: lines.append(" " * indent + f"print({text})") else: lines.append(" " * indent + f'print("{text}")') 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())Run it — no server and no input needed:
nme r functionscompiled mini language: def double(n): return n * 2 print(double(21)) def add(a, b): return a + b print(add(2, 3)) print("done") running out.py: 42 5 doneThe generated Python defines both functions, calls them, and prints
42and5— the mini language now has functions, compiled into real Python and run.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 call verb that stores a result (call double 21 -> r becomes
r = double(21)), or a sub verb that lowers to -. Then make def
usable after other verbs — the signature table makes that a two-line
change — and open out.py: it is plain Python, runnable with
python out.py.
What you learned
- A signature table (
functions) records each function's parameters. ','.join(params)turns a parameter list into a Pythondefheader.text.split("(")[0]tells a call apart from a plain word.raw.split(None, 1)[1]keeps the whole argument — a space-splitting tokenizer would lose commas, which is why real ones don't split blindly.- Functions are the step that turns a verb list into a real language.