Making it run
Tree walking is not the fastest strategy, but it is the clearest, and every faster design builds on the same ideas.
Many real interpreters started exactly like this — early Ruby and many scripting languages walked their syntax trees directly.
The parser tells you what a program means; now something has to do it. A tree-walking interpreter is the simplest way: a recursive function evaluate(node) that looks at a node's type, evaluates its children, and combines the results. For (1 + (2 * 3)) it evaluates 1, then the inner * node (giving 6), then adds them.
evaluate(parse("1 + 2 * 3")) // 7
evaluate(parse('"chi" + "ya"')) // "chiya"
evaluate(parse("!(1 < 2)")) // falseOne case per node type
Chiya values are plain JavaScript values: numbers, strings, true/false and null. That makes the evaluator short. Literals return their value. A Prefix node evaluates its operand and applies - or !. An Infix node evaluates both sides and hands them to an infix(op, a, b) helper. A Program evaluates each statement and keeps the last value.
function evaluate(node) {
switch (node.type) {
case "Number": case "String": case "Bool": return node.value;
case "Prefix": {
const v = evaluate(node.right);
if (node.op === "!") return !truthy(v);
return -v; // after checking it's a number
}
case "Infix": return infix(node.op, evaluate(node.left), evaluate(node.right));
}
}Decide truthiness once in a truthy(v) helper. In Chiya only false and null are falsy — so 0 and "" count as true, unlike JavaScript.