26. Meet Drip
Before you build a language, take it for a drive. Drip is small enough to implement in a few weeks, and it still has the closures and classes that make real engines interesting.
- Read and write small Drip programs with variables, loops, functions, closures and classes.
- Explain what dynamic typing means, and predict when a Drip type error appears.
- Say what a language specification is for, and how Drip's test programs act as one.
A language you can hold in your head
JavaScript is a big language, with many years of features piled on top of each other. Starting there is a poor way to learn how a language works.
So for Part III you’ll build a smaller one. It’s called Drip. Drip is
a dialect of Lox, the language Robert Nystrom designed for his book
Crafting Interpreters[1]. It is Lox with two
words respelled: fn instead of fun and let instead of var, so it
looks more like the JavaScript you already know. Every Drip test program
in this book is adapted from Lox’s MIT-licensed test suite.
Drip is like a model kit of a real car. It is much smaller, and some comforts are missing. But the engine, the wheels and the steering are all there, and they work the way the real ones do. When you have built the model, the real car stops being a mystery.
Every example in this chapter runs on the interpreter you are about to write. Edit them freely.
Values and variables
Drip has numbers, strings, true and false, and nil, which means “no
value”, like JavaScript’s null. Variables are made with let, and
print is a statement, not a function, so it needs no parentheses:
let cups = 3;
print cups + 1; // 4
print 7 / 2; print "drip" + "drop"; print 3 < 4 and 4 < 5; print !true; print nil; let cups = 3; cups = cups + 1; print cups; print 0.1 + 0.2; // print "a" + 1;
A few things will feel familiar. Every number is a 64-bit float, just
like in JavaScript, so 0.1 + 0.2 has the same rounding you met in
Part I. Strings join with +. Comments start with //. Logic uses the
words and and or instead of && and ||.
Deciding and repeating
if, while and for look the way you’d expect. One rule is different
from JavaScript: only nil and false count as false. Everything else,
including 0 and the empty string "", counts as true.
let n = 1;
while (n < 50) {
n = n * 2;
}
print n;
for (let i = 0; i < 3; i = i + 1) {
print i;
}
if (0) print "0 is true";
else print "0 is false";Functions and closures
Functions are declared with fn. They are values, so a function can
return another function, and the inner one remembers the variables
around it. That is a closure, exactly as in chapter 6:
fn square(x) {
return x * x;
}
print square(4);
fn makeCounter() {
let n = 0;
fn count() {
n = n + 1;
return n;
}
return count;
}
let c = makeCounter();
print c();
print c();
print makeCounter;Each call to makeCounter makes a new n, and the count it returns
keeps that n alive. Making that work is the job of chapter 41.
Classes
Drip has classes with methods, this and a constructor. The constructor
is the method called init. You make an instance by calling the class
like a function: there is no new. A class inherits from another with
<, and super calls the parent’s version of a method:
class Cup {
init(ml) {
this.ml = ml;
}
pour(x) {
this.ml = this.ml - x;
return this.ml;
}
}
class Mug < Cup {
pour(x) {
print "careful, hot";
return super.pour(x);
}
}
let m = Mug(300);
print m.pour(50);
print m;Dynamic typing
TypeScript, from chapter 13, checks types before your program runs.
Drip doesn’t. A Drip variable can hold any kind of value, and it can hold
a number now and a string later. Step through what happens when a
+ meets the wrong kind of value:
let a = 1;let b = 2;print a + b;b = "2";print a + b;
Dynamic typing means types belong to values, not variables, and they
are checked while the program runs, at the moment an operation needs
them. In Drip, + accepts two numbers or two strings. Anything else
stops the program with a runtime error that names the line.
Dynamic doesn’t mean “anything goes”. JavaScript is dynamically typed
too, but its + quietly converts: "a" + 1 is "a1". Drip refuses and
says so. Both are legitimate designs. To try it, remove the // in front
of the last line of the first example above.
Who decides what a program means?
Here’s a question with no obvious answer: what should print 7 / 2;
show? 3? 3.5? An error? Each is a reasonable choice, and different
languages make different ones. Drip prints 3.5. Something has to
write that decision down, so that every implementation of the language
makes the same choice.
A language specification (a spec) is a precise description of what every program in a language means: which programs are valid, and what each one does or which error it reports. An implementation is correct when it does what the spec says.
JavaScript’s spec is a document called ECMA-262, maintained by a
committee called TC39[2]. Drip has no standards
committee. Its spec is the description of Lox in Crafting Interpreters,
with the two respellings, made checkable by 265 test programs in
companion/corpus. Each one writes down what it should do in comments.
Here are four lines from one of them, if/truth.drip:
// Everything else is true.
if (true) print true; // expect: true
if (0) print 0; // expect: 0
if ("") print "empty"; // expect: empty
Other tests expect an error instead, written as
// expect runtime error: followed by the exact message. Run a test, and
if the output differs from its comments in any way, the implementation
has a bug.
You’ll build Drip twice: a tree-walking interpreter in Part III and a bytecode VM in Part IV. The companion code runs the same 265 programs against both, so the two must agree with the spec and with each other. That’s differential testing from chapter 15, at the scale of a whole language. JavaScript engines do the same with Test262, the official conformance suite for ECMA-262[3].
Your turn
Your first Drip programs. The tests check the exact output, line by line.
Write a Drip function sumTo(n) that returns 1 + 2 + … + n. The program then prints sumTo(10) and sumTo(100), so the expected output is 55 and then 5050.
fn sumTo(n) {
// your code here
}
print sumTo(10);
print sumTo(100);Write a Drip class Counter. Its init() sets a field n to 0. Its method inc() adds 1 to n and returns the new value. Each instance has its own count, so the program below should print 1, 2, then 1.
class Counter {
// your code here
}
let a = Counter();
print a.inc();
print a.inc();
let b = Counter();
print b.inc();What you learned
- Drip is a small dialect of Lox, with
fnandlet. It has numbers, strings, booleans,nil, functions, closures and classes. - Only
nilandfalseare false in Drip.printis a statement. Classes useinit,this,<andsuper. - Dynamic typing: values carry their types, and Drip checks them as each operation runs. A wrong type is a runtime error, not a silent conversion.
- A language spec says precisely what programs mean. Drip’s is made concrete by 265 test programs that both of its implementations must pass.
Next: the first stage of the pipeline. What exactly is a “word” of Drip?
Check your understanding
0/6- 1.
What does
print 7 / 2;show in Drip? - 2.
What does this Drip program print?
if ("") print "yes"; else print "no"; - 3.
What happens when this Drip program runs?
print "start"; print "a" * 2; - 4.
Which of these is a valid Drip function declaration?
- 5.
Drip’s tree-walker and its VM print different things for the same program. How do you decide which one is wrong?
- 6.
What does this print?
fn make() { let n = 0; fn inc() { n = n + 1; return n; } return inc; } let a = make(); let b = make(); print a(); print a(); print b();
References
- [1] Robert Nystrom (2021). Crafting Interpreters. Free web edition at craftinginterpreters.com (© 2015–2021); source, text and test suite at github.com/munificent/craftinginterpreters. github.com/munificent/craftinginterpreters (checked 2026-10-31)
- [2] Ecma International, Technical Committee 39 (TC39). ECMAScript® Language Specification (ECMA-262), current draft. Ecma International; living draft at tc39.es/ecma262, source at github.com/tc39/ecma262. tc39.es/ecma262/ (checked 2026-10-31)
- [3] Ecma TC39 and Test262 contributors. Test262: ECMAScript Test Suite. github.com/tc39/test262. github.com/tc39/test262 (checked 2026-10-31)