Compiler vs. Interpreter in Programming

Compilers and interpreters are used to convert a high-level language into machine code. However, there are differences between how and when they work.

Written by Rakia Ben Sassi
software developer coding
Image: Shutterstock / Built In
Brand Studio Logo
UPDATED BY
Brennan Whitfield | Sep 10, 2024

As developers, we all get used to writing code in a high-level language that humans can understand. However, computers can only understand a program written in a binary system known as machine code.

To speak to a computer in its non-human language, we came up with two solutions: interpreters and compilers. Ironically, most of us know very little about them, although they’re a part of our daily coding life.

Compilers vs. Interpreters

  • Compiler: A compiler translates code from a high-level programming language into machine code before the program runs.
  • Interpreter: An interpreter translates code written in a high-level programming language into machine code line-by-line as the code runs. 

In this post, I’ll dive into the journey of translating a high-level language into a machine code ready for execution. I’ll focus on the inner workings of the two key players in this game, the compiler and the interpreter, and break down the related concepts.

 

Why Compilers and Interpreters Are Important

Both compilers and interpreters are computer programs that convert a code written in a high-level language into a lower-level or machine code understood by computers. However, there are differences in how they work and when to use them.

Even if you’re not going to implement the next compiler or interpreter, these insights should help to improve your knowledge of the tools you use as a developer every day.

 

A tutorial on the differences between compilers and interpreters. | Video: ISO Training Institute

Compilers vs. Interpreters: How Do They Work?

Compilers and interpreters have long been used as computer programs to transform code, but they work in different ways.

How Compilers Work

A compiler translates code written in a high-level programming language into a lower-level language like assembly language, object code and machine code (binary 1 and 0 bits). It converts the code ahead of time before the program runs.

How Interpreters Work

An interpreter translates the code line-by-line when the program is running. You’ve likely used interpreters unknowingly at some point in your work career.

A high-level programming language is usually referred to as “compiled language” or “interpreted language.” However, in practice, they can have both compiled and interpreted implementations. C, for example, is called a compiled language, despite the existence of C interpreters. The first JavaScript engines were simple interpreters, but all modern engines use just-in-time (JIT) compilation for performance reasons.

More on Software DevelopmentBuild a Discord Bot With Python

 

Compilers vs. Interpreters: Advantages and Disadvantages

Both compilers and interpreters have pros and cons:

  • A compiler takes in the entire program and requires a lot of time to analyze the source code. The interpreter takes a single line of code and very little time to analyze it.
  • Compiled code runs faster, while interpreted code runs slower.
  • A compiler displays all errors after compilation. If your code has mistakes, it will not compile. But the interpreter displays errors of each line one by one.
  • Interpretation does not replace compilation completely.
  • Compilers can contain interpreters for optimization reasons like faster performance and smaller memory footprint.

 

4 Types of Interpreters to Know

1. Bytecode Interpreter

The trend toward bytecode interpretation and just-in-time compilation blurs the distinction between compilers and interpreters. Bytecode interpreters can process up to 256 instructions, with each instruction starting with a byte. 

2. Threaded Code Interpreter

Unlike bytecode interpreters, threaded code interpreters use pointers instead of bytes. Each instruction is a word pointing to a function or an instruction sequence, possibly followed by a parameter. The number of different instructions is limited by the available memory and address space.

Forth code, which is used in Open Firmware systems, is a classical example of threaded code. The source code is compiled into a bytecode known as “F code,” which a virtual machine then interprets.

3. Abstract Syntax Tree Interpreter

If you’re a TypeScript developer with some familiarity of the TypeScript architecture, you may have heard about the abstract syntax tree (AST).

AST is an approach to transform the source code into an optimized abstract syntax tree, then execute the program following this tree structure, or use it to generate native code just-in-time.

AST keeps the global program structure and relations between statements. This allows the system to perform better analysis during runtime and makes AST a better intermediate format for just-in-time compilers than bytecode representation.

