Compilers and Interpreters: How Code Runs
The same marks, different ways to run the code
A teacher has written instructions that add maths and science marks. Saving the file does not automatically display a total. The computer needs software that can read and carry out the instructions in that language.
A compiler translates code into another representation. An interpreter carries out code or an intermediate representation while the program runs. A language implementation can combine these approaches, so the distinction describes tools and stages rather than two permanent categories of language.
Compare the jobs
Explain the difference between translating a program and carrying it out.
Follow real workflows
Identify what happens when a student marks program is run with Python or built with a C compiler.
Diagnose a mistake
Distinguish a source-code error from an incorrect result, and explain why successful compilation is not enough.
Two jobs that can work together
Source program
The teacher writes an addition rule in a particular language.
maths + scienceTranslation or preparation
A language tool reads the source and may create another representation.
Source → executable or intermediate codeExecution
A processor or language engine carries out the prepared instructions.
Output: 75
Translation is different from execution
First revisit the journey from source code to the processor if the terms “source” or “machine instruction” are new. The student marks program gives us a task whose intended result is easy to check.
Translating means expressing instructions in a representation another tool can use. Executing means carrying out those instructions with actual values. A translated program may exist on disk before it has added a single mark.
An interpreter is software that performs the operations described by code. It can work with source code or prepared instructions. It is misleading to define all interpreters as tools that read one physical source line at a time: preparation, whole-file checks and intermediate code can be involved.
Start with the actual student marks program
This is JavaScript, using the same school task as the first lesson. const stores a named value, + adds the marks, and console.log displays the total. The expected result is 75, whether the language engine uses interpretation, compilation or a combination.
This example shows the program’s behavior. The next sections compare real tools that carry out equivalent instructions in other languages; they do not pretend that converting a text value to a number is compilation.
JavaScript · local teaching example
const maths = 40;
const science = 35;
const total = maths + science;
console.log(total);Expected output
75A real compiled workflow: build a C program
Here is the same addition in C. Save this complete source as student_total.c. The int declarations store whole-number marks. main is where this program begins. printf displays the total, and %d requests a whole-number representation. The included standard header declares the output function. In the output string, \n starts a new line; return 0 reports successful completion to the surrounding system.
#include <stdio.h>
int main(void) {
int maths = 40;
int science = 35;
int total = maths + science;
printf("%d\n", total);
return 0;
}In a Unix-like terminal with a C compiler available as cc, these are the shell commands to build and run it:
cc student_total.c -o student_total
./student_totalExpected output from running the executable:
75The first command compiles and links the program, producing the executable named student_total. Linking connects the program with required library facilities. The second command runs the executable. Successful building normally does not print the student’s total; execution does.
This is an ahead-of-time workflow: build before running. The executable targets a particular platform; copying it to an arbitrary different operating system or processor is not a guarantee it will run. Commands and executable names vary by environment. Here -o names the output file. For one widely used implementation, see Clang’s compilation and linking stages.
A real interpreted workflow: run the Python source
This version uses Python. Save it as student_total.py. Assignment stores the marks, + adds them, and print displays the result. Python does not use JavaScript’s const keyword.
maths = 40
science = 35
total = maths + science
print(total)With Python 3 available as python3, run this shell command:
python3 student_total.pyExpected output:
75You give the source file to the Python implementation instead of separately building a native executable. CPython, the commonly used implementation, first compiles source into bytecode—instructions for its own execution engine—and then carries out that bytecode.
The implementation still runs on real hardware. Bytecode instructions and CPU machine instructions are different instruction sets. See Python’s definition of bytecode for this distinction. The commands here are examples to run on your own configured computer; the lesson is not an installed C or Python environment.
Why Python can be both compiled and interpreted
The Python program is reproduced here because we are now looking at its preparation stage. It still adds the marks and prints 75 when executed.
maths = 40
science = 35
total = maths + science
print(total)With the file saved, the shell command below asks Python to compile it without running its top-level instructions:
python3 -m py_compile student_total.pyExpected terminal output on successful compilation: none. A bytecode cache file is created under __pycache__. The absence of 75 matters: preparing instructions is different from executing print(total).
That .pyc file is not a standalone native executable. Python bytecode is tied to its implementation and version. Cached bytecode can avoid some preparation when loading a module; it does not make the addition rule intrinsically faster. Python also treats a script run directly from the command line differently from an imported module. See Python’s explanation of compiled files and caching.
Follow CPython’s two stages
Python source
Readable instructions describe the marks calculation.
total = maths + scienceBytecode
Compilation prepares instructions for the Python engine.
Not a native executableExecution engine
CPython carries out those instructions and calls print.
75
Compilation can also happen while a program runs
JavaScript · the task stays the same across engine strategies
const maths = 40;
const science = 35;
const total = maths + science;
console.log(total);Expected output
75Just-in-time compilation, usually shortened to JIT, translates code during execution rather than only in a separate build step. A language engine can start with one execution strategy and compile selected code as it runs. Different engines make different choices.
JavaScript engines commonly use a combination of techniques. That is why calling JavaScript “only interpreted” does not describe every engine. See the documentation on just-in-time compilation.
For our small marks calculation, the important result is still 75. We have not measured its speed, and this diagram does not expose a particular engine’s internal instructions. Choose a tool for the whole project—its platform, libraries and development workflow—rather than assuming one execution label guarantees better performance.
Decide what happens before the total appears
A teacher saves the Python source shown here, then runs only python3 -m py_compile student_total.py. She sees no total and thinks addition has failed. What should you explain? What command would actually run the calculation?
maths = 40
science = 35
total = maths + science
print(total)Worked answer
The command prepared bytecode; it did not execute the addition and print instruction. No output is expected on successful compilation. Run python3 student_total.py to execute the source and display 75.
Now change science to 20 and run the source again. The total becomes 60 because the input changed. No change to the language’s execution model is needed. If using the C workflow, rebuild its executable after changing its source; an existing executable does not automatically absorb source edits.
Successful compilation does not prove the answer is right
Consider this complete JavaScript variation. It is valid code, but the teacher asked for the combined marks.
const maths = 40;
const science = 35;
const total = maths - science;
console.log(total);Expected output: 5. Translation or execution can succeed while the rule is wrong. This is a logic error: the chosen instructions do not solve the intended task.
A missing closing bracket can instead be a syntax error, meaning the source breaks the language’s grammar. Some checks happen before execution; other failures appear only on particular paths while running. Do not assume every interpreter will first print every earlier line, or that every compiler can find every possible error.
Use tests and code reviews to compare actual results with requirements, and learn more in the lesson on syntax, runtime and logic errors.
Check: compile, run or both?
Check: changing the compiled school program
Keep the distinction between preparing and running
A compiler translates code; an interpreter carries out code or prepared instructions. An implementation can use both. Ahead-of-time compilation happens before execution, while JIT compilation happens during it.
The C workflow builds an executable and then runs it. The CPython workflow prepares bytecode and carries it out. Compiling the Python file by itself prints no marks total. These are specific examples, not universal rules about every language implementation.
Try explaining why a successful build can still produce 5 when the expected total is 75. Next, compare ways to organize the same student marks program. If you want hands-on language practice, use the Python course.
Reaching the recap and completing the quizzes are separate milestones.