Computing & Digital Codexery

Compiler

Software that translates high-level code into low-level machine code.

Compiler

A compiler is a piece of software that takes code written in one programming language (the source language) and converts it into another language (the target language). The term "compiler" is most often used for programs that translate a high-level programming language into a low-level one—like assembly, object code, or machine code—to produce an executable program. Compilers are essential in modern computing because they turn high-level languages into the binary instructions that digital computers can actually run.

There are several specialized types of compilers. A cross-compiler generates code for a different CPU or operating system than the one it runs on. A bootstrap compiler is a temporary tool used to compile a more permanent or better-optimized compiler for a given language. Related software includes decompilers, which translate low-level languages back into higher-level ones; source-to-source compilers (or transpilers), which translate between two high-level languages; language rewriters, which change the form of expressions without switching languages; and compiler-compilers, which produce compilers (or parts of them) in a reusable way to create many different compilers.

A compiler typically carries out several operations, known as phases: preprocessing, lexical analysis, parsing, semantic analysis (syntax-directed translation), converting the input into an intermediate representation, code optimization, and machine-specific code generation. These phases are usually implemented as modular components, which helps with efficient design and ensures that the transformation from source to target is correct. Bugs caused by faulty compiler behavior can be extremely hard to find and work around, so compiler developers put a lot of effort into making sure their compilers are correct.

**Comparison with interpreter** An interpreter serves a similar purpose to a compiler—making source code runnable—but works differently. Instead of converting code to machine code, an interpreter executes it directly. Some interpreters run source code, while others run an intermediate form like bytecode. Programs compiled to native code generally run faster than interpreted ones, while environments using bytecode as an intermediate form tend to run at an intermediate speed. Just-in-time compilation offers native execution speed but with a one-time startup cost.

Low-level languages like assembly and C are typically compiled, especially when speed matters more than cross-platform support. In these languages, there is a closer one-to-one relationship between source code and the resulting machine code, giving programmers more control over hardware. In theory, any programming language can be used with either a compiler or an interpreter, but in practice, most languages are used with just one. Still, it is possible to write a compiler for a language that is usually interpreted. For example, Common Lisp can be compiled to Java bytecode (then interpreted by the Java virtual machine), to C code (which can be compiled again to native machine code), or directly to native code.

**History** The theoretical computing concepts developed by scientists, mathematicians, and engineers during World War II laid the groundwork for modern digital computing. Primitive binary languages emerged because digital devices only understand ones and zeros, based on the circuit patterns of the underlying machine architecture. In the late 1940s, assembly languages were created to provide a more workable abstraction of computer architectures. The limited memory of early computers posed major technical challenges for the first compilers, so the compiling process had to be split into several small programs. The front-end programs produced analysis results that back-end programs used to generate target code. As computers offered more resources, compiler designs could better match the overall compiling process.

Using a high-level language is generally more productive for a programmer, so the development of such languages followed naturally from the capabilities of digital computers. High-level languages are formal languages strictly defined by their syntax and semantics, which form the language's architecture. Elements of these formal languages include: an alphabet (any finite set of symbols), a string (a finite sequence of symbols), and a language (any set of strings on an alphabet). The sentences in a language can be defined by a set of rules called a grammar. Backus–Naur form (BNF) describes the syntax of these sentences. It was developed by John Backus for the syntax of Algol 60, and its ideas come from the context-free grammar concepts of linguist Noam Chomsky. BNF and its extensions have become standard tools for describing programming notation syntax, and in many cases, parts of compilers are automatically generated from a BNF description.

Between 1942 and 1945, Konrad Zuse designed the first algorithmic programming language for computers, called Plankalkül ("Plan Calculus"). Zuse also envisioned a "Planfertigungsgerät" (plan assembly device) to automatically translate a mathematical program formulation into machine-readable punched film. Although no implementation occurred until the 1970s, it introduced concepts later seen in APL, designed by Ken Iverson in the late 1950s. APL is a language for mathematical computations. Between 1949 and 1951, Heinz Rutishauser proposed Superplan, a high-level language and automatic translator. His ideas were later refined by Friedrich L. Bauer and Klaus Samelson. High-level language design during the early years of digital compilers continued to evolve from these foundations.

