01/08Input → output

LEARN TO PROGRAM · CHAPTER 01 · BASICS

What a program is

A program is a list of exact instructions that turns input into output. That's all. The magic is in how a line of text you can read becomes something a lump of silicon can do.

Follow one two-line program all the way down, from the letters you type to the number on the screen. Scroll, and the 3D follows.

Every code block runs in your browser. Edit it, break it, press Run.

01 · INPUT → OUTPUT

Input, process, output

A calculator takes keypresses (input), does arithmetic (process) and shows digits (output). A music app takes a file and turns it into sound. A website takes your click and sends back a page. Under the variety, almost every program has the same shape.

The computer brings two things to this: it never gets bored, and it does exactly what it is told. It doesn't know what you meant. A modern processor core can carry out billions of simple instructions a second, so a program is really a way of breaking what you want into steps small and precise enough for a machine.

temperature.js

Change 21 to 100 and press Run. Same rule, new input, new output.

02 · SOURCE TEXT

Source code is just text

You write programs as text in a programming language: a language with rules strict enough that a machine can follow them. This is the program the 3D follows, in JavaScript, the language every web browser runs:

program.js
let total = 2 + 3;
console.log(total * 4);

It stores 2 + 3 under the name total, then prints total times 4. You can probably already see it will print 20. The computer can't "see" anything: to it this file is a row of numbers, one per character. The bars over the editor show them.

characters.js

Every character has a number (its Unicode code point): l is 108, a space is 32, the digit 2 is 50. Try an emoji in the string.

159,801characters in the Unicode standard (2025), so every script on Earth can be text, and code. Unicode 17.0

03 · TOKENS

Tokens: the lexer reads the words

The first thing any language implementation does is lexing (or scanning): it walks the characters and groups them into tokens, the language's words and punctuation. let is a keyword, total is a name, = and + are operators, 2 is a number, ; ends a statement. Spaces and comments are thrown away.

In the 3D, the program's text lifts off the editor and becomes sixteen tiles, coloured by kind. Here is a working lexer for a small slice of JavaScript, in 20 lines:

tokenizer.js

Try tokenize("let speed = 60 * 60;"). Then press Step through and watch i walk along the text.

Real lexers are the same idea with more cases: strings, comments, decimals, ===. V8, the engine in Chrome and Node.js, has a hand-tuned scanner, because every web page's JavaScript passes through it before anything else can happen.

04 · PARSE TREE

The parse tree: finding the structure

A list of tokens isn't enough. In 2 + 3 * 4, which happens first? The parser checks the tokens against the language's grammar and builds a tree that shows how they belong together. Operators that bind tighter, like *, end up lower in the tree, and the tree is worked out from the bottom up.

On the stage, the tokens rearrange into the tree for the whole program: a declaration of total holding a +, and a call to console.log holding a *. Punctuation like ; and ( ) has done its job and folds away. This tree is called an abstract syntax tree, or AST.

parser.js

Change the input to "2 * 3 + 4" and compare the trees. Each grammar rule is one function: that technique is called recursive descent.

If the tokens don't fit the grammar, there is no tree, and the program never starts. That is a syntax error. Predict what this prints before you run it:

broken.js

Nothing printed, not even the first line, because the whole file is parsed before any of it runs.

05 · BYTECODE

Bytecode: a list of tiny steps

A tree is easy to understand but slow to walk. So most language implementations compile it: they walk the tree once and write out a flat list of very simple instructions. Visit the children first, then the operator, and you get a perfect recipe for a stack machine: push the numbers, then combine them.

In the 3D, each bytecode instruction is born from the tree node it came from, in exactly that order. The lights on each brick are its opcode: in our toy machine PUSH is 1, ADD is 2, STORE is 3, and so on. To a computer, instructions are numbers too.

compiler.js

Press Step through and watch the call stack: compile calls itself for each child. That is recursion, and chapter 4 is all about it.

06 · RUNNING IT

Running it: fetch, decode, execute

Now something has to run the instructions. A virtual machine is a loop: fetch the next instruction, decode what it means, execute it, move the program counter on, repeat. On the stage the PC arrow walks down the bricks, each one fires at the chip, and the stack of values rises and falls: 2, then 2 and 3, then 5 (stored as total), then 5 and 4, then 20.

stack-machine.js

This is the entire machine. Press Step through and watch pc and stack change line by line.

A CPU does the same dance in hardware, billions of times a second, on machine code: instructions encoded as bytes it can decode directly. A virtual machine is itself a program running as machine code, so the toy stack machine above is running on a real one underneath. To watch a real fetch–decode–execute cycle tick by tick, open Inside the CPU.

07 · OUTPUT

Output: the program touches the world

Our PRINT became console.log, which sends the characters 2 and 0 to a console, which asks the operating system to draw them, which ends as pixels on your screen. Output is anything a program does to the world: text, pixels, sound, a file on disk, a packet on the network. Programmers call these side effects.

