Menu Close

What Are Programming Languages Written In?

Posted in Computer Science

When programmers first learn Python, Java, C++, JavaScript, or Rust, an interesting question eventually appears:

What Are Programming Languages Written In?

What language are programming languages themselves written in?

Is Python written in C?

Is Java written in C++?

Is C written in C?

And if a C compiler is written in C, how was the first C compiler created before a C compiler already existed?

These questions lead to one of the most fascinating ideas in computer science: bootstrapping.

They also reveal an important connection between high-level programming languages, compilers, operating systems, machine code, and the CPU.

Let’s start with an important distinction.

1. A Programming Language Is Not Exactly a Program

Strictly speaking, a programming language is not “written” in another programming language.

A language is primarily a set of rules and definitions.

For example, a programming language defines things such as:

  • syntax
  • keywords
  • data types
  • operators
  • control structures
  • functions
  • memory behavior
  • execution semantics

What programmers actually build are implementations of those languages.

An implementation may include:

  • a compiler
  • an interpreter
  • a virtual machine
  • a runtime system
  • standard libraries

So when someone says:

Python is written in C.

What they usually mean is:

CPython, the most widely used implementation of Python, is largely written in C.

This distinction becomes important because one programming language can have several different implementations.

2. Python — Mostly C

Python provides one of the best-known examples.

The standard and most widely used Python implementation is called CPython.

CPython is largely implemented in C.

When you write:

x = 10
y = 20
print(x + y)

the CPU does not directly understand those Python statements.

A simplified execution path looks like this:

Python Source Code

CPython

Python Bytecode

Python Virtual Machine

Operating System and Native Code

CPU Instructions

CPython converts Python source code into an intermediate bytecode representation.

The Python virtual machine then executes that bytecode.

Underneath this process is a large runtime system implemented primarily in C.

This is one reason C remains extremely important even when programmers work almost entirely in higher-level languages.

However, CPython is not the only Python implementation.

There have also been implementations such as PyPy, Jython, and IronPython.

Therefore, saying “Python is written in C” is useful shorthand, but technically it describes CPython rather than the Python language itself.

3. JavaScript — Often Implemented in C++

JavaScript provides another interesting example.

Modern web browsers contain sophisticated JavaScript engines.

Google Chrome and Node.js, for example, use the V8 JavaScript engine.

V8 is primarily implemented in C++.

When JavaScript executes:

let result = a + b;

the processor does not directly execute the JavaScript text.

A modern JavaScript engine performs several stages of processing.

A simplified path might look like:

JavaScript

JavaScript Engine

Interpreter and JIT Compiler

Machine Code

CPU

Modern JavaScript engines are much more sophisticated than simple line-by-line interpreters.

They can analyze frequently executed code and compile important sections into optimized machine instructions using Just-In-Time compilation, or JIT.

So underneath a high-level JavaScript program sits a large amount of lower-level systems software.

4. Java — Java Running on a Virtual Machine

Java works differently again.

A Java program is normally compiled into Java bytecode.

For example:

int c = a + b;

goes through a process roughly like this:

Java Source Code

Java Compiler

Java Bytecode

Java Virtual Machine

JIT Compilation

Machine Code

CPU

The widely used OpenJDK ecosystem contains components implemented in multiple languages.

The HotSpot Java Virtual Machine contains substantial C++ code, while other parts of the Java ecosystem are written in Java and other languages.

This illustrates another important point:

A modern language implementation does not necessarily have to be written in only one language.

Large software systems frequently combine several languages.

5. C# — C# and C++

C# provides a similar example.

Modern .NET contains multiple major components.

The Roslyn C# compiler is itself written largely in C#.

Meanwhile, lower-level parts of the .NET runtime include substantial C++ code.

A simplified C# execution path can therefore look like:

C# Source

C# Compiler

Intermediate Language

.NET Runtime

JIT Compiler

Machine Code

CPU

Again, we see layers.

The language programmers use at the top may look very different from the software operating underneath it.

6. C — The Language Underneath Many Languages

C occupies a special place in this story.

Many important language implementations and runtimes have historically been written partly or largely in C.

Examples include major implementations of:

  • Python
  • Ruby
  • PHP
  • Lua
  • Perl

Why C?

Because C provides an unusual combination of characteristics.

It is relatively portable while still giving programmers direct control over memory.

It has pointers.

It can manipulate individual bits.

It maps efficiently to machine operations.

Its runtime requirements can be relatively small.

And C compilers have existed for an enormous range of processor architectures.

This made C an excellent language for building:

  • operating systems
  • compilers
  • interpreters
  • language runtimes
  • databases
  • device drivers
  • embedded software

This is one reason C continues to appear underneath technologies that look much newer than C itself.

But this immediately creates another question.

7. What Is a C Compiler Written In?

Today, major compiler projects such as GCC and LLVM/Clang contain substantial amounts of C and C++ code.

But now we have an apparent paradox.

Suppose we write a C compiler in C.

To turn that compiler’s C source code into an executable program, we need a C compiler.

But if the compiler does not exist yet, how can we compile it?

In other words:

How can C compile C if we need C before we have C?

The answer is bootstrapping.

8. The Bootstrapping Problem

Imagine a new programming language called Language X.

Eventually, we want the Language X compiler to be written in Language X.

But initially, no Language X compiler exists.

One solution is to implement the first compiler using an already available language.

For example:

Existing Language

First Language X Compiler

Language X Becomes Usable

Once Language X becomes powerful enough, developers can write a new compiler in Language X itself.

