A compiler is software that translates or converts a program written in a high-level language (Source Language) into a low-level language (Machine Language or Assembly Language). Compiler design is the process of developing a compiler.

These are some operations that are done by the compiler.
- Breaks source programs into smaller parts.
- Enables the creation of symbol tables and intermediate representations.
- Helps in Error Detection.
- Saves all codes and variables.
- Analyses the full program and translates it.
- Convert source code to machine code.
Computers understand only machine language, which is difficult for humans to write and understand. Therefore, we write programs in high-level languages.
To convert these programs into machine language, different language processing tools are used:

The preprocessor processes the source code before compilation begins.
It performs the following tasks:
- File inclusion (#include)
- Macro expansion (#define)
- Removal of comments
- Conditional compilation
#include
#define
Example: If the program contains include <stdio.h>, the preprocessor replaces this directive with the actual contents of the stdio.h file in the output.
#include <stdio.h>
stdio.h
Assembly language is neither pure binary nor a high-level language. It is a low-level language that uses symbolic instructions to represent machine code.
The assembler converts assembly language into machine code.
- It is platform-dependent (different systems require different assemblers).
- The output produced is called an object file.
A compiler is a system program that translates an entire high-level language program into machine code.
It performs:
- Syntax checking
- Semantic analysis
- Error detection
- Code optimization
- Generation of assembly or machine code
A compiler processes the entire program at once and then generates the output file. Compilation may take more time and memory, but the resulting program runs faster.
The linker combines multiple object files into a single executable file.
It:
- Merges object codes produced by the compiler and assembler
- Resolves external references (functions and variables)
- Links standard library functions
- Produces an executable file
The loader loads the executable file into main memory for execution.
Since the code generated by the compiler and linker is usually relocatable (its starting address is not fixed), the loader:
- Assigns actual memory addresses
- Performs relocation
- Prepares the program for execution
- Starts the program
An interpreter also converts high-level language into machine language, but it works differently from a compiler.
- A compiler translates the whole program at once.
- An interpreter translates and executes the program line by line.
Because translation happens during execution, interpreted programs are generally slower than compiled programs.
Compilers are classified into different types based on how they translate source code and when the translation process takes place.
A self compiler or a resident compiler is a compiler that runs on a machine and generates machine code for the same machine on which it is running.
A cross compiler runs on one machine but generates machine code for another machine (different platform). It is used when developing software for embedded systems or different architectures.
A source-to-source compiler translates source code written in one high-level language into another high-level language. Example: TypeScript → JavaScript.
A single-pass compiler completes the compilation process in one pass of the source code. It is faster but provides limited optimization.
A two-pass compiler processes the source code twice:
- First pass: Analysis (front-end)
- Second pass: Code generation (back-end)
It produces more accurate results than a single-pass compiler.
A multi-pass compiler processes the source code multiple times and generates several intermediate representations. It provides better error checking and code optimization.
A JIT compiler converts code into machine language during program execution (runtime). It combines the advantages of interpretation (dynamic execution) and compilation (better performance). Example: Java Virtual Machine (JVM).
An AOT compiler converts the entire source code into machine code before execution. It improves startup time and runtime performance.
An incremental compiler compiles only the modified parts of the program instead of recompiling the entire code. It saves time and improves development efficiency.