Computer ScienceCore22 min read

Translators and Compilation

Turning source you can read into instructions a processor can run

This topic appears in:

01

Three kinds of translator

A processor executes only machine code. Everything written in any other language has to be translated, and there are three tools for the job, differing in how much they translate at once.

An assembler handles assembly language, substituting one machine instruction per mnemonic. A compiler translates an entire high-level program in advance, producing an executable that runs without the compiler present. An interpreter translates and executes one statement at a time, every time the program runs.

CompilerInterpreter
When it translatesall at once, before runningline by line, while running
Produces a file?yes, an executableno
Speed when runningfast — already translatedslower — translating as it goes
Error reportinga list after the whole programstops at the first error found
Needed to run later?noyes, every time
Source code visible?no, only the executable is shippedyes, the source must be present
Suitsfinished software for distributiondevelopment, testing and scripting

Why developers often use both

An interpreter reports the first error immediately and lets a change be tested without waiting for a full rebuild, which suits writing and debugging. A compiler produces something fast that can be distributed without the source. So a program is commonly developed under an interpreter and compiled for release — the two are not competing choices so much as tools for different stages.

02

The stages of compilation

Compilation is not one action but a sequence, and each stage catches a different kind of error. Knowing which stage rejects which mistake is a standard exam question.

Lexical analysis breaks the source into tokens, strips comments and whitespace, and builds the symbol table. Syntax analysis checks those tokens against the grammar of the language — this is where a missing bracket or semicolon is caught. Semantic analysis checks meaning: using an undeclared variable, or assigning a string to an integer. Code generation produces the machine code, and optimisation improves it.

source code↓ lexical analysis→ tokens, symbol table↓ syntax analysis→ parse tree; grammar errors caught↓ semantic analysis→ type and declaration errors caught↓ code generation→ machine code produced↓ optimisation→ faster or smaller codeexecutableeach stage catches a different class of error
lexical
source into tokenscomments and spaces removed here
syntax
grammar checkingmissing brackets and semicolons
semantic
meaning checkingundeclared variables, type mismatches
optimisation
improving the outputremoving redundant code, reusing registers

Syntax and semantic errors are different things