field
Computing
known_for
Translating high-level programming languages into low-level machine code
related_software
Decompilers, source-to-source compilers, language rewriters, compiler-compilers

Lore & Background

Theoretical computing concepts developed during World War II formed the basis of digital modern computing. Primitive binary languages evolved because digital devices only understand ones and zeros. In the late 1940s, assembly languages were created to offer a more workable abstraction. Limited memory capacity of early computers led to substantial technical challenges when the first compilers were designed, so the compiling process needed to be divided into several small programs. The front end programs produce the analysis products used by the back end programs to generate target code. Between 1942 and 1945, Konrad Zuse designed the first algorithmic programming language for computers named Plankalkül. He also envisioned a Planfertigungsgerät to automatically translate the mathematical formulation of a program into machine-readable punched film stock. Between 1949 and 1951, Heinz Rutishauser proposed Superplan, a high-level language and automatic translator. High-level language design during the formative years of digital computing provided useful programming tools for a variety of applications, including FORTRAN for engineering and science, COBOL for business, and LISP for symbolic computation. Some early milestones include May 1952, when Grace Hopper's team at Remington Rand wrote the compiler for the A-0 programming language and coined the term compiler, though the A-0 compiler functioned more as a loader or linker. In 1952, before September, an Autocode compiler developed by Alick Glennie for the Manchester Mark I computer is considered by some to be the first compiled programming language.

Reader's Guide

Compilers are essential in computing because they allow programmers to write code in high-level languages that are easier for humans to understand and maintain, while still producing efficient low-level code that computers can execute. A compiler performs operations such as preprocessing, lexical analysis, parsing, semantic analysis, conversion to an intermediate representation, code optimization, and machine-specific code generation. These phases are implemented as modular components, promoting efficient design and correctness. Compiler implementers invest significant effort to ensure compiler correctness, as program faults caused by incorrect compiler behavior can be very difficult to track down and work around. Compared to interpreters, which execute code without converting it to machine code, a program compiled to native code tends to run faster. Just-in-time compilation allows for native execution speed with a one-time startup processing time cost. Low-level programming languages like assembly and C are typically compiled, especially when speed is a significant concern. In theory, any programming language can be used via either a compiler or an interpreter, but in practice languages tend to be used with only one or the other. Compiler technology evolved from the need for a strictly defined transformation of the high-level source program into a low-level target program, with the compiler viewed as a front end for analysis and a back end for synthesis, and optimization between them producing more efficient target code.

Did You Know?

The Core Function and Varieties

A compiler is software that takes code written in one programming language and transforms it into another, typically moving from a high-level language down to something a machine can execute—assembly, object code, or raw machine code. Beyond this basic translation role, the compiler family branches into several specialized forms. A cross-compiler targets a different CPU or operating system than the one it runs on, enabling developers to build software for platforms they do not directly inhabit. A bootstrap compiler serves as a temporary bridge, used to compile a more refined and optimized version of itself for a given language. The broader ecosystem also includes decompilers that reverse the flow from low-level to high-level code, transpilers that convert between high-level languages, language rewriters that reshape expressions within the same language, and compiler-compilers that generate compilers or their components in a generic, reusable fashion. This diversity reflects the many practical needs programmers face when bridging the gap between human-readable logic and hardware-level instructions.

The Phases of Compilation

