ContentsThe library

Turning Text Into Instructions

Before It Can Read Your Program It Has to Decide Where the Words End

Source code is one long run of characters. Something has to cut it into pieces, and the cutting rules decide what your program means before anything has parsed it.

What a token is

A source file is a run of characters. Your editor shows it with colours and

indentation, which makes it look structured, but the bytes on disk are one

sequence with no more shape than a shopping list written without spaces.

The first thing a compiler does is cut that sequence into pieces. Each piece is

a token, and a token is three things: a category, the exact characters, and the

position in the file where they started.

FIG 1One line of source, and the tokens it becomes
plaintext
source text:

    total = count * 2 + 1;

tokens produced:

    identifier   total    line 7 column 5
    operator     =        line 7 column 11
    identifier   count    line 7 column 13
    operator     *        line 7 column 19
    number       2        line 7 column 21
    operator     +        line 7 column 23
    number       1        line 7 column 25
    semicolon    ;        line 7 column 26

note: the spaces are gone, and nothing above says
which operation happens first
Eight tokens from twenty-two characters. The spaces have disappeared entirely, and nothing in the output records that multiplication binds tighter than addition. That is the next stage and this stage knows nothing about it.

Three observations about that list. The categories are few, perhaps forty in a

large language, and fixed before anybody wrote a line of your program. The

text is kept, because later stages need the actual name and the actual digits.

And the position is kept, which is what makes every downstream error message

able to point at a line.

FIG 2What the tokens in a typical source file are
Punctuation and operators together are not far off half of everything. The names you chose are under a third. This is a useful corrective to the feeling that a program is mostly words.

Taking the longest bite

Now the only rule that matters. Reading left to right, keep consuming

characters while what you have so far could still be the start of a valid

token, and cut when it could not. In other words, take the longest match.

It sounds trivial until it is applied to operators, which in most languages

come in families where the short ones are prefixes of the long ones.

FIG 3Reading the characters greater, greater, equals
characters consumedforms a valid operatorwhat the splitter choose
stop after one character110
stop after two character210
stop after three charact311
All three are valid operators in a typical language, which means the splitter has three legal answers and the rule picks one. It takes the longest, so the line is a shift and assign, not a comparison followed by something else. One character changes the operation.

Two consequences follow immediately, and both are things programmers encounter

without knowing why.

The first is that whitespace sometimes matters and sometimes does not. Between

two identifiers it is required, because without it the longest match swallows

both into one name. Between an identifier and a bracket it is optional, because

a bracket cannot extend a name. People develop an intuition for this and the

intuition is just the longest-match rule felt from the outside.

The second is the classic nested-template problem. In languages where angle

brackets close a type, two of them in a row at the end of a nested type are

consumed as a shift operator, and the program is rejected for a reason that

appears to have nothing to do with what you wrote. Several languages have

special-cased this, which is a language designer paying a permanent complexity

cost to hide one consequence of the rule.

FIG 4The splitter working through a short line
steppositioncharacter seenstate of the current piecetoken emittedwhat happened
11tbuilding an identifiernone yetA letter can start a name, so the splitter begins collecting and will keep going while letters and digits arrive.
26spacethe name endedidentifier totalA space cannot extend a name, so the longest match is over. The token is emitted and the space is consumed and discarded.
37equalsbuilding an operatornone yetAn equals sign could be the whole operator or the start of a longer one, so the splitter looks at the next character before deciding.
48spacethe operator ended at one characteroperator equalsThe next character cannot extend it, so the short form wins by default. Nothing here required backtracking: one pass, one character of lookahead.
4 steps
The whole stage is this loop, repeated until the file runs out. It never goes backwards, which is why splitting a large file costs almost nothing compared with everything that follows.

The cases that need a decision

The rule handles ordinary text. Four situations need a decision made in advance

by whoever designed the language.

Comments and whitespace are recognised like any other token and then thrown

away rather than emitted. That throwing away is why a comment can go almost

anywhere, and why the tools that need comments, such as documentation

generators and formatters, have to run their own splitter that keeps them.

Strings suspend the normal rules entirely. Inside quotes, a space is not a

separator and a semicolon is not punctuation. The splitter enters a different

mode at the opening quote and leaves it at the closing one, which is also why

an unterminated string produces such strange errors: everything after it is

being read in the wrong mode until the next quote, possibly pages later.

Numbers run into the question of where a dot belongs. In a language with both

decimals and member access, a dot after digits is ambiguous, and the answer is

a rule written down once and then half remembered by everyone who uses the

language.

Keywords are the one that surprises people. There is no keyword category in the

splitter. A keyword is an identifier that matched a fixed list after it was

collected, which is why keywords follow identifier rules exactly, and why

adding a keyword to a language breaks every existing program that used that

word as a name.

FIG 5The shape of the stage
The output is a list with positions and no structure. Keeping that in mind explains the character of errors from this stage: they are always about one piece of text at one place, never about something being in the wrong order.

What it hands to the next stage

The flatness is worth holding on to. This stage cannot tell you that a bracket

is unmatched, because it has no idea that brackets come in pairs. It cannot

tell you a name is undefined, because it has no idea what names exist. It can

only tell you that some run of characters is not a valid piece of anything, and

that is the entire category of error it produces.

That is also why its errors are the most useful ones in the compiler. They

point at exactly one position, they are never cascading, and they are almost

always a typo. Every later stage produces errors with a degree of guesswork in

them, because every later stage is trying to reconstruct an intent.

The next lesson takes this flat list and builds a tree from it, and the

interesting part is that precedence, the thing everyone memorises as a table,

turns out to be a property of how the tree is built rather than a separate rule

applied afterwards.

Recap

  • The first stage turns a run of characters into a flat list of tokens, each one a category, the exact text, and a position in the file.
  • Where a token ends is decided by taking the longest run of characters that forms a valid token, which is why adding one character to an operator can change the meaning of the line.
  • Whitespace and comments are consumed and thrown away, keywords are identifiers that matched a fixed list, and every error message you have seen points at a position this stage recorded.

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

NextFrom Pieces to a Tree →

The rest of this course

  1. 01Before It Can Read Your Program It Has to Decide Where the Words Endyou are here
  2. 02Precedence Is Not a Table You Memorise, It Is the Shape of the Rulesopening only
  3. 03The Stage That Finds Out Whether Your Names Refer to Anythingopening only
  4. 04One Neutral Form in the Middle Turns a Multiplication Into an Additionopening only
  5. 05Every Transformation Obeys One Rule, and the Rule Is Narrower Than You Thinkopening only
  6. 06The Compiler Deleted Your Safety Check Because You Had Already Broken the Ruleopening only
  7. 07Thousands of Values and Sixteen Places to Keep Themopening only
  8. 08Your Instructions Arrive in Order and Are Executed in Whatever Order Suitsopening only

Read alongside