From expressions to programs
Statements are where a language starts to feel like a language rather than a calculator. With let you can name results, with return you can leave early, and with if you can decide. The parser stays small because each rule is a short function that reads exactly like the grammar it implements.
A program is a list of statements. let x = 1 + 2; gives a name to a value, return x; leaves a function, and a bare expression like x * 2 is a statement too. Blocks in curly braces group statements, and if uses blocks for its two branches. Today parse(source) returns a whole Program node.
let rate = 13;
let price = 180;
if (price > 100) { price + price * rate / 100 } else { price }One function per statement kind
statement() looks at the first token. let means a Let statement: expect a name, expect =, parse an expression. return means a Return statement with an expression. Anything else is an expression statement. A semicolon after any of them is optional — if the next token is ;, swallow it.
function statement() {
const t = peek();
if (t.type === "let") {
next();
const name = expect("ident").value;
expect("=");
const value = expression(0);
endStatement();
return { type: "Let", name, value };
}
// return …, then expression statements
}An expect(type) helper that throws Expected 'x' but found 'y' at L:C keeps every grammar rule short and every error message consistent.