A common beginner mix-up: returning a value is not the same as printing it. A function that returns a value hands it back to the code that called it, silently. Only output reaches a person.

return-vs-print.js

The runner shows the value of the last line after →, the way a browser console does. That is a convenience for you, not output the program made.

08 · LANGUAGES

Languages: compiled, interpreted, and both

Every language makes the same journey (text, tokens, tree, instructions) but stops at different places. Here is our program in two more languages:

program.py · Python
total = 2 + 3
print(total * 4)

Python (the usual implementation, CPython) compiles your file to bytecode and then interprets that bytecode. Notice that it never adds 2 and 3 at run time: the compiler already folded them into 5, a trick called constant folding.

python -m dis program.py · abridged (CPython 3.12)
LOAD_CONST   5        # 2 + 3 was folded before it ran
STORE_NAME   total
LOAD_NAME    print
LOAD_NAME    total
LOAD_CONST   4
BINARY_OP    *
CALL         1
program.c · C
#include <stdio.h>
 
int main(void) {
    int total = 2 + 3;
    printf("%d\n", total * 4);
    return 0;
}

C is compiled ahead of time, all the way to machine code, into a file the CPU runs directly. Unoptimised, the arithmetic could look like this x86-64 code; the bytes on the right are the actual machine code. An optimising compiler would fold the whole thing to 20.

x86-64 assembly, with its machine code
mov  eax, 2         ; bytes: b8 02 00 00 00
add  eax, 3         ; bytes: 83 c0 03
imul eax, eax, 4    ; bytes: 6b c0 04

JavaScript in V8 does both. It parses and compiles to bytecode, starts interpreting at once, watches which functions run hot, and compiles those to machine code while the program is running (Sparkplug, Maglev and TurboFan are its compilers). That is called just-in-time (JIT) compilation, and it is why "compiled or interpreted?" describes an implementation, not a language.

PRACTICE

Now you do it

Type the code yourself rather than pasting it: producing an answer builds memory that reading does not. Use the hints one at a time.

1

Input, process, output

A rectangle is 4 wide and 3 high. Make area hold its area, then print Area: 12.

exercise-1.js
2

Teach the machine to subtract

Add a SUB instruction to the stack machine so this program prints 6 (that is, 10 − 4). Careful: unlike adding, the order matters.

exercise-2.js
3

Read the error, fix the bug

Run this. Read the error out loud: what kind is it, and which line? Then fix it so it prints 20. Notice that line 1 ran first: this is a run-time error, not a syntax error.

exercise-3.js

RECALL

Check yourself, from memory

Answer each one before you open it. Then come back tomorrow, and again next week, and answer them cold: retrieval spaced over days is the most reliable study method we know of. Why that works.

Name the three parts nearly every program has.

What does a lexer turn source code into?

Why is 2 + 3 * 4 equal to 14 and not 20?

In the syntax-error example, why didn't the first line print?

Bytecode or machine code: which does the CPU run directly?

Is JavaScript a compiled or an interpreted language?

GO FURTHER

Resources

  • BOOK · FREECrafting InterpretersRobert NystromBuilds a real language twice: a tree-walking interpreter, then a bytecode virtual machine. This chapter in 600 pages, and a joy to read.
  • COURSE · FREECS50x, Week 0Harvard UniversityHarvard's intro course starts exactly here: what computers do with information, and how to give them instructions.
  • DOCS · FREEJavaScript GuideMDN Web DocsThe reference most working web developers keep open. Read the first three sections next.
  • TOOL · FREEPython TutorPhilip GuoPaste a small program and step through it, watching every variable: the step mode above, for Python, Java, C and JavaScript.
  • TOOL · FREEAST ExplorerType any JavaScript and see the real parse tree a production parser builds from it.
  • TOOL · FREECompiler ExplorerMatt GodboltType C, Rust or Go and watch the machine code the compiler produces, line by line.
  • COURSE · FREENand2TetrisShimon Schocken and Noam NisanBuild a computer from logic gates up, then the assembler, the virtual machine and the compiler on top of it.

ON THIS SITE

See it running

SOURCES

Where the facts come from

  1. Computer History Museum: Ada Lovelace Her 1843 notes on the Analytical Engine, including Note G's method for Bernoulli numbers.
  2. First Draft of a Report on the EDVAC (1945) The stored-program design: instructions kept in the same memory as data.
  3. IBM: FORTRAN The first widely used high-level language and its compiler, released in 1957.
  4. Unicode 17.0 release announcement 159,801 encoded characters.
  5. MDN: Lexical grammar The token kinds JavaScript defines.
  6. V8: Blazingly fast parsing, part 1 (the scanner)
  7. V8: Firing up the Ignition interpreter A register machine with an accumulator.
  8. V8: Sparkplug, a non-optimizing JavaScript compiler
  9. V8: Maglev, V8's fastest optimizing JIT
  10. Python docs: dis, the bytecode disassembler
  11. x86 reference: ADD and IMUL, for the instruction encodings.
  12. x86 reference: IMUL
  13. Constant folding