if x > 5 { with a missing closing brace is a syntax error — the grammar is violated. total = "hello" + 3 may be perfectly well-formed grammatically but meaningless, which is a semantic error. Questions regularly give an example and ask which stage would catch it, so the distinction is worth being precise about.

03

Bytecode and the middle way

Some languages compile to an intermediate form rather than to machine code for a particular processor. Java produces bytecode, which is then executed by a virtual machine on whatever hardware is present.

This gives portability: one compiled file runs anywhere a suitable virtual machine exists, so the program does not have to be recompiled for every processor. The cost is a little speed, since the bytecode still has to be interpreted or just-in-time compiled as it runs.

Which translator, and why

  1. Assembler — assembly language only, one instruction per mnemonic.
  2. Compiler — whole program in advance; fast to run, source stays private.
  3. Interpreter — statement by statement; slower, but immediate feedback while developing.
  4. Bytecode plus a virtual machine — portable across processors, slightly slower.
  5. A compiler reports all errors together; an interpreter stops at the first one.
  6. A compiled program does not need the translator present; an interpreted one always does.
04

Errors, and which tool finds them when

Errors are classified by when they are discovered, and the classification matters because it determines which tool can help.

A syntax error breaks the rules of the language and is caught by the translator before the program runs at all. A run-time error is grammatically valid but fails during execution — dividing by zero, opening a file that is not there. A logic error is worst of all: the program runs, produces an answer, and the answer is wrong. No translator can detect it, because nothing is technically incorrect.

This is why testing exists as a separate discipline from compiling. A clean compile proves only that the program is well-formed, not that it does what was intended.

Error typeWhen foundFound byExample
Syntaxbefore runningthe translatormissing bracket
Run-timeduring executionthe program crashingdivision by zero
Logicpossibly nevertesting, by a humanusing + instead of −

A logic error cannot be found by a translator, so it has to be tracked down the way any fault is: by narrowing the possibilities systematically rather than by rereading the code hoping to spot it.

A program that compiles is not a program that works

Compiling successfully proves only that every statement is grammatically valid and type-consistent. A program that calculates an average by dividing by the wrong count compiles perfectly and is completely wrong. That gap between "accepted by the translator" and "correct" is exactly what the testing stage of the development life cycle exists to close.

05

What the symbol table is for

Lexical analysis produces more than a stream of tokens — it also builds the symbol table, a record of every identifier the program uses. For each name it stores the type, the scope in which it is valid, and eventually the memory address or offset assigned to it.

Every later stage depends on it. Semantic analysis consults it to check that a variable was declared before use and that the types in an assignment are compatible. Code generation consults it to find where each variable actually lives in memory, so it can emit the right address.

This is why a single undeclared variable can produce a cascade of errors: the name is absent from the symbol table, so every subsequent use of it fails the same check.

  • Identifier — the name as written in the source.
  • Type — integer, real, string, or a user-defined type.
  • Scope — where in the program the name is valid.
  • Address or offset — filled in during code generation.

Practice questions

5 questions · 15 marks · full working on every one

Try each one on paper first, then open the working. The marks are shown where they are actually awarded, because that is where they are actually lost.

Short questions

4 · 9 marks

Two marks each, in the style of the short-question section of the paper. Answer in two or three lines.

SQ1[2 marks]
State two differences between a compiler and an interpreter.
Model answer

A compiler translates the whole program before it runs and produces an executable file, whereas an interpreter translates and executes one statement at a time and produces no file. A compiler also reports all errors together at the end, while an interpreter halts at the first error it meets.

Examiner tip. One mark per genuine difference. "A compiler is faster" alone is too vague — say faster to run, because translation has already happened.

SQ2[3 marks]
Describe what happens during lexical analysis and syntax analysis.
Model answer

Lexical analysis breaks the source into tokens, removes comments and whitespace, and builds the symbol table. Syntax analysis checks that the sequence of tokens obeys the grammar of the language, building a parse tree and reporting errors such as a missing bracket.

Examiner tip. Three marks: tokens, removal of comments/whitespace, and grammar checking.

SQ3[2 marks]
A program contains the statement count = "seven" + 1, where count is declared as an integer. State which stage of compilation reports this and why.
Model answer

Semantic analysis. The statement is grammatically well-formed, so it passes syntax analysis, but it is meaningless because a string cannot be added to an integer and assigned to an integer variable — a type mismatch.

Examiner tip. The mark hinges on it being grammatically valid but meaningless, which is exactly the syntax-versus-semantics distinction.

SQ4[2 marks]
Explain why an assembler is simpler than a compiler.
Model answer

In assembly language each mnemonic corresponds to exactly one machine instruction, so translation is largely a matter of substituting opcodes and resolving addresses. A compiler must translate high-level statements that may each become many machine instructions, and must also perform syntax, semantic and optimisation work.

Examiner tip. The one-to-one versus one-to-many mapping is the key comparison.

Exam questions

1 · 6 marks

Multi-part questions with a full mark scheme.

Q1[6 marks]
(a) Explain why a developer might use an interpreter while writing a program but a compiler to release it.
(b) State what optimisation does and give one example.
(c) Explain what bytecode is and the advantage it gives.
Mark scheme
  1. (a) An interpreter reports the first error immediately and lets a change be tested without a full rebuild, which suits development.The fast feedback loop is the point.[1]
  2. A compiled executable runs faster and can be distributed without the source code, which suits release.Speed and source privacy are both valid.[1]
  3. (b) Optimisation improves the generated machine code so it runs faster or takes less space.Either faster or smaller is acceptable.[1]
  4. For example, removing code that can never execute, or keeping a frequently used value in a register instead of memory.A concrete example is required for the mark.[1]
  5. (c) Bytecode is an intermediate form produced by the compiler, executed by a virtual machine rather than directly by the processor.The intermediate nature must be stated.[1]
  6. It is portable: the same compiled file runs on any platform with a suitable virtual machine, without recompiling.Portability is the advantage being tested.[1]

(a) fast feedback vs speed and privacy; (b) faster or smaller code; (c) portable intermediate form