However, for interpreters, AST causes more overhead. Interpreters walking the abstract syntax tree are slower than those generating bytecode.

4. Just-in-Time Compilation

Just-in-time compilation (JIT) is a technique in which the intermediate representation is compiled to native machine code at runtime.

More on Software DevelopmentWhat Is the Java Runtime Environment?

 

10 Types of Compilers to Know

1. Cross-Compiler

A cross-compiler is one that runs on a computer whose CPU or operating system differs from the one on which the code it produces will run.

2. Native Compiler

A native compiler produces an output that would run on the same type of computer and operating system as the compiler itself.

3. Bootstrap Compiler

Bootstrap compiler is a compiler written in the language that it intends to compile.

4. Decompiler

A decompiler translates code from a low-level language to a higher level one.

5. Source-to-Source Compiler (Transpiler)

A source-to-source compiler is a program that translates between high-level languages. This type of compiler is also known as a transcompiler or transpiler.

Some examples of a transpiler include:

  • Emscripten: This transpiles C/C++ to JavaScript.
  • Babel: This transpiles JavaScript code from ES6+ to ES5.
  • Cfront: The original compiler for C++ (from around 1983). It used C as its target language and created C code with no indent style and no pretty C intermediate code, since the generated code was not usually intended to be readable by humans.

6. A Language Rewriter

This is usually a program translating form of expressions without a change of language.

7. Bytecode Compiler

A compiler that translates a high-level language into an intermediate simple language that a bytecode interpreter or virtual machine can interpret. Examples include: Bytecode compilers for Java and Python.

8. Just-in-Time Compiler (JIT Compiler)

A JIT compiler defers compilation until runtime. It generally runs inside an interpreter.

Examples of a JIT compiler include:

  • The earliest published JIT compiler is attributed to LISP in 1960.
  • The latter technique appeared in languages such as Smalltalk in the 1980s.
  • Since then, JIT compilation has gained mainstream attention amongst modern languages like Java, .NET Framework, Python and most modern JavaScript implementations.

In Java, source files are first compiled and converted into .class files that contain Java bytecode, a highly optimized set of instructions, then a bytecode interpreter executes the bytecode, and later the JIT compiler translates the bytecode to machine code.

Java bytecode can either be interpreted at runtime by a virtual machine, or compiled at load time or runtime into native code. Modern JVM implementations use the compilation approach, so after the initial startup time the performance is equivalent to native code.

9. AOT Compilation

Ahead-of-time (AOT) compilation is the approach of compiling a higher-level programming language, or an intermediate representation such as Java bytecode, before the runtime.

An example of this is the Angular framework. This uses an ahead-of-time (AOT) compiler to transform HTML and TypeScript code into JavaScript code during the build time to provide a faster rendering later on the browser when the code is running.

10. Assembler

An assembler translates human-readable assembly language into machine code. This compilation process is called assembly. The inverse program that converts machine code to assembly language is called a disassembler.

An assembly language (ASM) is a low-level programming language in which there is a dependence on the machine code instructions. That’s why every assembly language is designed for exactly one specific computer architecture.

Frequently Asked Questions

Both compilers and interpreters are programs used to translate source code from a high-level programming language into a low-level programming language (like machine code). The difference is compilers translate this code before executing it, and interpreters translate this code line-by-line as it's being executed.

C, C++, C#, Go, Rust and Swift are a few examples of compiled programming languages. Some languages, like Java and Python, are both compiled and interpreted.

Python can be considered both a compiled and interpreted programming language, since it is first compiled then interpreted line-by-line as it is run.

An interpreter can be preferred over a compiler for a few reasons:

  • Interpreters can analyze code and display any code errors one line at a time.
  • Interpreted programming language source code can be flexible and executable on multiple platforms without incompatibility problems.
  • Interpreters directly execute source code without additional steps required.

GCC (GNU Compiler Collection), Clang, Microsoft Visual C++ and javac are some examples of compilers.

Explore Job Matches.