A typical compiler moves source code through a sequence of well-defined stages, often called phases. These include preprocessing, lexical analysis, parsing, semantic analysis (sometimes described as syntax-directed translation), conversion into an intermediate representation, code optimization, and finally machine-specific code generation. Rather than treating this pipeline as a monolithic block, compiler designers generally implement each phase as a separate modular component. This architectural choice promotes both efficient design and correctness, since each transformation from source input to target output can be verified and maintained independently. The stakes of getting these phases right are high: a single fault introduced by incorrect compiler behavior can produce program errors that are extremely difficult to trace and work around. For this reason, compiler implementers invest substantial effort into ensuring their tools behave correctly. The modular, phased approach is what allows the enormous complexity of translating human-written logic into precise machine instructions to be managed in a structured, debuggable way.

Compiler Versus Interpreter

Both compilers and interpreters make source code runnable, but they achieve this through fundamentally different mechanisms. An interpreter executes code directly without first converting it into machine code; some interpreters run the source as-is, while others work with an intermediate form such as bytecode. A program compiled to native code generally runs faster than its interpreted counterpart, whereas bytecode-intermediate environments tend to land in the middle on the speed spectrum. Just-in-time compilation offers a hybrid: native execution speed after a one-time startup processing cost. Low-level languages like assembly and C are typically compiled rather than interpreted, especially when speed is critical, because the close one-to-one correspondence between source and machine code gives programmers fine-grained control over hardware. In theory any language could be handled by either approach, but in practice a language tends to be paired with one or the other. Common Lisp illustrates the flexibility: it can be compiled to Java bytecode, to C code for a second compilation pass, or directly to native machine code.

Origins and Early Milestones

The theoretical groundwork for modern computing was laid by scientists, mathematicians, and engineers during World War II, when primitive binary languages emerged because digital hardware only understood ones and zeros. By the late 1940s, assembly languages offered a more practical abstraction over raw circuit patterns. Early compilers faced severe memory constraints, forcing the compilation process to be split into small front-end and back-end programs. Konrad Zuse designed Plankalkül between 1942 and 1945, envisioning a device to automatically translate mathematical program formulations into machine-readable punched film, though no implementation appeared until the 1970s. Heinz Rutishauser proposed Superplan between 1949 and 1951, later refined by Bauer and Samelson. FORTRAN became one of the first implemented high-level languages and first optimizing compiler, targeting engineering and science. COBOL evolved from A-0 and FLOW-MATIC for business use, while LISP addressed symbolic computation. John Backus's BNF, drawing on Noam Chomsky's context-free grammar ideas, became a standard tool for describing programming syntax and even for auto-generating parts of compilers.

Frequently Asked Questions

What is a Compiler in computing?

A compiler is a piece of software that takes source code written in a high-level programming language and converts it into a lower-level form—such as assembly, object code, or machine code—that a processor can directly execute. It serves as the essential bridge between human-readable code and the binary instructions a digital machine actually runs.

What is a Compiler's primary role in the development pipeline?

Its core job is to translate high-level language constructs into low-level, machine-specific instructions so the resulting program becomes an executable artifact. Without this translation step, the readable syntax of languages like C or Rust would be meaningless to the CPU.

Why is the Compiler considered fundamental to modern computing?

It is the mechanism that makes high-level programming languages practical, letting developers write portable, readable code while the compiler handles the gritty details of producing instructions for a specific hardware target. Every time you run a program written in anything other than raw binary, a compiler (or a closely related tool) performed that critical conversion behind the scenes.

What software is closely related to a Compiler?

Decompilers attempt the reverse direction by reconstructing higher-level code from machine code, while source-to-source compilers and language rewriters transform code from one language or dialect into another. Compiler-compilers, sometimes called compiler generators, are meta-tools that help build entirely new compilers from grammar specifications.

How does a Compiler differ from a decompiler?

A compiler moves in one direction—taking high-level source and producing low-level, executable output—whereas a decompiler works backward, trying to recover something resembling readable source from compiled binary. The forward path is deterministic and well-defined; the reverse path is inherently lossy and approximate.

More in Computing & Digital 1-24

Spotted an error? Know more?

This is a living reference — every entry is fact-audited, and reader corrections feed straight into our audit queue. Suggest an edit · See this site's audit record

Comments

Loading…
Open in the interactive codex →