The old compiler can then compile the new compiler.

The process becomes:

Old Compiler

compiles

New Compiler Written in Language X

Once that succeeds, Language X can effectively build its own compiler.

This process is called bootstrapping.

9. Self-Hosting Compilers

A compiler capable of compiling its own source language is commonly described as self-hosting.

Conceptually:

Language X Compiler

is written in

Language X

The compiler can eventually compile a newer version of itself.

Modern programming languages such as Go and Rust have used forms of this development process.

For example, a modern compiler may largely be written in the language it compiles.

This sounds circular:

Rust compiles Rust.

or:

Go compiles Go.

But there is no paradox once we consider the history.

An earlier compiler already exists.

That earlier compiler builds the next compiler.

Then the newer compiler can build an even newer version.

The chain extends backward until we eventually reach software written using some previously available system.

10. How Was the First C Compiler Created?

This takes us back to C.

C emerged during the early development of Unix at Bell Labs.

Before C, earlier tools and languages already existed.

Early C development grew out of earlier languages and systems programming environments, and low-level software could be implemented using assembly language.

The important idea is that the first usable C implementation did not magically appear as a fully developed modern C compiler written in modern C.

The language and compiler evolved.

A simplified historical model is:

Machine Code

Assembly Language

Early Language Tools

Early C Compiler

C Becomes More Capable

More of the Compiler Can Be Written in C

C Can Build C

This is one of the most important transitions in programming-language development.

Once a language becomes capable of implementing its own compiler, development becomes much easier.

11. Go and Rust — Modern Examples of Self-Hosting

Modern languages show that bootstrapping is not just an ancient computer-science problem.

Go provides a good example.

Early Go compiler development relied on existing implementation technology.

Later, major parts of the Go toolchain were rewritten in Go itself.

Now Go can be used to build the tools required to compile Go programs.

Rust followed its own bootstrap path.

The modern Rust compiler, rustc, is largely written in Rust.

But Rust did not begin with a complete Rust compiler magically compiling itself.

Earlier implementations and compiler versions provided the path toward today’s self-hosting compiler.

The general pattern is:

Existing Toolchain

Early Compiler

More Mature Language

Compiler Rewritten in New Language

Self-Hosting

This pattern has appeared repeatedly throughout computing history.

12. What About Assembly Language?

Eventually we reach assembly language.

Assembly is much closer to the processor.

An instruction might look conceptually like:

ADD R1, R2, R3

But even the CPU does not directly understand the text:

ADD

That text is for humans.

An assembler converts assembly instructions into machine code.

Conceptually:

Assembly Language

Assembler

Machine Code

The processor executes the resulting binary instruction encoding.

So then we can ask another question:

What was the first assembler written in?

On very early computers, programmers could enter instructions directly as machine code.

Early software tools could therefore be built from extremely low-level code.

Once a primitive assembler existed, programmers could use assembly language to create a better assembler.

Again, we see bootstrapping.

13. Eventually Everything Reaches Machine Code

No matter which programming language we start with, eventually the software must interact with the physical processor.

Consider Python:

Python

CPython

Bytecode and Runtime

Native Machine Code

CPU

Or C:

C

Compiler

Assembly or Intermediate Representation

Machine Code

CPU

Or Java:

Java

Bytecode

JVM

JIT Compiler

Machine Code

CPU

The routes are different, but the destination is the same.

The CPU ultimately executes instructions defined by its instruction set architecture.

For example, a processor might execute instructions belonging to:

  • x86-64
  • ARM
  • RISC-V

The processor does not know whether those instructions originally came from Python, Java, C, Rust, or another programming language.

At that level, they are simply machine instructions.

14. From Programming Languages to Transistors

We can continue going deeper.

Suppose we start with a Python program.

Underneath Python we find an interpreter and runtime.

Underneath those components we find native software.

Underneath native software we find machine instructions.

Underneath machine instructions we find CPU hardware.

Inside the CPU we find components such as:

  • registers
  • ALUs
  • control logic
  • caches
  • execution units

Those components are constructed from digital logic.

Digital logic is built from logic gates.

And logic gates are ultimately constructed using transistors.

So we can imagine the entire computer as a stack:

Python / Java / JavaScript / C#

Compilers, Interpreters and Virtual Machines

C / C++ / Rust and Systems Software

Machine Code

Instruction Set Architecture

CPU

Registers, ALU and Control Logic

Logic Gates

Transistors

This is one of the most important ideas in computer science.

High-level programming languages do not replace the lower layers.

They are built on top of them.

15. So What Language Are Programming Languages Written In?

There is no single answer.

Python’s most common implementation, CPython, is largely written in C.

Major JavaScript engines such as V8 are largely written in C++.

Java’s ecosystem combines Java, C++, and other technologies.

Modern C# tooling includes substantial C# code, while the .NET runtime also contains lower-level native code.

Many traditional language runtimes were built in C.

Modern languages such as Go and Rust can increasingly implement their own compilers and tooling.

And underneath all of them, eventually, are machine instructions executed by a CPU.

The deeper lesson is therefore not simply:

Python is written in C.

The more interesting lesson is:

Software is built in layers.

One programming language can be used to implement another.

A language can eventually become powerful enough to implement its own compiler.

And through bootstrapping, generations of tools can build newer generations of themselves.

Follow the chain far enough, and every high-level language eventually connects back to machine code and physical hardware.

That connection—from programming language to compiler, from compiler to machine code, and from machine code to the CPU—is one of the foundations of modern computing.

Leave a Reply

Your email address will not be published. Required fields are marked *